diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 00000000..e98a4d4c
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,45 @@
+name: Deploy Web (Cloudflare)
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+concurrency:
+ group: deploy-web-production
+ cancel-in-progress: false
+
+permissions:
+ contents: read
+
+jobs:
+ deploy-production:
+ name: Deploy production
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build OpenNext (Cloudflare)
+ working-directory: web
+ env:
+ NEXT_PUBLIC_POSTHOG_HOST: ${{ vars.NEXT_PUBLIC_POSTHOG_HOST || 'https://i.agentrelay.com' }}
+ NEXT_PUBLIC_POSTHOG_KEY: ${{ vars.NEXT_PUBLIC_POSTHOG_KEY }}
+ run: npm run cf:build
+
+ - name: Deploy to Cloudflare
+ working-directory: web
+ env:
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
+ run: npx wrangler deploy
diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml
new file mode 100644
index 00000000..8c90a888
--- /dev/null
+++ b/.github/workflows/preview.yml
@@ -0,0 +1,96 @@
+name: Preview Web (Cloudflare)
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened]
+
+permissions:
+ contents: read
+ issues: write
+ pull-requests: write
+
+concurrency:
+ group: preview-web-pr-${{ github.event.pull_request.number }}
+ cancel-in-progress: true
+
+jobs:
+ preview:
+ name: Deploy Preview
+ # Same-repo PRs only (forks can't read the Cloudflare secret).
+ if: github.event.pull_request.head.repo.full_name == github.repository
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build OpenNext (Cloudflare)
+ working-directory: web
+ env:
+ NEXT_PUBLIC_POSTHOG_HOST: ${{ vars.NEXT_PUBLIC_POSTHOG_HOST || 'https://i.agentrelay.com' }}
+ NEXT_PUBLIC_POSTHOG_KEY: ${{ vars.NEXT_PUBLIC_POSTHOG_KEY }}
+ run: npm run cf:build
+
+ - name: Upload preview version
+ id: preview
+ working-directory: web
+ env:
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
+ run: |
+ set -o pipefail
+
+ upload_log="$(mktemp)"
+ # Upload a new version WITHOUT promoting it to production. Cloudflare
+ # retains/expires versions automatically, so there is no stale-stage
+ # cleanup to run. A per-version preview URL is minted only when the
+ # account has a workers.dev subdomain enabled; if it isn't, the upload
+ # still succeeds and we just skip the URL (don't fail the check).
+ npx wrangler versions upload | tee "$upload_log"
+
+ preview_url="$(grep -oE 'https://[a-z0-9][a-z0-9-]*\.[a-z0-9-]+\.workers\.dev' "$upload_log" | tail -n 1 || true)"
+ echo "preview_url=${preview_url}" >> "$GITHUB_OUTPUT"
+
+ - name: Comment on PR
+ continue-on-error: true
+ uses: actions/github-script@v7
+ env:
+ PREVIEW_URL: ${{ steps.preview.outputs.preview_url }}
+ with:
+ script: |
+ const prNum = context.payload.pull_request.number;
+ const url = process.env.PREVIEW_URL;
+ const body = url
+ ? `**Preview deployed!**\n\n` +
+ `| Environment | URL |\n` +
+ `|-------------|-----|\n` +
+ `| Web | ${url} |\n\n` +
+ `This is a Cloudflare Workers preview version of this PR's build.`
+ : `**Preview built** — a Cloudflare Worker version was uploaded for this PR, ` +
+ `but no per-version preview URL was returned. Enable a workers.dev subdomain ` +
+ `on the Cloudflare account to get preview URLs.`;
+
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prNum,
+ });
+ const existing = comments.find(c =>
+ c.user.type === 'Bot' && c.body.includes('Preview deployed')
+ );
+
+ const params = { owner: context.repo.owner, repo: context.repo.repo, body };
+ if (existing) {
+ await github.rest.issues.updateComment({ ...params, comment_id: existing.id });
+ } else {
+ await github.rest.issues.createComment({ ...params, issue_number: prNum });
+ }
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a25c5c98
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+# Dependencies
+node_modules/
+.npm-cache/
+
+# Next.js
+web/.next/
+web/out/
+
+# OpenNext (Cloudflare) build output
+web/.open-next/
+
+# Wrangler
+.wrangler/
+web/.wrangler/
+web/.dev.vars
+
+# Build info
+**/tsconfig.tsbuildinfo
+
+# Runtime data
+web/waitlist.json
+
+# Env
+.env
+.env.local
+.env*.local
+
+# OS
+.DS_Store
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 00000000..2bd5a0a9
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+22
diff --git a/README.md b/README.md
index 188bd7d4..853a7272 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,52 @@
# agentrelay.com
-It's the, uh, agentrelay.com site I feel like that's obvious
+
+The Agent Relay front door: the public marketing/docs site, and (incoming) the
+Cloudflare router that fronts `agentrelay.com`. Runs entirely on Cloudflare —
+no AWS.
+
+## Layout
+
+```
+web/ Next.js marketing + docs site (OpenNext → Cloudflare Workers)
+```
+
+`router/` (the seamless front-door Cloudflare Worker) lands here in a later phase.
+
+## Develop
+
+```bash
+npm install # workspace root
+npm run dev # next dev
+npm run build # next build
+npm test # vitest
+npm run preview # build + run the OpenNext worker locally (wrangler dev)
+```
+
+## Deploy
+
+`web` is a Next.js app built with [`@opennextjs/cloudflare`](https://opennext.js.org/cloudflare)
+and deployed as a Cloudflare Worker via Wrangler (`web/wrangler.jsonc`). Wrangler
+auto-provisions the Worker, uploads static assets, and creates the
+`origin-web.agentrelay.com` DNS record + TLS cert (the `agentrelay.com` zone must
+live in the Cloudflare account).
+
+GitHub Actions:
+
+- `.github/workflows/deploy.yml` — production, on push to `main`
+ (`wrangler deploy` → `origin-web.agentrelay.com`)
+- `.github/workflows/preview.yml` — per-PR preview via `wrangler versions upload`
+ (ephemeral `*.workers.dev` URL, no custom domain; Cloudflare expires old
+ versions automatically — no stale-preview cleanup needed)
+
+Required GitHub Actions config (repository-level — prod and previews share the
+same Cloudflare account, so no per-environment scoping is needed):
+
+- Secrets: `CLOUDFLARE_API_TOKEN` (Workers Scripts + Workers Routes + DNS edit on
+ the account)
+- Vars: `CLOUDFLARE_ACCOUNT_ID`, `NEXT_PUBLIC_POSTHOG_KEY`
+ (optional `NEXT_PUBLIC_POSTHOG_HOST`, defaults to `https://i.agentrelay.com`)
+
+Pushes to `main` auto-deploy to production; PRs get an ephemeral preview version.
+
+The apex `agentrelay.com` reaches this site via the router's fallback; domain
+consolidation onto `*.agentrelay.com` is a later phase.
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..9e68a7d3
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,16236 @@
+{
+ "name": "agentrelay.com",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "agentrelay.com",
+ "version": "0.0.0",
+ "workspaces": [
+ "web"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@ant-design/colors": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz",
+ "integrity": "sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@ant-design/fast-color": "^3.0.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.1.2.tgz",
+ "integrity": "sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@emotion/hash": "^0.8.0",
+ "@emotion/unitless": "^0.7.5",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "stylis": "^4.3.4"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs-utils": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-2.1.2.tgz",
+ "integrity": "sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@ant-design/cssinjs": "^2.1.2",
+ "@babel/runtime": "^7.23.2",
+ "@rc-component/util": "^1.4.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/@ant-design/fast-color": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz",
+ "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@ant-design/icons": {
+ "version": "6.2.5",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.2.5.tgz",
+ "integrity": "sha512-0hKtoKqTjGFOndUyJLJmC9Cg6k4rEO7rLo6xmgbNJH+/ZX1C57RVals2v1j1knHl9n7Q+sBOveTvn931wLOCKw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@ant-design/colors": "^8.0.1",
+ "@ant-design/icons-svg": "^4.4.2",
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/icons-svg": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz",
+ "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@ant-design/react-slick": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-2.0.0.tgz",
+ "integrity": "sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "clsx": "^2.1.1",
+ "json2mq": "^0.2.0",
+ "throttle-debounce": "^5.0.0"
+ },
+ "peerDependencies": {
+ "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@antfu/install-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
+ "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "package-manager-detector": "^1.3.0",
+ "tinyexec": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@ast-grep/napi": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi/-/napi-0.40.5.tgz",
+ "integrity": "sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@ast-grep/napi-darwin-arm64": "0.40.5",
+ "@ast-grep/napi-darwin-x64": "0.40.5",
+ "@ast-grep/napi-linux-arm64-gnu": "0.40.5",
+ "@ast-grep/napi-linux-arm64-musl": "0.40.5",
+ "@ast-grep/napi-linux-x64-gnu": "0.40.5",
+ "@ast-grep/napi-linux-x64-musl": "0.40.5",
+ "@ast-grep/napi-win32-arm64-msvc": "0.40.5",
+ "@ast-grep/napi-win32-ia32-msvc": "0.40.5",
+ "@ast-grep/napi-win32-x64-msvc": "0.40.5"
+ }
+ },
+ "node_modules/@ast-grep/napi-darwin-arm64": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-arm64/-/napi-darwin-arm64-0.40.5.tgz",
+ "integrity": "sha512-2F072fGN0WTq7KI3okuEnkGJVEHLbi56Bw1H6NAMf7j2mJJeQWsRyGOMcyNnUXZDeNdvoMH0OB2a5wwUegY/nQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ast-grep/napi-darwin-x64": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-x64/-/napi-darwin-x64-0.40.5.tgz",
+ "integrity": "sha512-dJMidHZhhxuLBYNi6/FKI812jQ7wcFPSKkVPwviez2D+KvYagapUMAV/4dJ7FCORfguVk8Y0jpPAlYmWRT5nvA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ast-grep/napi-linux-arm64-gnu": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-gnu/-/napi-linux-arm64-gnu-0.40.5.tgz",
+ "integrity": "sha512-nBRCbyoS87uqkaw4Oyfe5VO+SRm2B+0g0T8ME69Qry9ShMf41a2bTdpcQx9e8scZPogq+CTwDHo3THyBV71l9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ast-grep/napi-linux-arm64-musl": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-musl/-/napi-linux-arm64-musl-0.40.5.tgz",
+ "integrity": "sha512-/qKsmds5FMoaEj6FdNzepbmLMtlFuBLdrAn9GIWCqOIcVcYvM1Nka8+mncfeXB/MFZKOrzQsQdPTWqrrQzXLrA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ast-grep/napi-linux-x64-gnu": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-gnu/-/napi-linux-x64-gnu-0.40.5.tgz",
+ "integrity": "sha512-DP4oDbq7f/1A2hRTFLhJfDFR6aI5mRWdEfKfHzRItmlKsR9WlcEl1qDJs/zX9R2EEtIDsSKRzuJNfJllY3/W8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ast-grep/napi-linux-x64-musl": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-musl/-/napi-linux-x64-musl-0.40.5.tgz",
+ "integrity": "sha512-BRZUvVBPUNpWPo6Ns8chXVzxHPY+k9gpsubGTHy92Q26ecZULd/dTkWWdnvfhRqttsSQ9Pe/XQdi5+hDQ6RYcg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ast-grep/napi-win32-arm64-msvc": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-arm64-msvc/-/napi-win32-arm64-msvc-0.40.5.tgz",
+ "integrity": "sha512-y95zSEwc7vhxmcrcH0GnK4ZHEBQrmrszRBNQovzaciF9GUqEcCACNLoBesn4V47IaOp4fYgD2/EhGRTIBFb2Ug==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ast-grep/napi-win32-ia32-msvc": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-ia32-msvc/-/napi-win32-ia32-msvc-0.40.5.tgz",
+ "integrity": "sha512-K/u8De62iUnFCzVUs7FBdTZ2Jrgc5/DLHqjpup66KxZ7GIM9/HGME/O8aSoPkpcAeCD4TiTZ11C1i5p5H98hTg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ast-grep/napi-win32-x64-msvc": {
+ "version": "0.40.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-x64-msvc/-/napi-win32-x64-msvc-0.40.5.tgz",
+ "integrity": "sha512-dqm5zg/o4Nh4VOQPEpMS23ot8HVd22gG0eg01t4CFcZeuzyuSgBlOL3N7xLbz3iH2sVkk7keuBwAzOIpTqziNQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@aws-crypto/crc32": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
+ "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/crc32c": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz",
+ "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz",
+ "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/supports-web-crypto": "^5.2.0",
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "@aws-sdk/util-locate-window": "^3.0.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
+ "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-js": "^5.2.0",
+ "@aws-crypto/supports-web-crypto": "^5.2.0",
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "@aws-sdk/util-locate-window": "^3.0.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-js": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz",
+ "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/supports-web-crypto": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz",
+ "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/util": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz",
+ "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.222.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-sdk/checksums": {
+ "version": "3.1000.7",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1000.7.tgz",
+ "integrity": "sha512-qh0fG/RtrFztst4+vn1HZehAvAhr5Jlq/WMP7e5KvvfF16oNVBc9CDNVdxdm19vzOY2x0qiDMFCRjhxQAusGWQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/crc32": "5.2.0",
+ "@aws-crypto/crc32c": "5.2.0",
+ "@aws-crypto/util": "5.2.0",
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-cloudfront": {
+ "version": "3.984.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.984.0.tgz",
+ "integrity": "sha512-couDuDLpJtoeWne/nYyJ+I+5ntBVdNgBVRTCoDaXuVV7OC3u/wz5Ps0+GogspEwMLEFoOJ8t691h3YXQtnpQTw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.973.6",
+ "@aws-sdk/credential-provider-node": "^3.972.5",
+ "@aws-sdk/middleware-host-header": "^3.972.3",
+ "@aws-sdk/middleware-logger": "^3.972.3",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.3",
+ "@aws-sdk/middleware-user-agent": "^3.972.6",
+ "@aws-sdk/region-config-resolver": "^3.972.3",
+ "@aws-sdk/types": "^3.973.1",
+ "@aws-sdk/util-endpoints": "3.984.0",
+ "@aws-sdk/util-user-agent-browser": "^3.972.3",
+ "@aws-sdk/util-user-agent-node": "^3.972.4",
+ "@smithy/config-resolver": "^4.4.6",
+ "@smithy/core": "^3.22.0",
+ "@smithy/fetch-http-handler": "^5.3.9",
+ "@smithy/hash-node": "^4.2.8",
+ "@smithy/invalid-dependency": "^4.2.8",
+ "@smithy/middleware-content-length": "^4.2.8",
+ "@smithy/middleware-endpoint": "^4.4.12",
+ "@smithy/middleware-retry": "^4.4.29",
+ "@smithy/middleware-serde": "^4.2.9",
+ "@smithy/middleware-stack": "^4.2.8",
+ "@smithy/node-config-provider": "^4.3.8",
+ "@smithy/node-http-handler": "^4.4.8",
+ "@smithy/protocol-http": "^5.3.8",
+ "@smithy/smithy-client": "^4.11.1",
+ "@smithy/types": "^4.12.0",
+ "@smithy/url-parser": "^4.2.8",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.28",
+ "@smithy/util-defaults-mode-node": "^4.2.31",
+ "@smithy/util-endpoints": "^3.2.8",
+ "@smithy/util-middleware": "^4.2.8",
+ "@smithy/util-retry": "^4.2.8",
+ "@smithy/util-stream": "^4.5.10",
+ "@smithy/util-utf8": "^4.2.0",
+ "@smithy/util-waiter": "^4.2.8",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-cloudfront/node_modules/@smithy/util-utf8": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.4.1.tgz",
+ "integrity": "sha512-OCnvu9xJnNJf0kJHI4101r4bf2l4niSMOzeiN5AgE97RnbzmYKmixc95VpiRRU5QqEab8jq2Afpfs1xM86KRSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-dynamodb": {
+ "version": "3.984.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.984.0.tgz",
+ "integrity": "sha512-8/Oft9MWQtbG6p9f8eY5fsKC2CcO5YVDlwive8eUYS9mEbgnyQxm68OyH26WvsSTykQ9QkIbR+fOG56RsIBODw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.973.6",
+ "@aws-sdk/credential-provider-node": "^3.972.5",
+ "@aws-sdk/dynamodb-codec": "^3.972.7",
+ "@aws-sdk/middleware-endpoint-discovery": "^3.972.3",
+ "@aws-sdk/middleware-host-header": "^3.972.3",
+ "@aws-sdk/middleware-logger": "^3.972.3",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.3",
+ "@aws-sdk/middleware-user-agent": "^3.972.6",
+ "@aws-sdk/region-config-resolver": "^3.972.3",
+ "@aws-sdk/types": "^3.973.1",
+ "@aws-sdk/util-endpoints": "3.984.0",
+ "@aws-sdk/util-user-agent-browser": "^3.972.3",
+ "@aws-sdk/util-user-agent-node": "^3.972.4",
+ "@smithy/config-resolver": "^4.4.6",
+ "@smithy/core": "^3.22.0",
+ "@smithy/fetch-http-handler": "^5.3.9",
+ "@smithy/hash-node": "^4.2.8",
+ "@smithy/invalid-dependency": "^4.2.8",
+ "@smithy/middleware-content-length": "^4.2.8",
+ "@smithy/middleware-endpoint": "^4.4.12",
+ "@smithy/middleware-retry": "^4.4.29",
+ "@smithy/middleware-serde": "^4.2.9",
+ "@smithy/middleware-stack": "^4.2.8",
+ "@smithy/node-config-provider": "^4.3.8",
+ "@smithy/node-http-handler": "^4.4.8",
+ "@smithy/protocol-http": "^5.3.8",
+ "@smithy/smithy-client": "^4.11.1",
+ "@smithy/types": "^4.12.0",
+ "@smithy/url-parser": "^4.2.8",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.28",
+ "@smithy/util-defaults-mode-node": "^4.2.31",
+ "@smithy/util-endpoints": "^3.2.8",
+ "@smithy/util-middleware": "^4.2.8",
+ "@smithy/util-retry": "^4.2.8",
+ "@smithy/util-utf8": "^4.2.0",
+ "@smithy/util-waiter": "^4.2.8",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-dynamodb/node_modules/@smithy/util-utf8": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.4.1.tgz",
+ "integrity": "sha512-OCnvu9xJnNJf0kJHI4101r4bf2l4niSMOzeiN5AgE97RnbzmYKmixc95VpiRRU5QqEab8jq2Afpfs1xM86KRSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3": {
+ "version": "3.984.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.984.0.tgz",
+ "integrity": "sha512-7ny2Slr93Y+QniuluvcfWwyDi32zWQfznynL56Tk0vVh7bWrvS/odm8WP2nInKicRVNipcJHY2YInur6Q/9V0A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha1-browser": "5.2.0",
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.973.6",
+ "@aws-sdk/credential-provider-node": "^3.972.5",
+ "@aws-sdk/middleware-bucket-endpoint": "^3.972.3",
+ "@aws-sdk/middleware-expect-continue": "^3.972.3",
+ "@aws-sdk/middleware-flexible-checksums": "^3.972.4",
+ "@aws-sdk/middleware-host-header": "^3.972.3",
+ "@aws-sdk/middleware-location-constraint": "^3.972.3",
+ "@aws-sdk/middleware-logger": "^3.972.3",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.3",
+ "@aws-sdk/middleware-sdk-s3": "^3.972.6",
+ "@aws-sdk/middleware-ssec": "^3.972.3",
+ "@aws-sdk/middleware-user-agent": "^3.972.6",
+ "@aws-sdk/region-config-resolver": "^3.972.3",
+ "@aws-sdk/signature-v4-multi-region": "3.984.0",
+ "@aws-sdk/types": "^3.973.1",
+ "@aws-sdk/util-endpoints": "3.984.0",
+ "@aws-sdk/util-user-agent-browser": "^3.972.3",
+ "@aws-sdk/util-user-agent-node": "^3.972.4",
+ "@smithy/config-resolver": "^4.4.6",
+ "@smithy/core": "^3.22.0",
+ "@smithy/eventstream-serde-browser": "^4.2.8",
+ "@smithy/eventstream-serde-config-resolver": "^4.3.8",
+ "@smithy/eventstream-serde-node": "^4.2.8",
+ "@smithy/fetch-http-handler": "^5.3.9",
+ "@smithy/hash-blob-browser": "^4.2.9",
+ "@smithy/hash-node": "^4.2.8",
+ "@smithy/hash-stream-node": "^4.2.8",
+ "@smithy/invalid-dependency": "^4.2.8",
+ "@smithy/md5-js": "^4.2.8",
+ "@smithy/middleware-content-length": "^4.2.8",
+ "@smithy/middleware-endpoint": "^4.4.12",
+ "@smithy/middleware-retry": "^4.4.29",
+ "@smithy/middleware-serde": "^4.2.9",
+ "@smithy/middleware-stack": "^4.2.8",
+ "@smithy/node-config-provider": "^4.3.8",
+ "@smithy/node-http-handler": "^4.4.8",
+ "@smithy/protocol-http": "^5.3.8",
+ "@smithy/smithy-client": "^4.11.1",
+ "@smithy/types": "^4.12.0",
+ "@smithy/url-parser": "^4.2.8",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.28",
+ "@smithy/util-defaults-mode-node": "^4.2.31",
+ "@smithy/util-endpoints": "^3.2.8",
+ "@smithy/util-middleware": "^4.2.8",
+ "@smithy/util-retry": "^4.2.8",
+ "@smithy/util-stream": "^4.5.10",
+ "@smithy/util-utf8": "^4.2.0",
+ "@smithy/util-waiter": "^4.2.8",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region": {
+ "version": "3.984.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.984.0.tgz",
+ "integrity": "sha512-TaWbfYCwnuOSvDSrgs7QgoaoXse49E7LzUkVOUhoezwB7bkmhp+iojADm7UepCEu4021SquD7NG1xA+WCvmldA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-sdk-s3": "^3.972.6",
+ "@aws-sdk/types": "^3.973.1",
+ "@smithy/protocol-http": "^5.3.8",
+ "@smithy/signature-v4": "^5.3.8",
+ "@smithy/types": "^4.12.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-utf8": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.4.1.tgz",
+ "integrity": "sha512-OCnvu9xJnNJf0kJHI4101r4bf2l4niSMOzeiN5AgE97RnbzmYKmixc95VpiRRU5QqEab8jq2Afpfs1xM86KRSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-sqs": {
+ "version": "3.984.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.984.0.tgz",
+ "integrity": "sha512-TDvHpOUWlpanc3xQ5Xw0y8L2hoojBFCCSmXQ/6rKqGOf1ScX3dMA+K9aF0Zp0iwjhSh4VvsHD42esl8XwQZDjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.973.6",
+ "@aws-sdk/credential-provider-node": "^3.972.5",
+ "@aws-sdk/middleware-host-header": "^3.972.3",
+ "@aws-sdk/middleware-logger": "^3.972.3",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.3",
+ "@aws-sdk/middleware-sdk-sqs": "^3.972.5",
+ "@aws-sdk/middleware-user-agent": "^3.972.6",
+ "@aws-sdk/region-config-resolver": "^3.972.3",
+ "@aws-sdk/types": "^3.973.1",
+ "@aws-sdk/util-endpoints": "3.984.0",
+ "@aws-sdk/util-user-agent-browser": "^3.972.3",
+ "@aws-sdk/util-user-agent-node": "^3.972.4",
+ "@smithy/config-resolver": "^4.4.6",
+ "@smithy/core": "^3.22.0",
+ "@smithy/fetch-http-handler": "^5.3.9",
+ "@smithy/hash-node": "^4.2.8",
+ "@smithy/invalid-dependency": "^4.2.8",
+ "@smithy/md5-js": "^4.2.8",
+ "@smithy/middleware-content-length": "^4.2.8",
+ "@smithy/middleware-endpoint": "^4.4.12",
+ "@smithy/middleware-retry": "^4.4.29",
+ "@smithy/middleware-serde": "^4.2.9",
+ "@smithy/middleware-stack": "^4.2.8",
+ "@smithy/node-config-provider": "^4.3.8",
+ "@smithy/node-http-handler": "^4.4.8",
+ "@smithy/protocol-http": "^5.3.8",
+ "@smithy/smithy-client": "^4.11.1",
+ "@smithy/types": "^4.12.0",
+ "@smithy/url-parser": "^4.2.8",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.28",
+ "@smithy/util-defaults-mode-node": "^4.2.31",
+ "@smithy/util-endpoints": "^3.2.8",
+ "@smithy/util-middleware": "^4.2.8",
+ "@smithy/util-retry": "^4.2.8",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/util-utf8": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.4.1.tgz",
+ "integrity": "sha512-OCnvu9xJnNJf0kJHI4101r4bf2l4niSMOzeiN5AgE97RnbzmYKmixc95VpiRRU5QqEab8jq2Afpfs1xM86KRSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/core": {
+ "version": "3.974.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.22.tgz",
+ "integrity": "sha512-YofH63shc6YRdXjz80BJkpJW+Bkn0Cuu2dn4Rv7s9G2Idt58tgtzQEWxrR2xVljlVfIBeUjPuULnSVYLke3sUQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.13",
+ "@aws-sdk/xml-builder": "^3.972.30",
+ "@aws/lambda-invoke-store": "^0.2.2",
+ "@smithy/core": "^3.24.6",
+ "@smithy/signature-v4": "^5.4.6",
+ "@smithy/types": "^4.14.3",
+ "bowser": "^2.11.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-env": {
+ "version": "3.972.48",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.48.tgz",
+ "integrity": "sha512-h6FEC95fbexUd6zxm4PdgS82bTcI2PRtUb2ZwMipb/Xr8bPwtf0G8rBo2jp7NA24Mbx2JA8/WingiYpA9RCCyw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-http": {
+ "version": "3.972.50",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.50.tgz",
+ "integrity": "sha512-lJO3OLpjvz5m/RSBQmsG/CEUGsvCy5ruxKwPQaOCqxqCMuyYT2BZwQUTDZVVwqQ9LrZKuK24JSa6r31hL/tvkg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/fetch-http-handler": "^5.4.6",
+ "@smithy/node-http-handler": "^4.7.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-ini": {
+ "version": "3.972.55",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.55.tgz",
+ "integrity": "sha512-TBoF4buBGYhXjdZAryayY2TrkQj2B2KfE/msG4V53XCt+w0EhEwM2JRjx8p2grJ2C6gtH5++SAwEvGMRdi0yyw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/credential-provider-env": "^3.972.48",
+ "@aws-sdk/credential-provider-http": "^3.972.50",
+ "@aws-sdk/credential-provider-login": "^3.972.54",
+ "@aws-sdk/credential-provider-process": "^3.972.48",
+ "@aws-sdk/credential-provider-sso": "^3.972.54",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.54",
+ "@aws-sdk/nested-clients": "^3.997.22",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/credential-provider-imds": "^4.3.7",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-login": {
+ "version": "3.972.54",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.54.tgz",
+ "integrity": "sha512-hBWI3wZTdTGiuMfmPts6AWbAjFfRniOQnqx68tc2cQvRKWawFbN9wkLOVPWM1FAOyowZU73mC6Fi+rHSHNyLFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/nested-clients": "^3.997.22",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-node": {
+ "version": "3.972.57",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.57.tgz",
+ "integrity": "sha512-u6dClpzNdWf1HGWz4wwhdXi1wiOofCLniM9S4BQQGlLAN9TW7VB+ld5V533GdKrYMaFeBGFqKnj0JCYvynLqwQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/credential-provider-env": "^3.972.48",
+ "@aws-sdk/credential-provider-http": "^3.972.50",
+ "@aws-sdk/credential-provider-ini": "^3.972.55",
+ "@aws-sdk/credential-provider-process": "^3.972.48",
+ "@aws-sdk/credential-provider-sso": "^3.972.54",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.54",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/credential-provider-imds": "^4.3.7",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-process": {
+ "version": "3.972.48",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.48.tgz",
+ "integrity": "sha512-w6VZwojPt12WnEkAUy6Nu4K6sWCbBmR7QX390b0nE6vRvkXbrYr9Lq9VySGkfjiMjpUA87op+J4EgvRmtWIDoQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-sso": {
+ "version": "3.972.54",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.54.tgz",
+ "integrity": "sha512-23uZpIpF2SIFDCa1fcWa202tK4gGeyvX6GIIAjiB8WBsvsVRBMnJ/7dCxHzxf7eZT7GToJg837LDIBnZsl/VUg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/nested-clients": "^3.997.22",
+ "@aws-sdk/token-providers": "3.1071.0",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-web-identity": {
+ "version": "3.972.54",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.54.tgz",
+ "integrity": "sha512-0Iv5QttS6wcATlodYKgvQj6B9Db51rx7NU9fqu0PoLeS4BIgdYMc/QK4smwLwpm5RFrs02V/eLyEFp3FklvlNQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/nested-clients": "^3.997.22",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/dynamodb-codec": {
+ "version": "3.973.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.22.tgz",
+ "integrity": "sha512-QBs7/nWHsPA0wTEqhU5iPgaDjeZzQHhmwJr6Q0f56rW3Fk8ExJQgXFzEg/l48iDwJVUIMLjPqhw7dNP3cShUxA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/endpoint-cache": {
+ "version": "3.972.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.972.8.tgz",
+ "integrity": "sha512-bBmkG0Dnhfq0/T4Z0PpUr7HkncBVaWvvCbvafeaUM+yC9wa8GGjLJmonq0QL17REB9WivgGeYgWQ5A80Uw5UnQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "mnemonist": "0.38.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-bucket-endpoint": {
+ "version": "3.972.26",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.26.tgz",
+ "integrity": "sha512-DSIJDe1WTc7V9JPfpGfRatZFlzWYX4/fd0K7mOoyRijXFqo6YrPBptebOi1K+fJIkKL7zzsocL4pbMsMtuX/oA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-sdk-s3": "^3.972.53",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-endpoint-discovery": {
+ "version": "3.972.19",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.19.tgz",
+ "integrity": "sha512-FMgyzUq3Jh+ONRYxryBRNdBd+FUX8PwRl07ccQknNdoms6KCeAEusCkl6whqpDrPQ6OH0ddeSifKyqYSs2DLIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/endpoint-cache": "^3.972.8",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-expect-continue": {
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.22.tgz",
+ "integrity": "sha512-LmEmLhxrxh7uEQrWEpfqlhj8tkfClrLiOF5sDR/k18pbNaVSleSAwcwEMrlz1MOFnXB5ibS7boY1r8tYZKkvxQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-sdk-s3": "^3.972.53",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-flexible-checksums": {
+ "version": "3.974.32",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.32.tgz",
+ "integrity": "sha512-KhuzFMzUbb3oEj43CdPDbEJ/RG/RkErkmXk3J/LE8OPFNvkCn8PYPMpjOLgzAzvxBacsSyytdWf+R50q0alJ4w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/checksums": "^3.1000.7",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-host-header": {
+ "version": "3.972.23",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.23.tgz",
+ "integrity": "sha512-sWeojvaTI86wGRIt5HQb9o50V6AExi+NV3VrH2AwBlzV47a+m4JAejaC8CObaLXGVqSRqGt1B9AGYONp+NcTXg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-location-constraint": {
+ "version": "3.972.19",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.19.tgz",
+ "integrity": "sha512-Ka3boCBtsOj7LC7w0z5dBF2SmxSUJEkvxb5Qd+MnFAwJyBc22FmR2LpB/3dQqsiRLtS81WEBfnpUkj4Sx3u/qA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-sdk-s3": "^3.972.53",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-logger": {
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.22.tgz",
+ "integrity": "sha512-opYE2yUoQKJJz5QrGylnZeaBRmUaRzUSxlW40cX4LnmwO5XI61IWC3k/LDbJwF15AvmlNbome+glhIlFJzCWJw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-recursion-detection": {
+ "version": "3.972.24",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.24.tgz",
+ "integrity": "sha512-M0gO9RgAppZx7yDvFRB1pdnyzd34Gnt/JjpT9HoMZxxDYnAkYNa3eabbjsf2A6ffxDFAvZKFdkjthrQxlQgcRg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-sdk-s3": {
+ "version": "3.972.53",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.53.tgz",
+ "integrity": "sha512-keWp6Z5cEIJzPwoCf/WRm0ceAeephPDDivhRsK/xXs2ZYXyypJ2/DL9G1IR0bz/s+iZC0EgzmFV4r7rlvLlxQQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.35",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-sdk-sqs": {
+ "version": "3.972.31",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.31.tgz",
+ "integrity": "sha512-56ifsBmK9bLn5EE/t6c0nmjOB1BO8cJDLkA1VOlsN1GR85ROqnaCwVDspqcwsLaBDgPlwyYNedoDIoT3t6Ho1A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-ssec": {
+ "version": "3.972.19",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.19.tgz",
+ "integrity": "sha512-x0FepbRqAXRwfCw+bZN78rcVw3ZInXFXd8FjNQl2GBRTIZ/t7aj/p70qeG3ZnrpN4e5SeDi0y39gs1J/jUP4FQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-sdk-s3": "^3.972.53",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-user-agent": {
+ "version": "3.972.52",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.52.tgz",
+ "integrity": "sha512-p4ltuhV74lYk4sKYj1iWo7UQuzHPYHfbcBKbKaZb/tKy92lqDMt/gaI+w4ShmEUOXV9k/uV/nRpoH0tHqpwXeA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/nested-clients": {
+ "version": "3.997.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.22.tgz",
+ "integrity": "sha512-4IwtcYSxEIVw5hcp8ogq0CMbFNZFw7jJUetpfFUhFFeqsa1K8j2Ihg2hnxLyOp3stMZnXda6VzOmPi1AFZQXcg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.35",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/fetch-http-handler": "^5.4.6",
+ "@smithy/node-http-handler": "^4.7.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/region-config-resolver": {
+ "version": "3.972.26",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.26.tgz",
+ "integrity": "sha512-wDR6i4YBvJTGGElCQlXQd42f6AraatbU9ckvrjsKu1wFBwH0vTUBxFo6kqRWfuleKhDkMqSkQujCL1eWbFFLtw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/signature-v4-multi-region": {
+ "version": "3.996.35",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.35.tgz",
+ "integrity": "sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/signature-v4": "^5.4.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/token-providers": {
+ "version": "3.1071.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1071.0.tgz",
+ "integrity": "sha512-4LDW2Qob6LoLFuqYSYZq2AyTE9koSE9+i+n5UZcm10GpmQOK0zRD9L4uYlzItiTKksIWgC/qMFChAi3RvKYtMg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "@aws-sdk/nested-clients": "^3.997.22",
+ "@aws-sdk/types": "^3.973.13",
+ "@smithy/core": "^3.24.6",
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/types": {
+ "version": "3.973.13",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.13.tgz",
+ "integrity": "sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.14.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-endpoints": {
+ "version": "3.984.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.984.0.tgz",
+ "integrity": "sha512-9ebjLA0hMKHeVvXEtTDCCOBtwjb0bOXiuUV06HNeVdgAjH6gj4x4Zwt4IBti83TiyTGOCl5YfZqGx4ehVsasbQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.973.1",
+ "@smithy/types": "^4.12.0",
+ "@smithy/url-parser": "^4.2.8",
+ "@smithy/util-endpoints": "^3.2.8",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-locate-window": {
+ "version": "3.965.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.8.tgz",
+ "integrity": "sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-user-agent-browser": {
+ "version": "3.972.23",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.23.tgz",
+ "integrity": "sha512-5jF9hUGr6vplVanji97KRmbbTUlStXOp/WSz7xArD9fIWxId7tZoq0fPVRNJC5xOuaGzkwO3jHZn8BicrwfwSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-sdk/util-user-agent-node": {
+ "version": "3.973.38",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.38.tgz",
+ "integrity": "sha512-s8V8B0m09mDVFJn7iBDWTZTAFXPHXqjGDhoQ0WK3JKac/Ckm0VkwF7Y/b2RS7/4VMijzaGzdkVwx1jfEHNQLAQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.22",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/xml-builder": {
+ "version": "3.972.30",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.30.tgz",
+ "integrity": "sha512-StElZPEoBquWwNqw1AcfpzEyZqJvFxouG+mpDNYlcH6ZOrqd2CuIryv+8LV8gNHZUOyKyJF3Dq9vxaXEmDR9TQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.14.3",
+ "fast-xml-parser": "5.7.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws/lambda-invoke-store": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz",
+ "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
+ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@base-ui/react": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.5.0.tgz",
+ "integrity": "sha512-z1gSAlced1yY+iM+mHDEtIkD8UI3Ebs52MuBPxvV6f5hRutk+xvCH/wuB7hDqDzK9JG5FoMz5nhrqtSs1wjt1A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@base-ui/utils": "0.2.9",
+ "@floating-ui/react-dom": "^2.1.8",
+ "@floating-ui/utils": "^0.2.11",
+ "use-sync-external-store": "^1.6.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@date-fns/tz": "^1.2.0",
+ "@types/react": "^17 || ^18 || ^19",
+ "date-fns": "^4.0.0",
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "@date-fns/tz": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ },
+ "date-fns": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@base-ui/utils": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.2.9.tgz",
+ "integrity": "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@floating-ui/utils": "^0.2.11",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.6.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^17 || ^18 || ^19",
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@braintree/sanitize-url": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz",
+ "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@chevrotain/types": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz",
+ "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==",
+ "license": "Apache-2.0",
+ "peer": true
+ },
+ "node_modules/@cloudflare/kv-asset-handler": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz",
+ "integrity": "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==",
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@cloudflare/unenv-preset": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz",
+ "integrity": "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==",
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "peerDependencies": {
+ "unenv": "2.0.0-rc.24",
+ "workerd": ">1.20260305.0 <2.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "workerd": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@cloudflare/workerd-darwin-64": {
+ "version": "1.20260617.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260617.1.tgz",
+ "integrity": "sha512-jWwmgEVVWbsHNrLSNXzwjJaH90VzRxq1cWkQFUidxyeUPnMxemeNE8I9qFAfrpzGgE11e9sKDcE3ettJW08swQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@cloudflare/workerd-darwin-arm64": {
+ "version": "1.20260617.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260617.1.tgz",
+ "integrity": "sha512-LHH7b565g9znfCUOkwbec6FG2rmRbsgCy6aJiU9KN662mNheWl5sw/iKleiFSiljPKQQP3HkjnC/NSkdgi/aSA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@cloudflare/workerd-linux-64": {
+ "version": "1.20260617.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260617.1.tgz",
+ "integrity": "sha512-FMnaAKXe4Cfd8TQurCVd9fs2XQVBFRCsP+Id/SRdUv89MlwYu9zXfoyx6BxM+brPTIUK38SHbo8iaxiwzLi9JQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@cloudflare/workerd-linux-arm64": {
+ "version": "1.20260617.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260617.1.tgz",
+ "integrity": "sha512-MRoifFYcqbxxIIQy7PqO5tFY/qPFSnjXzakWl0sO93l+HLyG35jRAgOi6jfqa4kBxc7gKKtH861DcewjxUfkjA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@cloudflare/workerd-windows-64": {
+ "version": "1.20260617.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260617.1.tgz",
+ "integrity": "sha512-rgBV9wQrv0OSKgCTTbhFUFY3sLGNANZ88aqaLvtmEn2gmbFVb1J4PDGochVUdB7NSEp4D/ghHva6/8SZmbONpw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@dnd-kit/accessibility": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
+ "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/core": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
+ "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@dnd-kit/accessibility": "^3.1.1",
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/modifiers": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz",
+ "integrity": "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.3.0",
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/sortable": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
+ "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.3.0",
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/utilities": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
+ "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dotenvx/dotenvx": {
+ "version": "1.31.0",
+ "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.31.0.tgz",
+ "integrity": "sha512-GeDxvtjiRuoyWVU9nQneId879zIyNdL05bS7RKiqMkfBSKpHMWHLoRyRqjYWLaXmX/llKO1hTlqHDmatkQAjPA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "commander": "^11.1.0",
+ "dotenv": "^16.4.5",
+ "eciesjs": "^0.4.10",
+ "execa": "^5.1.1",
+ "fdir": "^6.2.0",
+ "ignore": "^5.3.0",
+ "object-treeify": "1.1.33",
+ "picomatch": "^4.0.2",
+ "which": "^4.0.0"
+ },
+ "bin": {
+ "dotenvx": "src/cli/dotenvx.js",
+ "git-dotenvx": "src/cli/dotenvx.js"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/@dotenvx/dotenvx/node_modules/commander": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
+ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@ecies/ciphers": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.6.tgz",
+ "integrity": "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "bun": ">=1",
+ "deno": ">=2.7.10",
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "@noble/ciphers": "^1.0.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emoji-mart/data": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
+ "integrity": "sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@emoji-mart/react": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@emoji-mart/react/-/react-1.1.1.tgz",
+ "integrity": "sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "emoji-mart": "^5.2",
+ "react": "^16.8 || ^17 || ^18"
+ }
+ },
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache/node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/css": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz",
+ "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.13.5",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
+ "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/serialize/node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/serialize/node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+ "license": "MIT"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
+ "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz",
+ "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz",
+ "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz",
+ "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz",
+ "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz",
+ "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz",
+ "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz",
+ "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz",
+ "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz",
+ "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz",
+ "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz",
+ "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz",
+ "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz",
+ "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz",
+ "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz",
+ "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz",
+ "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz",
+ "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz",
+ "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz",
+ "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz",
+ "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
+ "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz",
+ "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz",
+ "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz",
+ "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz",
+ "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
+ "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.11"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
+ "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@floating-ui/core": "^1.7.5",
+ "@floating-ui/utils": "^0.2.11"
+ }
+ },
+ "node_modules/@floating-ui/react": {
+ "version": "0.27.19",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz",
+ "integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.1.8",
+ "@floating-ui/utils": "^0.2.11",
+ "tabbable": "^6.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=17.0.0",
+ "react-dom": ">=17.0.0"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz",
+ "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@floating-ui/dom": "^1.7.6"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
+ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@giscus/react": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@giscus/react/-/react-3.1.0.tgz",
+ "integrity": "sha512-0TCO2TvL43+oOdyVVGHDItwxD1UMKP2ZYpT6gXmhFOqfAJtZxTzJ9hkn34iAF/b6YzyJ4Um89QIt9z/ajmAEeg==",
+ "peer": true,
+ "dependencies": {
+ "giscus": "^1.6.0"
+ },
+ "peerDependencies": {
+ "react": "^16 || ^17 || ^18 || ^19",
+ "react-dom": "^16 || ^17 || ^18 || ^19"
+ }
+ },
+ "node_modules/@iconify/types": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
+ "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@iconify/utils": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.3.tgz",
+ "integrity": "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@antfu/install-pkg": "^1.1.0",
+ "@iconify/types": "^2.0.0",
+ "import-meta-resolve": "^4.2.0"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz",
+ "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@lit-labs/ssr-dom-shim": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz",
+ "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==",
+ "license": "BSD-3-Clause",
+ "peer": true
+ },
+ "node_modules/@lit/reactive-element": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz",
+ "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "@lit-labs/ssr-dom-shim": "^1.5.0"
+ }
+ },
+ "node_modules/@lobehub/emojilib": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@lobehub/emojilib/-/emojilib-1.0.0.tgz",
+ "integrity": "sha512-s9KnjaPjsEefaNv150G3aifvB+J3P4eEKG+epY9zDPS2BeB6+V2jELWqAZll+nkogMaVovjEE813z3V751QwGw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@lobehub/fluent-emoji": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@lobehub/fluent-emoji/-/fluent-emoji-4.1.0.tgz",
+ "integrity": "sha512-R1MB2lfUkDvB7XAQdRzY75c1dx/tB7gEvBPaEEMarzKfCJWmXm7rheS6caVzmgwAlq5sfmTbxPL+un99sp//Yw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@lobehub/emojilib": "^1.0.0",
+ "antd-style": "^4.1.0",
+ "emoji-regex": "^10.6.0",
+ "es-toolkit": "^1.43.0",
+ "lucide-react": "^0.562.0",
+ "url-join": "^5.0.0"
+ },
+ "peerDependencies": {
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ }
+ },
+ "node_modules/@lobehub/fluent-emoji/node_modules/lucide-react": {
+ "version": "0.562.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz",
+ "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==",
+ "license": "ISC",
+ "peer": true,
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@lobehub/icons": {
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.10.0.tgz",
+ "integrity": "sha512-CIpjkISCLRK7haDtSugGFd0o3odaJts8ewJOkUiEFtns3xvsqbl8i24eowBnjw+yMDQVQyNONlhqTD58YC6Ljg==",
+ "license": "MIT",
+ "workspaces": [
+ "packages/*"
+ ],
+ "dependencies": {
+ "antd-style": "^4.1.0",
+ "es-toolkit": "^1.45.1",
+ "lucide-react": "^0.469.0",
+ "polished": "^4.3.1"
+ },
+ "peerDependencies": {
+ "@lobehub/ui": "^5.0.0",
+ "antd": "^6.1.1",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ }
+ },
+ "node_modules/@lobehub/icons/node_modules/lucide-react": {
+ "version": "0.469.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.469.0.tgz",
+ "integrity": "sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@lobehub/ui": {
+ "version": "5.15.17",
+ "resolved": "https://registry.npmjs.org/@lobehub/ui/-/ui-5.15.17.tgz",
+ "integrity": "sha512-hhsrjQvNiS8rjRPwtqHGntzWgSH+30XjSvRmlnq6rhEv6tmdyZD1M9aR6wFp59xNLmApU5ZlTc1ujx5eEv6Z5Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@ant-design/cssinjs": "^2.1.2",
+ "@base-ui/react": "1.5.0",
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/modifiers": "^9.0.0",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
+ "@emoji-mart/data": "^1.2.1",
+ "@emoji-mart/react": "^1.1.1",
+ "@emotion/is-prop-valid": "^1.4.0",
+ "@floating-ui/react": "^0.27.19",
+ "@giscus/react": "^3.1.0",
+ "@mdx-js/mdx": "^3.1.1",
+ "@mdx-js/react": "^3.1.1",
+ "@pierre/diffs": "1.2.8",
+ "@radix-ui/react-slot": "^1.2.5",
+ "@shikijs/core": "^4.2.0",
+ "@shikijs/transformers": "^4.2.0",
+ "@splinetool/runtime": "0.9.526",
+ "ahooks": "^3.9.7",
+ "antd-style": "^4.1.0",
+ "chroma-js": "^3.2.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "dayjs": "^1.11.21",
+ "emoji-mart": "^5.6.0",
+ "es-toolkit": "^1.47.0",
+ "fast-deep-equal": "^3.1.3",
+ "hast-util-to-jsx-runtime": "^2.3.6",
+ "html-url-attributes": "^3.0.1",
+ "immer": "^11.1.8",
+ "katex": "^0.16.47",
+ "leva": "^0.10.1",
+ "lucide-react": "^1.17.0",
+ "marked": "^17.0.6",
+ "mermaid": "^11.15.0",
+ "motion": "^12.40.0",
+ "numeral": "^2.0.6",
+ "polished": "^4.3.1",
+ "query-string": "^9.4.0",
+ "rc-collapse": "^4.0.0",
+ "rc-footer": "^0.6.8",
+ "rc-image": "^7.12.0",
+ "rc-input-number": "^9.5.0",
+ "rc-menu": "^9.16.1",
+ "re-resizable": "^6.11.2",
+ "react-avatar-editor": "^15.1.0",
+ "react-error-boundary": "^6.1.2",
+ "react-hotkeys-hook": "^5.3.2",
+ "react-markdown": "^10.1.0",
+ "react-merge-refs": "^3.0.2",
+ "react-rnd": "^10.5.3",
+ "react-zoom-pan-pinch": "^3.7.0",
+ "rehype-github-alerts": "^4.2.0",
+ "rehype-katex": "^7.0.1",
+ "rehype-raw": "^7.0.0",
+ "remark-breaks": "^4.0.0",
+ "remark-cjk-friendly": "^2.0.1",
+ "remark-gfm": "^4.0.1",
+ "remark-github": "^12.0.0",
+ "remark-math": "^6.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.1.2",
+ "remend": "^1.3.0",
+ "shiki": "^4.2.0",
+ "shiki-stream": "^0.1.5",
+ "swr": "^2.4.1",
+ "ts-md5": "^2.0.1",
+ "unified": "^11.0.5",
+ "url-join": "^5.0.0",
+ "use-merge-value": "^1.2.0",
+ "uuid": "^13.0.2",
+ "vfile": "^6.0.3",
+ "virtua": "^0.49.1"
+ },
+ "peerDependencies": {
+ "@lobehub/fluent-emoji": "^4.0.0",
+ "@lobehub/icons": "^5.0.0",
+ "antd": "^6.1.1",
+ "motion": "^12.0.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ }
+ },
+ "node_modules/@mdx-js/mdx": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz",
+ "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdx": "^2.0.0",
+ "acorn": "^8.0.0",
+ "collapse-white-space": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-util-scope": "^1.0.0",
+ "estree-walker": "^3.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "markdown-extensions": "^2.0.0",
+ "recma-build-jsx": "^1.0.0",
+ "recma-jsx": "^1.0.0",
+ "recma-stringify": "^1.0.0",
+ "rehype-recma": "^1.0.0",
+ "remark-mdx": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "source-map": "^0.7.0",
+ "unified": "^11.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/@mdx-js/react": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz",
+ "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/mdx": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16",
+ "react": ">=16"
+ }
+ },
+ "node_modules/@mermaid-js/parser": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.1.tgz",
+ "integrity": "sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@chevrotain/types": "~11.1.1"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
+ "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.7.tgz",
+ "integrity": "sha512-tMJizPlj6ZYpBMMdK8S0LJufrP4QTdR6pcv9KQ/bVETPAmg0j1mlHE9G2c38UyGHxoBapgwuj7XjbGJ2RcDFOg==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.7.tgz",
+ "integrity": "sha512-vm1EDI/pVaBNNiychmxk3fft+OhQPVD9cIM/tReLZIQ3TfQ4kqI9DwKk00dzuS1ulC7icbrzCFrmRRlk9PfNdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.7.tgz",
+ "integrity": "sha512-O3IRSv1ZBL1zs0WrIgefTEcTKFVn+ryxBNe54erJ6KsD+2f/Mmt7g2jOYh8PSBdUwPtKQJuCsTMlZ7tIu2AcsQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.7.tgz",
+ "integrity": "sha512-Re6PZtjBDd0aMU+VcZcC/PrIvj4WhrjDYtMhhCVQamWN4L90EVP0pcEOBQD25prSlw7OzNw5QpHLWMilRLsRNw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.7.tgz",
+ "integrity": "sha512-qyogG9QtBzWxgJfeGBvOEHI3851gTfCF3wLZ5RDLTBJGAmE9p1qDwKCOdrBrvBzRvYDT+gUDp72pzlSEfAXgNA==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.7.tgz",
+ "integrity": "sha512-Vhe4ZDuBpmMogrGi5D4R2Kq4JAQlj6+wvgaFYy31zfES0zPmt6TLA+cuYpM/OLrPZjo2MYQTHVqNUSCR6+fDZQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.7.tgz",
+ "integrity": "sha512-srvian89JahFLw1YLBEuhvPJ0DO5lpUeJQMXy4xYo7g628ZlNgXdNkqoxSAv9OYrBfByh6vxISMwW/mRbzCY+g==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.7.tgz",
+ "integrity": "sha512-GX3wvLpULFuRFJzwHaKfm7QZJ18F4ZSuxlPJ96BoBglCzBmdSjyeBKF+ZhWhvL/ckxNfLnNa7bsObO2ipYpszw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.7.tgz",
+ "integrity": "sha512-J4WlM72NMk076Qsg0jTdK3SNXatlSdnjW7L7oNGLst1tAGjHrJh/FYi+pw9wyIjEtGRKDNzD0zuiY16oWYWVaw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@noble/ciphers": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
+ "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/curves": {
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz",
+ "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "1.8.0"
+ },
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@nodable/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodable"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@node-minify/core": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@node-minify/core/-/core-8.0.6.tgz",
+ "integrity": "sha512-/vxN46ieWDLU67CmgbArEvOb41zlYFOkOtr9QW9CnTrBLuTyGgkyNWC2y5+khvRw3Br58p2B5ZVSx/PxCTru6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@node-minify/utils": "8.0.6",
+ "glob": "9.3.5",
+ "mkdirp": "1.0.4"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@node-minify/core/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@node-minify/core/node_modules/brace-expansion": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
+ "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@node-minify/core/node_modules/glob": {
+ "version": "9.3.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz",
+ "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "minimatch": "^8.0.2",
+ "minipass": "^4.2.4",
+ "path-scurry": "^1.6.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@node-minify/core/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@node-minify/core/node_modules/minimatch": {
+ "version": "8.0.7",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz",
+ "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@node-minify/core/node_modules/minipass": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz",
+ "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@node-minify/core/node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@node-minify/core/node_modules/path-scurry/node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/@node-minify/terser": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@node-minify/terser/-/terser-8.0.6.tgz",
+ "integrity": "sha512-grQ1ipham743ch2c3++C8Isk6toJnxJSyDiwUI/IWUCh4CZFD6aYVw6UAY40IpCnjrq5aXGwiv5OZJn6Pr0hvg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@node-minify/utils": "8.0.6",
+ "terser": "5.16.9"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@node-minify/utils": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@node-minify/utils/-/utils-8.0.6.tgz",
+ "integrity": "sha512-csY4qcR7jUwiZmkreNTJhcypQfts2aY2CK+a+rXgXUImZiZiySh0FvwHjRnlqWKvg+y6ae9lHFzDRjBTmqlTIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "gzip-size": "6.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@opennextjs/aws": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@opennextjs/aws/-/aws-4.0.2.tgz",
+ "integrity": "sha512-nXQPT8GZDV+NWMJV9mD/Ywxyo+tCZfFvrR6jpEOYNcxK/AqiEDlJzPybGOrHUDWAYdR1b6tmh+MoR3VbqIao3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ast-grep/napi": "^0.40.5",
+ "@aws-sdk/client-cloudfront": "3.984.0",
+ "@aws-sdk/client-dynamodb": "3.984.0",
+ "@aws-sdk/client-lambda": "3.984.0",
+ "@aws-sdk/client-s3": "3.984.0",
+ "@aws-sdk/client-sqs": "3.984.0",
+ "@node-minify/core": "^8.0.6",
+ "@node-minify/terser": "^8.0.6",
+ "@tsconfig/node18": "^1.0.3",
+ "aws4fetch": "^1.0.20",
+ "chalk": "^5.6.2",
+ "cookie": "^1.0.2",
+ "esbuild": "0.25.4",
+ "express": "^5.1.0",
+ "path-to-regexp": "^6.3.0",
+ "urlpattern-polyfill": "^10.1.0",
+ "yaml": "^2.8.1"
+ },
+ "bin": {
+ "open-next": "dist/index.js"
+ },
+ "peerDependencies": {
+ "next": ">=15.5.18 <16 || >=16.2.6"
+ }
+ },
+ "node_modules/@opennextjs/aws/node_modules/@aws-sdk/client-lambda": {
+ "version": "3.984.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.984.0.tgz",
+ "integrity": "sha512-kqwNBIGNxGVhINwgN/UQfdsQkaMjbu9PFV2EhATWouV+RT60uMjK9JENgLDwbgJmEVbbnPsh9HaZ5KKwPSdiDg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.973.6",
+ "@aws-sdk/credential-provider-node": "^3.972.5",
+ "@aws-sdk/middleware-host-header": "^3.972.3",
+ "@aws-sdk/middleware-logger": "^3.972.3",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.3",
+ "@aws-sdk/middleware-user-agent": "^3.972.6",
+ "@aws-sdk/region-config-resolver": "^3.972.3",
+ "@aws-sdk/types": "^3.973.1",
+ "@aws-sdk/util-endpoints": "3.984.0",
+ "@aws-sdk/util-user-agent-browser": "^3.972.3",
+ "@aws-sdk/util-user-agent-node": "^3.972.4",
+ "@smithy/config-resolver": "^4.4.6",
+ "@smithy/core": "^3.22.0",
+ "@smithy/eventstream-serde-browser": "^4.2.8",
+ "@smithy/eventstream-serde-config-resolver": "^4.3.8",
+ "@smithy/eventstream-serde-node": "^4.2.8",
+ "@smithy/fetch-http-handler": "^5.3.9",
+ "@smithy/hash-node": "^4.2.8",
+ "@smithy/invalid-dependency": "^4.2.8",
+ "@smithy/middleware-content-length": "^4.2.8",
+ "@smithy/middleware-endpoint": "^4.4.12",
+ "@smithy/middleware-retry": "^4.4.29",
+ "@smithy/middleware-serde": "^4.2.9",
+ "@smithy/middleware-stack": "^4.2.8",
+ "@smithy/node-config-provider": "^4.3.8",
+ "@smithy/node-http-handler": "^4.4.8",
+ "@smithy/protocol-http": "^5.3.8",
+ "@smithy/smithy-client": "^4.11.1",
+ "@smithy/types": "^4.12.0",
+ "@smithy/url-parser": "^4.2.8",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.28",
+ "@smithy/util-defaults-mode-node": "^4.2.31",
+ "@smithy/util-endpoints": "^3.2.8",
+ "@smithy/util-middleware": "^4.2.8",
+ "@smithy/util-retry": "^4.2.8",
+ "@smithy/util-stream": "^4.5.10",
+ "@smithy/util-utf8": "^4.2.0",
+ "@smithy/util-waiter": "^4.2.8",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@opennextjs/aws/node_modules/@smithy/util-utf8": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.4.1.tgz",
+ "integrity": "sha512-OCnvu9xJnNJf0kJHI4101r4bf2l4niSMOzeiN5AgE97RnbzmYKmixc95VpiRRU5QqEab8jq2Afpfs1xM86KRSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@opennextjs/aws/node_modules/aws4fetch": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz",
+ "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@opennextjs/aws/node_modules/yaml": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/@opennextjs/cloudflare": {
+ "version": "1.19.11",
+ "resolved": "https://registry.npmjs.org/@opennextjs/cloudflare/-/cloudflare-1.19.11.tgz",
+ "integrity": "sha512-spZ7YsZZW9q/OJStaZlJhuklYKei5e2tNQ7PMi3DDSJ7x7167m7EYYHQZfVN5gwJBDkMR2jUDW88oHjnGz4YYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ast-grep/napi": "^0.40.5",
+ "@dotenvx/dotenvx": "1.31.0",
+ "@opennextjs/aws": "4.0.2",
+ "ci-info": "^4.2.0",
+ "cloudflare": "^4.4.1",
+ "comment-json": "^4.5.1",
+ "enquirer": "^2.4.1",
+ "glob": "^12.0.0",
+ "ts-tqdm": "^0.8.6",
+ "yargs": "^18.0.0"
+ },
+ "bin": {
+ "opennextjs-cloudflare": "dist/cli/index.js"
+ },
+ "peerDependencies": {
+ "next": ">=15.5.18 <16 || >=16.2.6",
+ "wrangler": "^4.86.0"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.133.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz",
+ "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@pierre/diffs": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@pierre/diffs/-/diffs-1.2.8.tgz",
+ "integrity": "sha512-HVaWzZ1cW5GDKodivaPCN1hU05DGvoLiG/uvVBNZODD+qY2NTr36KYgATGhb79Vr8OCKNG3qATTWJEwbkMGfzA==",
+ "license": "apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@pierre/theme": "1.0.3",
+ "@shikijs/transformers": "^3.0.0",
+ "diff": "8.0.3",
+ "hast-util-to-html": "9.0.5",
+ "lru_map": "0.4.1",
+ "shiki": "^3.0.0"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1 || ^19.0.0",
+ "react-dom": "^18.3.1 || ^19.0.0"
+ }
+ },
+ "node_modules/@pierre/diffs/node_modules/@shikijs/core": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz",
+ "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.5"
+ }
+ },
+ "node_modules/@pierre/diffs/node_modules/@shikijs/engine-javascript": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz",
+ "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "oniguruma-to-es": "^4.3.4"
+ }
+ },
+ "node_modules/@pierre/diffs/node_modules/@shikijs/engine-oniguruma": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz",
+ "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ }
+ },
+ "node_modules/@pierre/diffs/node_modules/@shikijs/langs": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz",
+ "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@pierre/diffs/node_modules/@shikijs/themes": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz",
+ "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@pierre/diffs/node_modules/@shikijs/transformers": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.23.0.tgz",
+ "integrity": "sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/core": "3.23.0",
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@pierre/diffs/node_modules/@shikijs/types": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz",
+ "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@pierre/diffs/node_modules/shiki": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz",
+ "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/core": "3.23.0",
+ "@shikijs/engine-javascript": "3.23.0",
+ "@shikijs/engine-oniguruma": "3.23.0",
+ "@shikijs/langs": "3.23.0",
+ "@shikijs/themes": "3.23.0",
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@pierre/theme": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@pierre/theme/-/theme-1.0.3.tgz",
+ "integrity": "sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "vscode": "^1.0.0"
+ }
+ },
+ "node_modules/@poppinss/colors": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz",
+ "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^4.1.5"
+ }
+ },
+ "node_modules/@poppinss/dumper": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz",
+ "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@poppinss/colors": "^4.1.5",
+ "@sindresorhus/is": "^7.0.2",
+ "supports-color": "^10.0.0"
+ }
+ },
+ "node_modules/@poppinss/exception": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz",
+ "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@posthog/core": {
+ "version": "1.35.3",
+ "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.35.3.tgz",
+ "integrity": "sha512-EsGPbSLl39Jgo2KZ+kI9UAxFnh5nddaN5bNm2rXvUwF+vGmam9eN1EXeNbxhRU7ulEeIiGdm7XjoU7pzavkgIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@posthog/types": "^1.390.2"
+ }
+ },
+ "node_modules/@posthog/next": {
+ "version": "0.4.97",
+ "resolved": "https://registry.npmjs.org/@posthog/next/-/next-0.4.97.tgz",
+ "integrity": "sha512-s9+lH5XmgJe96ZNYiyzlFTt5zx/fBas3QT/hc69PaJz91IaK/uf7lQjzBa4eKPf4Qio+iiohCm0VwwM0z+xdbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@posthog/core": "^1.35.2",
+ "@posthog/react": "^1.10.2",
+ "posthog-js": "^1.390.2",
+ "posthog-node": "^5.38.1",
+ "server-only": "^0.0.1"
+ },
+ "peerDependencies": {
+ "@vercel/functions": ">=1.0.0",
+ "next": ">=13.0.0",
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@vercel/functions": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@posthog/react": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/@posthog/react/-/react-1.10.2.tgz",
+ "integrity": "sha512-/KXzUZiSByE9/6fHmnfDVVvld/3pvBuM6KFUuT5NkR4XwOHEAT/EwNwhUUFOOJpsyDa0PaEWWHZS6VY9r2CB3Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": ">=16.8.0",
+ "posthog-js": ">=1.257.2",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@posthog/types": {
+ "version": "1.390.2",
+ "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.390.2.tgz",
+ "integrity": "sha512-WcfKz2GNn2vfDX8vXmJYbKxegPxVWHuDQ/pHdAn0HoZDXDFnEp/+x3qBQA+fEvtbPjjtjgAt2wIgJMlM7asx7g==",
+ "license": "MIT"
+ },
+ "node_modules/@primer/octicons": {
+ "version": "19.28.1",
+ "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.28.1.tgz",
+ "integrity": "sha512-pwSilXmgNrbVF2bChkh4zZtUyb4Vr4niYhA9PhUdtjVz86A2iwA/YjjopHS0suT+I7niUZJEepEpmSC7kARKNQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "object-assign": "^4.1.1"
+ }
+ },
+ "node_modules/@radix-ui/primitive": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz",
+ "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@radix-ui/react-arrow": {
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.10.tgz",
+ "integrity": "sha512-j2VTDz1vgCsmuG0k5lBfOcM8n5JPFqZBcMryasFjHYMhwxYL5SRUV5lMSUpRdNtw3D/Sv8pzJtrlAgkssYSsQQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz",
+ "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-context": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz",
+ "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.13.tgz",
+ "integrity": "sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.4",
+ "@radix-ui/react-compose-refs": "1.1.3",
+ "@radix-ui/react-primitive": "2.1.6",
+ "@radix-ui/react-use-callback-ref": "1.1.2",
+ "@radix-ui/react-use-escape-keydown": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-id": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz",
+ "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.1.tgz",
+ "integrity": "sha512-bhnq/0DEPTi2lsOD3J5rTL65qUKHbKbhqHsmN9TMiclSXpipi651ooUKPPp6G5lF/WiHBdn1s0Wuqsn+myVAvw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.0.0",
+ "@radix-ui/react-arrow": "1.1.10",
+ "@radix-ui/react-compose-refs": "1.1.3",
+ "@radix-ui/react-context": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.6",
+ "@radix-ui/react-use-callback-ref": "1.1.2",
+ "@radix-ui/react-use-layout-effect": "1.1.2",
+ "@radix-ui/react-use-rect": "1.1.2",
+ "@radix-ui/react-use-size": "1.1.2",
+ "@radix-ui/rect": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.12.tgz",
+ "integrity": "sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.6",
+ "@radix-ui/react-use-layout-effect": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-presence": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz",
+ "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.6.tgz",
+ "integrity": "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-slot": "1.3.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.0.tgz",
+ "integrity": "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tooltip": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.10.tgz",
+ "integrity": "sha512-NlNe8D0dWEpVfXFli90IO6X07Josx/b1iu98tDnx9Xv0HT4wLIL+m2VOheMHhK7qbp2HoTBqALEFzGyZs/levw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.4",
+ "@radix-ui/react-compose-refs": "1.1.3",
+ "@radix-ui/react-context": "1.1.4",
+ "@radix-ui/react-dismissable-layer": "1.1.13",
+ "@radix-ui/react-id": "1.1.2",
+ "@radix-ui/react-popper": "1.3.1",
+ "@radix-ui/react-portal": "1.1.12",
+ "@radix-ui/react-presence": "1.1.6",
+ "@radix-ui/react-primitive": "2.1.6",
+ "@radix-ui/react-slot": "1.3.0",
+ "@radix-ui/react-use-controllable-state": "1.2.3",
+ "@radix-ui/react-visually-hidden": "1.2.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz",
+ "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz",
+ "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-use-effect-event": "0.0.3",
+ "@radix-ui/react-use-layout-effect": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-effect-event": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz",
+ "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.2.tgz",
+ "integrity": "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-use-callback-ref": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz",
+ "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-rect": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.2.tgz",
+ "integrity": "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/rect": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-size": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.2.tgz",
+ "integrity": "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.6.tgz",
+ "integrity": "sha512-jCE0WljWifTI4niIMCll06kGpsJTAPiZVU9H4WR1N6qW7At9ystHbN7dDB+we2xH535roFHj7qKS+RGj0FMDWQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/rect": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.2.tgz",
+ "integrity": "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@rc-component/async-validator": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-6.0.0.tgz",
+ "integrity": "sha512-D3AGQwdyE58gmvx6waVSXJ80JGO+IY5L2O8HDnSOex7JNlzB3GuN/4hyHNTdhy2qtOhkpbIjmeAN3tL993wKbA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.24.4"
+ },
+ "engines": {
+ "node": ">=14.x"
+ }
+ },
+ "node_modules/@rc-component/cascader": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/cascader/-/cascader-1.16.1.tgz",
+ "integrity": "sha512-wxLopwM+EBed0zNNGdnGE4coYoqcO+XD42fHgn+pDvO+XzhNFbdgSlSNXdKocIYqccvqgWvoxDPNb0OVRdi59A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/select": "~1.7.1",
+ "@rc-component/tree": "~1.3.2",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/checkbox": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/checkbox/-/checkbox-2.0.0.tgz",
+ "integrity": "sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/collapse": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/collapse/-/collapse-1.2.0.tgz",
+ "integrity": "sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/color-picker": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-3.1.1.tgz",
+ "integrity": "sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@ant-design/fast-color": "^3.0.1",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/context": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-2.0.2.tgz",
+ "integrity": "sha512-uiGpAlblCNlziHPwj4S4Iy/oemeuz/hR03mbiEjTCXwsqOIN3BOzsRMyDwpyO5Fm0vIEEJRUf9ZtbRLbhksuTA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.11.0"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/dialog": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/dialog/-/dialog-1.9.0.tgz",
+ "integrity": "sha512-zbAAogkg4kkKum79sLE6M+vq1jSAW25zdkafrahgcTP9t9S//SD634Znd1A4c8F2Gc12ZKnehGLsVaaOvZzD2A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/motion": "^1.1.3",
+ "@rc-component/portal": "^2.1.0",
+ "@rc-component/util": "^1.9.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/drawer": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/drawer/-/drawer-1.4.2.tgz",
+ "integrity": "sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/portal": "^2.1.3",
+ "@rc-component/util": "^1.9.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/dropdown": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/dropdown/-/dropdown-1.0.2.tgz",
+ "integrity": "sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.2.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.11.0",
+ "react-dom": ">=16.11.0"
+ }
+ },
+ "node_modules/@rc-component/form": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@rc-component/form/-/form-1.8.5.tgz",
+ "integrity": "sha512-d24EYtvUOBhxEtSd/EqIu9DaMuqrWF2IRIvAFCTM6NQ/GJIYNr8DvEpUSUlv2uPxEJ0ZPwYQ+wwlGIAaiHvdrw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/async-validator": "^6.0.0",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/image": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/image/-/image-1.9.0.tgz",
+ "integrity": "sha512-khF7w7xkBH5B1bsBcI1FSUZdkyd1aqpl2eYyILCqCzzQH3XdfehGUaZTnptyaJJfs09/R5hv9jXWyazOMFIClQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/motion": "^1.0.0",
+ "@rc-component/portal": "^2.1.2",
+ "@rc-component/util": "^1.10.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/input": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/input/-/input-1.3.1.tgz",
+ "integrity": "sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/resize-observer": "^1.1.1",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@rc-component/input-number": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/input-number/-/input-number-1.6.2.tgz",
+ "integrity": "sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/mini-decimal": "^1.0.1",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mentions": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/mentions/-/mentions-1.9.0.tgz",
+ "integrity": "sha512-WUwfFKDSOF5S9UPsNsXcLYtzjTxBGsftTXWRbZuxX6BYrsySISTnujfJNgaaQ6qVzaCDJ35QUkZKvsYxip1C5g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/input": "~1.3.0",
+ "@rc-component/menu": "~1.3.0",
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/menu": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/menu/-/menu-1.3.1.tgz",
+ "integrity": "sha512-pSZl9nBPgKgxN0aaW7NilIBEwWsc+43S+ulGdWAg9afak96dNOGWsGx0DLLBB1VQsAJvo6bQMTDzXoPlEHsBEw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/overflow": "^1.0.0",
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mini-decimal": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.4.tgz",
+ "integrity": "sha512-xiuXcaCwyOWpD8a8scdExFl+bntNphAW8XeenL1ig2en0AAZY0Pcp4pC0dI22qJ+NvxKn9RoNIoRdqYU3BLH4w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.18.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@rc-component/motion": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@rc-component/motion/-/motion-1.3.3.tgz",
+ "integrity": "sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mutate-observer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-2.0.1.tgz",
+ "integrity": "sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/notification": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-2.0.7.tgz",
+ "integrity": "sha512-nqZzpf6BPdaj+3ILx7si79LLmqPKyUmQoXa+/9gg0SkH0v1DbD66oJgRMSBEVnd/zUT3D4gwxWIHUKebYf2ZXQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/overflow": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/overflow/-/overflow-1.0.1.tgz",
+ "integrity": "sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@rc-component/resize-observer": "^1.0.1",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/pagination": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/pagination/-/pagination-1.3.0.tgz",
+ "integrity": "sha512-12ahTY+HPITg1L2bjWKXUqBJe/oOnpA2QsChdCjthqLVf/e19StiCsv8OLKpWoHbc+8PFEkNjRqRqrLoRBHjFw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/picker": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/picker/-/picker-1.10.0.tgz",
+ "integrity": "sha512-vVOXP2RVWozwpERGUFAehVH1Jz6o/uRrAb9qSZm1LC+iJs8rvEwFo1bzz2jlOYV+uWwu0dIuG86tnDui14Ea0w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/overflow": "^1.0.0",
+ "@rc-component/resize-observer": "^1.0.0",
+ "@rc-component/trigger": "^3.6.15",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12.x"
+ },
+ "peerDependencies": {
+ "date-fns": ">= 2.x",
+ "dayjs": ">= 1.x",
+ "luxon": ">= 3.x",
+ "moment": ">= 2.x",
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ },
+ "peerDependenciesMeta": {
+ "date-fns": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rc-component/portal": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-2.2.1.tgz",
+ "integrity": "sha512-ck+r1kW/JSv0wxPji3KN2ss9K6Z0qqwusw/mf/0JobXhZ8hC2ejZwCJObW/SvDi0uhA0VzmCnx0CaCci95tcmA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/progress": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/progress/-/progress-1.0.2.tgz",
+ "integrity": "sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.2.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/qrcode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-2.0.0.tgz",
+ "integrity": "sha512-aAv3QhPP1xyafuTZOxub6a54pCeBnN3IwQkpETrBtthq4BL5IgxnCbuoBWPDpdLw1y1j6BgBUCAKV92+yX06Dw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/rate": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/rate/-/rate-1.0.1.tgz",
+ "integrity": "sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/resize-observer": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/resize-observer/-/resize-observer-1.1.2.tgz",
+ "integrity": "sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/segmented": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/segmented/-/segmented-1.3.0.tgz",
+ "integrity": "sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@rc-component/select": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.7.1.tgz",
+ "integrity": "sha512-GZ1cMJk2xQh0VHyOQjjG8drYL4iu24NcbkXioUcReQOCUr+ub/3fmRonZe6cRPEZhWMbJdeHsqnEltogDaZ5Tg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/overflow": "^1.0.0",
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.11.1",
+ "@rc-component/virtual-list": "^1.2.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/@rc-component/slider": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/slider/-/slider-1.0.1.tgz",
+ "integrity": "sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/steps": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/steps/-/steps-1.2.2.tgz",
+ "integrity": "sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.2.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/switch": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rc-component/switch/-/switch-1.0.3.tgz",
+ "integrity": "sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/table": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/table/-/table-1.10.2.tgz",
+ "integrity": "sha512-b3PjqB9Gp25p5t/zq+9QrbXbodkptT8/zvLmwgd2FNPUUtaYyDnQqfxeD5a7ao8E8lpinLHsi2u2vdfPhyNvAw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/context": "^2.0.1",
+ "@rc-component/resize-observer": "^1.0.0",
+ "@rc-component/util": "^1.11.1",
+ "@rc-component/virtual-list": "^1.0.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/tabs": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/tabs/-/tabs-1.9.1.tgz",
+ "integrity": "sha512-6mY08Fce6aNOHuGsxbzT+f2ekgL9mg1cGGHkittMlVGymjGg+kGupu5v90sRxcUd/paRU9jclLLXtF/PkK1FUA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/dropdown": "~1.0.0",
+ "@rc-component/menu": "~1.3.0",
+ "@rc-component/motion": "^1.1.3",
+ "@rc-component/resize-observer": "^1.0.0",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/tooltip": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/tooltip/-/tooltip-1.4.0.tgz",
+ "integrity": "sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/trigger": "^3.7.1",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/tour": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-2.4.0.tgz",
+ "integrity": "sha512-aui4r4TqmTzwaBgcQxHYep8kM8PTjZFufjokObpy35KfFeZ0k9ArquWFZqegQlH24P14t+F0qO0mGTgzlav1yg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/portal": "^2.2.0",
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.7.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/tree": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/tree/-/tree-1.3.2.tgz",
+ "integrity": "sha512-bJFj46wEkpBPnWyTm18XmgAgNQ/4YvprxMOPPY2a6rmhGJYxLuNKEFiL5Qej4Qctu9wHJm8WW+v2SYskafE0kA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/motion": "^1.0.0",
+ "@rc-component/util": "^1.11.1",
+ "@rc-component/virtual-list": "^1.2.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=10.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/@rc-component/tree-select": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/tree-select/-/tree-select-1.10.0.tgz",
+ "integrity": "sha512-E1U4pn2LAbXEhLJdzIzid7WYbIuFbkTIctuFoeC6weppf8UbPR3+YYB6/ay0c0ksand4gXMRQpa1Z60Auo7VJA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/select": "~1.7.0",
+ "@rc-component/tree": "~1.3.0",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/@rc-component/trigger": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-3.9.1.tgz",
+ "integrity": "sha512-LNsYvz60mrLJ/kRvKcHE7boUvcQfVMCfRqZ71x3Fo9AOiZ1KKIEqkzMA8DNvz2V3Bcvir/vwQNn7JF1NPODQ7Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/portal": "^2.2.0",
+ "@rc-component/resize-observer": "^1.1.1",
+ "@rc-component/util": "^1.2.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/upload": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/upload/-/upload-1.1.1.tgz",
+ "integrity": "sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/util": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.11.1.tgz",
+ "integrity": "sha512-awVlI3ub2vqfqkYxOBc/uQ0efm3jw0wcrhtO/YWLyZfxiKXczKwNbVuhlnyxytDt7H9pbbVQiqr+O6MLATtRYg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-mobile": "^5.0.0",
+ "react-is": "^18.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/virtual-list": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/virtual-list/-/virtual-list-1.2.0.tgz",
+ "integrity": "sha512-iavRm1Jo4GDbASQwdGa7jFyk93RvSOo9xHyBT4QL1pgFJj/Fdf1G+3RErH7/7BmAMvx2AkF62mjGYxDbXsK9TQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.20.0",
+ "@rc-component/resize-observer": "^1.0.1",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
+ "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz",
+ "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz",
+ "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz",
+ "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz",
+ "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz",
+ "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz",
+ "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz",
+ "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz",
+ "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz",
+ "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz",
+ "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz",
+ "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz",
+ "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz",
+ "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz",
+ "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@shikijs/core": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.2.0.tgz",
+ "integrity": "sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/primitive": "4.2.0",
+ "@shikijs/types": "4.2.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/engine-javascript": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.2.0.tgz",
+ "integrity": "sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.2.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "oniguruma-to-es": "^4.3.6"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.2.0.tgz",
+ "integrity": "sha512-hTorK1dffPkpbMUk6Z+828PgRo7d07HbnizoP0hNPFjhxMHctj0Px/qoHeGMYafc6ju+u9iMldN4JbVzNQM++g==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.2.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/langs": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.2.0.tgz",
+ "integrity": "sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/primitive": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.2.0.tgz",
+ "integrity": "sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.2.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/stream": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/stream/-/stream-4.2.0.tgz",
+ "integrity": "sha512-OaMUUStdIZ+l1GJad9uVACR3Xvgwo4y+RmEuDMU62cgFMMg1IBCaIFmvzAR2HiCpGtwoc/qPfpNnP+ivgrPXZg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/core": "4.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "react": "^19.0.0",
+ "solid-js": "^1.9.0",
+ "vue": "^3.2.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "solid-js": {
+ "optional": true
+ },
+ "vue": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@shikijs/themes": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.2.0.tgz",
+ "integrity": "sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/transformers": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-4.2.0.tgz",
+ "integrity": "sha512-pKrYVNUr1oPjJvs76gkPPirDySx3GKG9O88P2Y3AQ+7AjSFws9Y+Ry/Q/6Yg6QpyigzjdQ6H5JAMNAvLXZ63dw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/core": "4.2.0",
+ "@shikijs/types": "4.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.2.0.tgz",
+ "integrity": "sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
+ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
+ "license": "MIT"
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz",
+ "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/@smithy/config-resolver": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.6.1.tgz",
+ "integrity": "sha512-VgMsljDnHwHjJQAvLmkaOc1d3On5/2Zh1/21++9FFhYS9YKMnGgolJF73EU3QfFPSzK5jTTFlGk3em0hiRKtwg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/core": {
+ "version": "3.25.1",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.25.1.tgz",
+ "integrity": "sha512-zpDbpXBCBsxfLtG2GEUyfgvHvSFrw5CwDZSNzL0v52gx/c3oPlPbm+7W7num8xs6vyiUBn+bvYPHcQDOXZynCQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/crc32": "5.2.0",
+ "@smithy/types": "^4.15.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/credential-provider-imds": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.1.tgz",
+ "integrity": "sha512-TSAF5NHgxEsllbErYWbK8aLnl5L601NGc5VYJlSPsKnf3YlkhdoBN+geGcaU00oiw2OK3QO5LA3QNXiiWhCidQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "@smithy/types": "^4.15.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-browser": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.4.1.tgz",
+ "integrity": "sha512-u1wI6ilZtur9uBmfndaccTMOvzdszEhmnG9CmnB7ws0omdYxkpRvO112UZdDxo7Ab8TIbrRKtITMua5ntBCJ9g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-config-resolver": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.5.1.tgz",
+ "integrity": "sha512-tnPdkY8zM98wboQFRoog1XV/Yjv7Zz61cDJxciHqyHwuCLo/jhkRCUwnicpmUJ6EzGys0FkwveHZNkIO7lcOpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.4.1.tgz",
+ "integrity": "sha512-h/4/rKk5d2w+vRBTbVzOvu4kxq+zNmtuGIlcI8hpE/yhsR2Ky+4YZRVU1JLx/sp+Ee9eoG8/hRjvAErcQOCQoQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/fetch-http-handler": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.5.1.tgz",
+ "integrity": "sha512-96JrD1q71anokymx9Iblb+zKmNQYNstlV/25A9ZYIJ2A0rp1r7/GZAIm0bDWSmVvz3DpNOCZuabzsiL+w0UHhw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "@smithy/types": "^4.15.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/hash-blob-browser": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.4.1.tgz",
+ "integrity": "sha512-5pDuFvbCim3Er0SDLJWiR9P9r73ranu/FK3ZV/i3/mIVYi+P10h/rcaS51Kgh+1TKQbZSCnsGJkbJJnM27T9bg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/hash-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.4.1.tgz",
+ "integrity": "sha512-hFez+gkhSHuBoo3t/xTNp+rKx8Hd5VaJPBexBtVkyA/XqMgOUm1eNY00nIEtUDMTjdRbfJASIWXbY2bGpjRYcA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/hash-stream-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.4.1.tgz",
+ "integrity": "sha512-5mIF0Qnpkokf8qN1LH2+cI09TQOvFedvNNK/eRhfhbC5E9zxtyTPSprWQFI4Kb6tRp+iBsllO/aaxeDjgASREQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/invalid-dependency": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.4.1.tgz",
+ "integrity": "sha512-eMmXNUign1HaUU5KGcnOWMeTt21oeuvamPaP5ZwM6VeyuHeoBKOSGOW3DVnJbBX79Xd4/yeLcbZKrIS3HpCDMg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@smithy/md5-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.4.1.tgz",
+ "integrity": "sha512-6xEY7LhQYgj0mEKUa4TNNkPaF/EwY4Z7OM6lmOwOqaykK3BRMqabHVf0wk+C1e1JYTmx6rYVxz+8JWxbLErytA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-content-length": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.4.1.tgz",
+ "integrity": "sha512-3WPxBy1eRXsVnXh7twWJWCkoV7cEjPwBbYaJ0RuIVFP/LjcKkAbLuWOtpv2j770yPgA+Jyf6y7z3QMEICVfxrQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-endpoint": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.6.1.tgz",
+ "integrity": "sha512-TgyNn9dd8oC87+9M5Iarpf0Nr8UTtlJW0FwEMtZHc6CbhD2NZDv9kYapnnLPmhdIOvX/XDatn26Cl9UhdZJsNQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-retry": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.7.1.tgz",
+ "integrity": "sha512-PYBrqSyaoW0f5aU7tFzJhrm0F0t3zIrEhwF69cIHSkDRlcP90ueF/Atm0npgRk2xSvcTYzfbs1kCT8Fw/SS+3w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-serde": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.4.1.tgz",
+ "integrity": "sha512-WoibOTvoqWrHuahqyHx677qPKDqB/Xp4pW2793kAHgh/WD1fAdLp6DrNfvXViUKxJWb/W5QNAv3R9nYcrQnbuw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-stack": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.4.1.tgz",
+ "integrity": "sha512-QKi4auCXmRAKOlu9YRF39Ni4JynmwoHvIwWXZS+trP/Zm8r7Z5KmaiW7R3CJERfMcrSzqMHjadzGugUuzUnZKQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-config-provider": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.5.1.tgz",
+ "integrity": "sha512-0eZmif/bZe3dbrazG24BmormVJ7E0yoK9IN4wcNopYLM0MNElhgd3QfPyLeuLWWi0HR+MYu3JBy0LMzAZ8u2aQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-http-handler": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.8.1.tgz",
+ "integrity": "sha512-emtXvoky671puri18ETf64AFIQUGIEA093F2drXpBgB0OGnBLjcwNR3CA2mYu62IAqNsS56xa5lnTxAgPq7cjw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "@smithy/types": "^4.15.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/protocol-http": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.5.1.tgz",
+ "integrity": "sha512-GRQofBqr/pPNjR04mU0JjdjsbV8F33KCxSHMFXBb2mWJXxyEalNe814Y4cFd4Efd5tNucaOc31L3/8wXlymjfg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/signature-v4": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.5.1.tgz",
+ "integrity": "sha512-X9rVls3En0z3NtrmguTmpRM0/NqtWUxBjal6fcAkwtsub+gOdLZ6kD+V7xhUgFMGdG14bHbZ7M5QjaRI1+DatQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "@smithy/types": "^4.15.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/smithy-client": {
+ "version": "4.14.1",
+ "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.14.1.tgz",
+ "integrity": "sha512-/GsNFh1enYQB4w3x1+ZHR3IFtEya3UlcyF/8+0GUX6qObxPsm8uA4EHzopmcO5Z6DjxLwjAOco+1wwCr9mis0A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "@smithy/types": "^4.15.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/types": {
+ "version": "4.15.0",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.15.0.tgz",
+ "integrity": "sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/url-parser": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.4.1.tgz",
+ "integrity": "sha512-nuY/KEW0mHOS1ZA3NN4xjSOR5TxIwnUSbvSh39KUhBGQ3xKkaf89ftnvw0yI+Ekr3GGobzO4Elj9t+4POP8iDg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-base64": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.5.1.tgz",
+ "integrity": "sha512-B4bqm9OpfcKda2sppILh7MFXNBS6IkcCYJAiFzgUWbC75luhBvgfHd5eMvEFpTfhDtpUQAPQrcCX0twMNeExmw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-body-length-browser": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.4.1.tgz",
+ "integrity": "sha512-eOowAQTjfrhd0Uci2vPppHx4SMdWD8wouzniFd8AOU0HCp6bmEsRZd3pEDJHhXWZ2jh7erz4+qpY+tVJabRHKA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-body-length-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.4.1.tgz",
+ "integrity": "sha512-eEDHEciyZ3qXYY2rNBWFApS3iaTq5etqkUju4p5CO1ofF7cGFeH36yM3YlC7o1PHwMN4qvq5jiU+UkG4kzaHGw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@smithy/util-defaults-mode-browser": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.5.1.tgz",
+ "integrity": "sha512-Q19s6CWcliyjkGesjKikzDuUUu8/a6ijKM8x9S84WfUgVFimcX2WM9gfZIXbdrD93YD7AFklX4ZsPa/H0DKSNw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-defaults-mode-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.4.1.tgz",
+ "integrity": "sha512-I6LP1stNp6TkSjHWw3s5hXDDYUOxML5hBWAP6jshMYcsufZiLErpD5rc0657IWTMbYocs2OjRAjQJ8KMt4OpiQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-endpoints": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.6.1.tgz",
+ "integrity": "sha512-7/p7K1wgOrwA+NJDk7ca+eGWs9G2xQqhcZhFa0T7inEOav31ZmGTWgPIR382ydh5TsBE8K/kR0sAss1+PlBqrA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-middleware": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.4.1.tgz",
+ "integrity": "sha512-UGV7Kg5WwiJaUoYQcS6VPe/qa/BRYnTLx5drtkCExohCxnJ0IKCtXe/HZUig8NswFjXf4xmsg4lAVRQHppB3fA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-retry": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.5.1.tgz",
+ "integrity": "sha512-SQJhb5j3Btnqvh4lYdWbVH5uuoJN1kdn+UI/ehsEwhV7kHMOp4DwTBxFpUiMIgviiZsIrOk0YyI9R42yBZw90A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-stream": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.7.1.tgz",
+ "integrity": "sha512-PxEh26CfAkeY9et6ak8gvu+23QdXyBwvf8bnkltD2cei9bMPT9rx5wSBWv1+mTuFtZzdpcksvQfdT0JA8IFcXQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@smithy/util-waiter": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.5.1.tgz",
+ "integrity": "sha512-pqI0LUxwqkjMYCyIm8A5MrTYpfS3WTD9210QL5MW8m1KQ83MWFR+rjuy1eBnttpZwFqQ9DWnEwr8TZsq9wVTbw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.25.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@speed-highlight/core": {
+ "version": "1.2.17",
+ "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.17.tgz",
+ "integrity": "sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/@splinetool/runtime": {
+ "version": "0.9.526",
+ "resolved": "https://registry.npmjs.org/@splinetool/runtime/-/runtime-0.9.526.tgz",
+ "integrity": "sha512-qznHbXA5aKwDbCgESAothCNm1IeEZcmNWG145p5aXj4w5uoqR1TZ9qkTHTKLTsUbHeitCwdhzmRqan1kxboLgQ==",
+ "peer": true,
+ "dependencies": {
+ "on-change": "^4.0.0",
+ "semver-compare": "^1.0.0"
+ }
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@stitches/react": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@stitches/react/-/react-1.2.8.tgz",
+ "integrity": "sha512-9g9dWI4gsSVe8bNLlb+lMkBYsnIKCZTmvqvDG+Avnn69XfmHZKiaMrx7cgTaddq7aTPPmXiTsbFcUy0xgI4+wA==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "react": ">= 16.3.0"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz",
+ "integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "enhanced-resolve": "5.21.6",
+ "jiti": "^2.7.0",
+ "lightningcss": "1.32.0",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.3.1"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz",
+ "integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.3.1",
+ "@tailwindcss/oxide-darwin-arm64": "4.3.1",
+ "@tailwindcss/oxide-darwin-x64": "4.3.1",
+ "@tailwindcss/oxide-freebsd-x64": "4.3.1",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.3.1",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.3.1",
+ "@tailwindcss/oxide-linux-x64-musl": "4.3.1",
+ "@tailwindcss/oxide-wasm32-wasi": "4.3.1",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.3.1"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz",
+ "integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz",
+ "integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz",
+ "integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz",
+ "integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz",
+ "integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz",
+ "integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz",
+ "integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz",
+ "integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz",
+ "integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz",
+ "integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.10.0",
+ "@emnapi/runtime": "^1.10.0",
+ "@emnapi/wasi-threads": "^1.2.1",
+ "@napi-rs/wasm-runtime": "^1.1.4",
+ "@tybys/wasm-util": "^0.10.2",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz",
+ "integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz",
+ "integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.1.tgz",
+ "integrity": "sha512-dNJuNbdEJT/SWRuXTYP1WSamelsz3ztkUsdtWQPjrexysrTpaEPM40P/71knXiXLYEojqPOEGitVLLpPMS5T6A==",
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.3.1",
+ "@tailwindcss/oxide": "4.3.1",
+ "postcss": "8.5.15",
+ "tailwindcss": "4.3.1"
+ }
+ },
+ "node_modules/@tsconfig/node18": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-1.0.3.tgz",
+ "integrity": "sha512-RbwvSJQsuN9TB04AQbGULYfOGE/RnSFk/FLQ5b0NmDf5Kx2q/lABZbHQPKCO1vZ6Fiwkplu+yb9pGdLy1iGseQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
+ "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/d3": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
+ "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-axis": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
+ "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-brush": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
+ "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-chord": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
+ "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-contour": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+ "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-dispatch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",
+ "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-drag": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+ "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
+ "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-fetch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
+ "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "node_modules/@types/d3-force": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
+ "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-format": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
+ "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
+ "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-polygon": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
+ "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-quadtree": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
+ "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-random": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
+ "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-selection": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+ "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
+ "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-time-format": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+ "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/d3-transition": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+ "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-zoom": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+ "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/js-cookie": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
+ "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/katex": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz",
+ "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdx": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz",
+ "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.20.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.0.tgz",
+ "integrity": "sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.13",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz",
+ "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^4.0.4"
+ }
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
+ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
+ "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
+ "license": "ISC"
+ },
+ "node_modules/@upsetjs/venn.js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz",
+ "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==",
+ "license": "MIT",
+ "peer": true,
+ "optionalDependencies": {
+ "d3-selection": "^3.0.0",
+ "d3-transition": "^3.0.1"
+ }
+ },
+ "node_modules/@use-gesture/core": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz",
+ "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@use-gesture/react": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz",
+ "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@use-gesture/core": "10.3.1"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz",
+ "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.1.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.1.9",
+ "@vitest/utils": "4.1.9",
+ "chai": "^6.2.2",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz",
+ "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz",
+ "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.1.9",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz",
+ "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.9",
+ "@vitest/utils": "4.1.9",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz",
+ "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz",
+ "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.9",
+ "convert-source-map": "^2.0.0",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agentkeepalive": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
+ "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "node_modules/ahooks": {
+ "version": "3.9.7",
+ "resolved": "https://registry.npmjs.org/ahooks/-/ahooks-3.9.7.tgz",
+ "integrity": "sha512-S0lvzhbdlhK36RFBkGv+RbOM/dbbweym+BIHM/bwwuWVSVN5TuVErHPMWo4w0t1NDYg5KPp2iEf7Y7E5LASYiw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.21.0",
+ "@types/js-cookie": "^3.0.6",
+ "dayjs": "^1.9.1",
+ "intersection-observer": "^0.12.0",
+ "js-cookie": "^3.0.5",
+ "lodash": "^4.17.21",
+ "react-fast-compare": "^3.2.2",
+ "resize-observer-polyfill": "^1.5.1",
+ "screenfull": "^5.0.0",
+ "tslib": "^2.4.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/antd": {
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/antd/-/antd-6.4.5.tgz",
+ "integrity": "sha512-xyAgX/sqF/CRS1G95oM4ql0+3TBG+tE58aRJqdUPVv4yMZcQrnnkA4cU7Uc5Rny2yK2TrusDVargHzzXUrlJ1g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@ant-design/colors": "^8.0.1",
+ "@ant-design/cssinjs": "^2.1.2",
+ "@ant-design/cssinjs-utils": "^2.1.2",
+ "@ant-design/fast-color": "^3.0.1",
+ "@ant-design/icons": "^6.2.5",
+ "@ant-design/react-slick": "~2.0.0",
+ "@babel/runtime": "^7.29.2",
+ "@rc-component/cascader": "~1.16.1",
+ "@rc-component/checkbox": "~2.0.0",
+ "@rc-component/collapse": "~1.2.0",
+ "@rc-component/color-picker": "~3.1.1",
+ "@rc-component/dialog": "~1.9.0",
+ "@rc-component/drawer": "~1.4.2",
+ "@rc-component/dropdown": "~1.0.2",
+ "@rc-component/form": "~1.8.5",
+ "@rc-component/image": "~1.9.0",
+ "@rc-component/input": "~1.3.1",
+ "@rc-component/input-number": "~1.6.2",
+ "@rc-component/mentions": "~1.9.0",
+ "@rc-component/menu": "~1.3.1",
+ "@rc-component/motion": "^1.3.3",
+ "@rc-component/mutate-observer": "^2.0.1",
+ "@rc-component/notification": "~2.0.7",
+ "@rc-component/pagination": "~1.3.0",
+ "@rc-component/picker": "~1.10.0",
+ "@rc-component/progress": "~1.0.2",
+ "@rc-component/qrcode": "~2.0.0",
+ "@rc-component/rate": "~1.0.1",
+ "@rc-component/resize-observer": "^1.1.2",
+ "@rc-component/segmented": "~1.3.0",
+ "@rc-component/select": "~1.7.1",
+ "@rc-component/slider": "~1.0.1",
+ "@rc-component/steps": "~1.2.2",
+ "@rc-component/switch": "~1.0.3",
+ "@rc-component/table": "~1.10.2",
+ "@rc-component/tabs": "~1.9.1",
+ "@rc-component/tooltip": "~1.4.0",
+ "@rc-component/tour": "~2.4.0",
+ "@rc-component/tree": "~1.3.2",
+ "@rc-component/tree-select": "~1.10.0",
+ "@rc-component/trigger": "^3.9.1",
+ "@rc-component/upload": "~1.1.1",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1",
+ "dayjs": "^1.11.11",
+ "scroll-into-view-if-needed": "^3.1.0",
+ "throttle-debounce": "^5.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ant-design"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/antd-style": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/antd-style/-/antd-style-4.1.0.tgz",
+ "integrity": "sha512-vnPBGg0OVlSz90KRYZhxd89aZiOImTiesF+9MQqN8jsLGZUQTjbP04X9jTdEfsztKUuMbBWg/RmB/wHTakbtMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/cssinjs": "^2.0.0",
+ "@babel/runtime": "^7.24.1",
+ "@emotion/cache": "^11.11.0",
+ "@emotion/css": "^11.11.2",
+ "@emotion/react": "^11.11.4",
+ "@emotion/serialize": "^1.1.3",
+ "@emotion/utils": "^1.2.1",
+ "use-merge-value": "^1.2.0"
+ },
+ "peerDependencies": {
+ "antd": ">=6.0.0",
+ "react": ">=18"
+ }
+ },
+ "node_modules/anynum": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz",
+ "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/array-timsort": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz",
+ "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/astring": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz",
+ "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
+ "license": "MIT",
+ "bin": {
+ "astring": "bin/astring"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/attr-accept": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz",
+ "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.38",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
+ "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/blake3-wasm": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
+ "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz",
+ "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^2.0.0",
+ "debug": "^4.4.3",
+ "http-errors": "^2.0.1",
+ "iconv-lite": "^0.7.2",
+ "on-finished": "^2.4.1",
+ "qs": "^6.15.2",
+ "raw-body": "^3.0.2",
+ "type-is": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/body-parser/node_modules/content-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
+ "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/body-parser/node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/bowser": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz",
+ "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001799",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+ "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chroma-js": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-3.2.0.tgz",
+ "integrity": "sha512-os/OippSlX1RlWWr+QDPcGUZs0uoqr32urfxESG9U93lhUfbnlyckte84Q8P1UQY/qth983AS1JONKmLS4T0nw==",
+ "license": "(BSD-3-Clause AND Apache-2.0)",
+ "peer": true
+ },
+ "node_modules/ci-info": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+ "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/cloudflare": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/cloudflare/-/cloudflare-4.5.0.tgz",
+ "integrity": "sha512-fPcbPKx4zF45jBvQ0z7PCdgejVAPBBCZxwqk1k7krQNfpM07Cfj97/Q6wBzvYqlWXx/zt1S9+m8vnfCe06umbQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/node": "^18.11.18",
+ "@types/node-fetch": "^2.6.4",
+ "abort-controller": "^3.0.0",
+ "agentkeepalive": "^4.2.1",
+ "form-data-encoder": "1.7.2",
+ "formdata-node": "^4.3.2",
+ "node-fetch": "^2.6.7"
+ }
+ },
+ "node_modules/cloudflare/node_modules/@types/node": {
+ "version": "18.19.130",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
+ "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/cloudflare/node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/collapse-white-space": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz",
+ "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/colord": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/comment-json": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.6.2.tgz",
+ "integrity": "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-timsort": "^1.0.3",
+ "esprima": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/compute-scroll-into-view": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
+ "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/content-disposition": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
+ "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/core-js": {
+ "version": "3.49.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
+ "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/cose-base": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
+ "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "layout-base": "^1.0.0"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cross-spawn/node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/cross-spawn/node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/cytoscape": {
+ "version": "3.34.0",
+ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.0.tgz",
+ "integrity": "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/cytoscape-cose-bilkent": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
+ "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "cose-base": "^1.0.0"
+ },
+ "peerDependencies": {
+ "cytoscape": "^3.2.0"
+ }
+ },
+ "node_modules/cytoscape-fcose": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
+ "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "cose-base": "^2.2.0"
+ },
+ "peerDependencies": {
+ "cytoscape": "^3.2.0"
+ }
+ },
+ "node_modules/cytoscape-fcose/node_modules/cose-base": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
+ "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "layout-base": "^2.0.0"
+ }
+ },
+ "node_modules/cytoscape-fcose/node_modules/layout-base": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
+ "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/d3": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
+ "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
+ "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-sankey": {
+ "version": "0.12.3",
+ "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
+ "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "1 - 2",
+ "d3-shape": "^1.2.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-array": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+ "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "internmap": "^1.0.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",
+ "license": "BSD-3-Clause",
+ "peer": true
+ },
+ "node_modules/d3-sankey/node_modules/d3-shape": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+ "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "d3-path": "1"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/internmap": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
+ "license": "ISC",
+ "peer": true
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/dagre-d3-es": {
+ "version": "7.0.14",
+ "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz",
+ "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "d3": "^7.9.0",
+ "lodash-es": "^4.17.21"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.21",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
+ "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/decode-uri-component": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
+ "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/delaunator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz",
+ "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/diff": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
+ "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dompurify": {
+ "version": "3.4.11",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz",
+ "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eciesjs": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.18.tgz",
+ "integrity": "sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ecies/ciphers": "^0.2.5",
+ "@noble/ciphers": "^1.3.0",
+ "@noble/curves": "^1.9.7",
+ "@noble/hashes": "^1.8.0"
+ },
+ "engines": {
+ "bun": ">=1",
+ "deno": ">=2",
+ "node": ">=16"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emoji-mart": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz",
+ "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.21.6",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz",
+ "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/enquirer": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
+ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-colors": "^4.1.1",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/error-stack-parser-es": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
+ "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
+ "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-toolkit": {
+ "version": "1.48.1",
+ "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.48.1.tgz",
+ "integrity": "sha512-wfnXlwd5I75eXRtdD2vuEs50xHHESECDsGD7yiQnfFVNoa5522NwXEbmgo98LfiukSQHs+mBM7/YG3qKJB9/mQ==",
+ "license": "MIT",
+ "workspaces": [
+ "docs",
+ "benchmarks"
+ ]
+ },
+ "node_modules/esast-util-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz",
+ "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/esast-util-from-js": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz",
+ "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "acorn": "^8.0.0",
+ "esast-util-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
+ "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.4",
+ "@esbuild/android-arm": "0.25.4",
+ "@esbuild/android-arm64": "0.25.4",
+ "@esbuild/android-x64": "0.25.4",
+ "@esbuild/darwin-arm64": "0.25.4",
+ "@esbuild/darwin-x64": "0.25.4",
+ "@esbuild/freebsd-arm64": "0.25.4",
+ "@esbuild/freebsd-x64": "0.25.4",
+ "@esbuild/linux-arm": "0.25.4",
+ "@esbuild/linux-arm64": "0.25.4",
+ "@esbuild/linux-ia32": "0.25.4",
+ "@esbuild/linux-loong64": "0.25.4",
+ "@esbuild/linux-mips64el": "0.25.4",
+ "@esbuild/linux-ppc64": "0.25.4",
+ "@esbuild/linux-riscv64": "0.25.4",
+ "@esbuild/linux-s390x": "0.25.4",
+ "@esbuild/linux-x64": "0.25.4",
+ "@esbuild/netbsd-arm64": "0.25.4",
+ "@esbuild/netbsd-x64": "0.25.4",
+ "@esbuild/openbsd-arm64": "0.25.4",
+ "@esbuild/openbsd-x64": "0.25.4",
+ "@esbuild/sunos-x64": "0.25.4",
+ "@esbuild/win32-arm64": "0.25.4",
+ "@esbuild/win32-ia32": "0.25.4",
+ "@esbuild/win32-x64": "0.25.4"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/estree-util-attach-comments": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz",
+ "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-build-jsx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz",
+ "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-walker": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-scope": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz",
+ "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-to-js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz",
+ "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "astring": "^1.8.0",
+ "source-map": "^0.7.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-visit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz",
+ "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
+ "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.1",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extend-shallow/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/fast-xml-builder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz",
+ "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "path-expression-matcher": "^1.5.0",
+ "xml-naming": "^0.1.0"
+ }
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz",
+ "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@nodable/entities": "^2.1.0",
+ "fast-xml-builder": "^1.1.7",
+ "path-expression-matcher": "^1.5.0",
+ "strnum": "^2.2.3"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fflate": {
+ "version": "0.4.8",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
+ "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==",
+ "license": "MIT"
+ },
+ "node_modules/file-selector": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.5.0.tgz",
+ "integrity": "sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/filter-obj": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
+ "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
+ "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "license": "MIT"
+ },
+ "node_modules/for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/foreground-child/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/form-data-encoder": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
+ "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/form-data/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/form-data/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/formdata-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
+ "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "1.0.0",
+ "web-streams-polyfill": "4.0.0-beta.3"
+ },
+ "engines": {
+ "node": ">= 12.20"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/framer-motion": {
+ "version": "12.40.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.40.0.tgz",
+ "integrity": "sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "motion-dom": "^12.40.0",
+ "motion-utils": "^12.39.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+ "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/giscus": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/giscus/-/giscus-1.6.0.tgz",
+ "integrity": "sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "lit": "^3.2.1"
+ }
+ },
+ "node_modules/glob": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-12.0.0.tgz",
+ "integrity": "sha512-5Qcll1z7IKgHr5g485ePDdHcNQY0k2dtv/bjYy0iuyGxQw2qSOiiXUXJ+AYQpg3HNoUMHqAruX478Jeev7UULw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "foreground-child": "^3.3.1",
+ "jackspeak": "^4.1.1",
+ "minimatch": "^10.1.1",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^2.0.0"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
+ },
+ "node_modules/gray-matter": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
+ "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-yaml": "^3.13.1",
+ "kind-of": "^6.0.2",
+ "section-matter": "^1.0.0",
+ "strip-bom-string": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/gzip-size": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
+ "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "duplexer": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hachure-fill": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
+ "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hast-util-from-dom": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz",
+ "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hastscript": "^9.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-html": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
+ "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "devlop": "^1.1.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "parse5": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-html-isomorphic": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz",
+ "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-from-dom": "^5.0.0",
+ "hast-util-from-html": "^2.0.0",
+ "unist-util-remove-position": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
+ "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "hastscript": "^9.0.0",
+ "property-information": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-location": "^5.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-element": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
+ "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-parse-selector": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+ "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-raw": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz",
+ "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "hast-util-to-parse5": "^8.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "parse5": "^7.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-estree": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz",
+ "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-attach-comments": "^3.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-html": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
+ "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "stringify-entities": "^4.0.0",
+ "zwitch": "^2.0.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-parse5": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz",
+ "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-text": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
+ "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "unist-util-find-after": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
+ "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^4.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/html-url-attributes": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.0.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/immer": {
+ "version": "11.1.8",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.8.tgz",
+ "integrity": "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inline-style-parser": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
+ "license": "MIT"
+ },
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "license": "ISC",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/intersection-observer": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz",
+ "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==",
+ "deprecated": "The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019.",
+ "license": "Apache-2.0",
+ "peer": true
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "license": "MIT"
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.2",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
+ "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-mobile": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz",
+ "integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==",
+ "license": "MIT"
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz",
+ "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/jackspeak": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz",
+ "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^9.0.0"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-cookie": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz",
+ "integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "license": "MIT"
+ },
+ "node_modules/json2mq": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
+ "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "string-convert": "^0.2.0"
+ }
+ },
+ "node_modules/katex": {
+ "version": "0.16.47",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
+ "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==",
+ "funding": [
+ "https://opencollective.com/katex",
+ "https://github.com/sponsors/katex"
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "commander": "^8.3.0"
+ },
+ "bin": {
+ "katex": "cli.js"
+ }
+ },
+ "node_modules/khroma": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
+ "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==",
+ "peer": true
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/layout-base": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
+ "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/leva": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/leva/-/leva-0.10.1.tgz",
+ "integrity": "sha512-BcjnfUX8jpmwZUz2L7AfBtF9vn4ggTH33hmeufDULbP3YgNZ/C+ss/oO3stbrqRQyaOmRwy70y7BGTGO81S3rA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@radix-ui/react-portal": "^1.1.4",
+ "@radix-ui/react-tooltip": "^1.1.8",
+ "@stitches/react": "^1.2.8",
+ "@use-gesture/react": "^10.2.5",
+ "colord": "^2.9.2",
+ "dequal": "^2.0.2",
+ "merge-value": "^1.0.0",
+ "react-colorful": "^5.5.1",
+ "react-dropzone": "^12.0.0",
+ "v8n": "^1.3.3",
+ "zustand": "^3.6.9"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
+ "node_modules/lit": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz",
+ "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "@lit/reactive-element": "^2.1.0",
+ "lit-element": "^4.2.0",
+ "lit-html": "^3.3.0"
+ }
+ },
+ "node_modules/lit-element": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz",
+ "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "@lit-labs/ssr-dom-shim": "^1.5.0",
+ "@lit/reactive-element": "^2.1.0",
+ "lit-html": "^3.3.0"
+ }
+ },
+ "node_modules/lit-html": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz",
+ "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "@types/trusted-types": "^2.0.2"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/lodash-es": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru_map": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz",
+ "integrity": "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/lucide-react": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.21.0.tgz",
+ "integrity": "sha512-reEZMXq8Qdd5jg5XYkQ5TR1fB/GiQ7ih4vcrthYDtgjSDwh0i6/YLiGjsWsIwgN49gpAnd4J2elSNzncMEEUUQ==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/markdown-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz",
+ "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/marked": {
+ "version": "17.0.6",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.6.tgz",
+ "integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==",
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
+ "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-math": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz",
+ "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.1.0",
+ "unist-util-remove-position": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz",
+ "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-newline-to-break": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz",
+ "integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-find-and-replace": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown-cjk-friendly": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown-cjk-friendly/-/mdast-util-to-markdown-cjk-friendly-1.0.0.tgz",
+ "integrity": "sha512-BoaAm8mlJ+LAYz0Qs532Y3ciTuQYgBUPZcSFbvC/ZKmEMAKgulw84YvQK1gI34t/vL2euSfuaWlqczkTBgamkw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "mdast-util-to-markdown": "^2.1.2",
+ "micromark-extension-cjk-friendly-util": "3.0.1",
+ "micromark-util-symbol": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/mdast": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/mdast": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/merge-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/merge-value/-/merge-value-1.0.0.tgz",
+ "integrity": "sha512-fJMmvat4NeKz63Uv9iHWcPDjCWcCkoiRoajRTEO8hlhUC6rwaHg0QCF9hBOTjZmm4JuglPckPSTtcuJL5kp0TQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "get-value": "^2.0.6",
+ "is-extendable": "^1.0.0",
+ "mixin-deep": "^1.2.0",
+ "set-value": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mermaid": {
+ "version": "11.15.0",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.15.0.tgz",
+ "integrity": "sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@braintree/sanitize-url": "^7.1.1",
+ "@iconify/utils": "^3.0.2",
+ "@mermaid-js/parser": "^1.1.1",
+ "@types/d3": "^7.4.3",
+ "@upsetjs/venn.js": "^2.0.0",
+ "cytoscape": "^3.33.1",
+ "cytoscape-cose-bilkent": "^4.1.0",
+ "cytoscape-fcose": "^2.2.0",
+ "d3": "^7.9.0",
+ "d3-sankey": "^0.12.3",
+ "dagre-d3-es": "7.0.14",
+ "dayjs": "^1.11.19",
+ "dompurify": "^3.3.1",
+ "es-toolkit": "^1.45.1",
+ "katex": "^0.16.25",
+ "khroma": "^2.1.0",
+ "marked": "^16.3.0",
+ "roughjs": "^4.6.6",
+ "stylis": "^4.3.6",
+ "ts-dedent": "^2.2.0",
+ "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0"
+ }
+ },
+ "node_modules/mermaid/node_modules/marked": {
+ "version": "16.4.2",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
+ "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-cjk-friendly": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-cjk-friendly/-/micromark-extension-cjk-friendly-2.0.1.tgz",
+ "integrity": "sha512-OkzoYVTL1ChbvQ8Cc1ayTIz7paFQz8iS9oIYmewncweUSwmWR+hkJF9spJ1lxB90XldJl26A1F4IkPOKS3bDXw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "devlop": "^1.1.0",
+ "micromark-extension-cjk-friendly-util": "3.0.1",
+ "micromark-util-chunked": "^2.0.1",
+ "micromark-util-resolve-all": "^2.0.1",
+ "micromark-util-symbol": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "micromark": "^4.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "micromark-util-types": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/micromark-extension-cjk-friendly-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-cjk-friendly-util/-/micromark-extension-cjk-friendly-util-3.0.1.tgz",
+ "integrity": "sha512-GcbXqTTHOsiZHyF753oIddP/J2eH8j9zpyQPhkof6B2JNxfEJabnQqxbCgzJNuNes0Y2jTNJ3LiYPSXr6eJA8w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.4.0",
+ "micromark-util-character": "^2.1.1",
+ "micromark-util-symbol": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "micromark-util-types": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-math": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz",
+ "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/katex": "^0.16.0",
+ "devlop": "^1.0.0",
+ "katex": "^0.16.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdx-expression": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz",
+ "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-jsx": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz",
+ "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdx-md": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz",
+ "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz",
+ "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.0.0",
+ "acorn-jsx": "^5.0.0",
+ "micromark-extension-mdx-expression": "^3.0.0",
+ "micromark-extension-mdx-jsx": "^3.0.0",
+ "micromark-extension-mdx-md": "^2.0.0",
+ "micromark-extension-mdxjs-esm": "^3.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs-esm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz",
+ "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-mdx-expression": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz",
+ "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-events-to-acorn": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz",
+ "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/miniflare": {
+ "version": "4.20260617.1",
+ "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260617.1.tgz",
+ "integrity": "sha512-Go3/gzStm99QHptsSgU+q1S+xDfLoRgwjJNY80kaTVi0ENhTyqKq+sc4xZiWBSbM7uUcJwmzm8+QFKtcYLJ9nw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cspotcode/source-map-support": "0.8.1",
+ "sharp": "0.34.5",
+ "undici": "7.28.0",
+ "workerd": "1.20260617.1",
+ "ws": "8.21.0",
+ "youch": "4.1.0-beta.10"
+ },
+ "bin": {
+ "miniflare": "bootstrap.js"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mnemonist": {
+ "version": "0.38.3",
+ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz",
+ "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "obliterator": "^1.6.1"
+ }
+ },
+ "node_modules/motion": {
+ "version": "12.40.0",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.40.0.tgz",
+ "integrity": "sha512-yjrHUrBFW6kQvjJwRsoiPSAhC5tRwRqNGJWmiJ4CrGnbKp0V88AdzkhBmDoqIsIPfarOe0Uddd37Xq43/gIocA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "framer-motion": "^12.40.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "12.40.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
+ "integrity": "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "motion-utils": "^12.39.0"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.39.0",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
+ "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.15",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
+ "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/next": {
+ "version": "16.2.7",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.2.7.tgz",
+ "integrity": "sha512-eMJxgjRzBaj3olkP4cBamHDXL79A8FC6u1GcsO1D1Tsx8bw/LLXUJCaoajVxtnhD3A1IJqIT8IcRJjgBIPJq4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "16.2.7",
+ "@swc/helpers": "0.5.15",
+ "baseline-browser-mapping": "^2.9.19",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "16.2.7",
+ "@next/swc-darwin-x64": "16.2.7",
+ "@next/swc-linux-arm64-gnu": "16.2.7",
+ "@next/swc-linux-arm64-musl": "16.2.7",
+ "@next/swc-linux-x64-gnu": "16.2.7",
+ "@next/swc-linux-x64-musl": "16.2.7",
+ "@next/swc-win32-arm64-msvc": "16.2.7",
+ "@next/swc-win32-x64-msvc": "16.2.7",
+ "sharp": "^0.34.5"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/numeral": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
+ "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-treeify": {
+ "version": "1.1.33",
+ "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz",
+ "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/obliterator": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz",
+ "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/obug": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz",
+ "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/on-change": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/on-change/-/on-change-4.0.2.tgz",
+ "integrity": "sha512-cMtCyuJmTx/bg2HCpHo3ZLeF7FZnBOapLqZHr2AlLeJ5Ul0Zu2mUJJz051Fdwu/Et2YW04ZD+TtU+gVy0ACNCA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/on-change?sponsor=1"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/oniguruma-parser": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz",
+ "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-to-es": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz",
+ "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==",
+ "license": "MIT",
+ "dependencies": {
+ "oniguruma-parser": "^0.12.2",
+ "regex": "^6.1.0",
+ "regex-recursion": "^6.0.2"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/package-manager-detector": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz",
+ "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-data-parser": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",
+ "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/path-expression-matcher": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.0.tgz",
+ "integrity": "sha512-e5y7RCLHKjemsgQ4eqGJtPyr10ILz25HO7flzxhTV8bgvd5yHx98DGtCAtbVW9f2TqnYI/gEVZd+vz7snrdPTw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "license": "MIT"
+ },
+ "node_modules/path-scurry": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "11.5.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
+ "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
+ "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/points-on-curve": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",
+ "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/points-on-path": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz",
+ "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "path-data-parser": "0.1.0",
+ "points-on-curve": "0.2.0"
+ }
+ },
+ "node_modules/polished": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz",
+ "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.17.8"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/posthog-js": {
+ "version": "1.391.7",
+ "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.391.7.tgz",
+ "integrity": "sha512-zjAJZeL+1PhJIfXVavgd5WY3VObDUEOMBwTM1tfDVPUDeT+8H7q7miPOpTLD40VQXErntfXYBq0flw3WdWyscw==",
+ "license": "SEE LICENSE IN LICENSE",
+ "dependencies": {
+ "@posthog/core": "^1.35.3",
+ "@posthog/types": "^1.390.2",
+ "core-js": "^3.38.1",
+ "dompurify": "^3.3.2",
+ "fflate": "^0.4.8",
+ "preact": "^10.29.2",
+ "query-selector-shadow-dom": "^1.0.1",
+ "web-vitals": "^5.3.0"
+ }
+ },
+ "node_modules/posthog-node": {
+ "version": "5.38.2",
+ "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.38.2.tgz",
+ "integrity": "sha512-eiKpU+vX4hVuHbO/EosvPHsmh2AVIdoVmWss/uUOs1t4b0ViCblw2o8OIFqHxKj3mYRnSOBlX0Dw3wBvcCaYpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@posthog/core": "^1.35.3"
+ },
+ "engines": {
+ "node": "^20.20.0 || >=22.22.0"
+ },
+ "peerDependencies": {
+ "rxjs": "^7.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rxjs": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/preact": {
+ "version": "10.29.2",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz",
+ "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/preact"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/property-information": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
+ "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
+ "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/query-selector-shadow-dom": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz",
+ "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==",
+ "license": "MIT"
+ },
+ "node_modules/query-string": {
+ "version": "9.4.0",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.4.0.tgz",
+ "integrity": "sha512-ivvWyHqU9K1Log4hJFhqVIIMoEi0nzmlRhvk2pPcTuQH/Y0K5iTTMxEx7R0PRHD2Z1hMVbWnjfsEWbIKIK+3IA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "decode-uri-component": "^0.4.1",
+ "filter-obj": "^5.1.0",
+ "split-on-first": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.7.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/raw-body/node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/rc-collapse": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-4.0.0.tgz",
+ "integrity": "sha512-SwoOByE39/3oIokDs/BnkqI+ltwirZbP8HZdq1/3SkPSBi7xDdvWHTp7cpNI9ullozkR6mwTWQi6/E/9huQVrA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.3.4",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-dialog": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz",
+ "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/portal": "^1.0.0-8",
+ "classnames": "^2.2.6",
+ "rc-motion": "^2.3.0",
+ "rc-util": "^5.21.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-dialog/node_modules/@rc-component/portal": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz",
+ "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-footer": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/rc-footer/-/rc-footer-0.6.8.tgz",
+ "integrity": "sha512-JBZ+xcb6kkex8XnBd4VHw1ZxjV6kmcwUumSHaIFdka2qzMCo7Klcy4sI6G0XtUpG/vtpislQCc+S9Bc+NLHYMg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/rc-image": {
+ "version": "7.12.0",
+ "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz",
+ "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "@rc-component/portal": "^1.0.2",
+ "classnames": "^2.2.6",
+ "rc-dialog": "~9.6.0",
+ "rc-motion": "^2.6.2",
+ "rc-util": "^5.34.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-image/node_modules/@rc-component/portal": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz",
+ "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-input": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz",
+ "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.18.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/rc-input-number": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz",
+ "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/mini-decimal": "^1.0.1",
+ "classnames": "^2.2.5",
+ "rc-input": "~1.8.0",
+ "rc-util": "^5.40.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-menu": {
+ "version": "9.16.1",
+ "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz",
+ "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "2.x",
+ "rc-motion": "^2.4.3",
+ "rc-overflow": "^1.3.1",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-menu/node_modules/@rc-component/portal": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz",
+ "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-menu/node_modules/@rc-component/trigger": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.3.1.tgz",
+ "integrity": "sha512-ORENF39PeXTzM+gQEshuk460Z8N4+6DkjpxlpE7Q3gYy1iBpLrx0FOJz3h62ryrJZ/3zCAUIkT1Pb/8hHWpb3A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.23.2",
+ "@rc-component/portal": "^1.1.0",
+ "classnames": "^2.3.2",
+ "rc-motion": "^2.0.0",
+ "rc-resize-observer": "^1.3.1",
+ "rc-util": "^5.44.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-motion": {
+ "version": "2.9.5",
+ "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz",
+ "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.44.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-overflow": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.5.0.tgz",
+ "integrity": "sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.37.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-resize-observer": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz",
+ "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.20.7",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.44.1",
+ "resize-observer-polyfill": "^1.5.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util": {
+ "version": "5.44.4",
+ "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz",
+ "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "react-is": "^18.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/re-resizable": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz",
+ "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-avatar-editor": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/react-avatar-editor/-/react-avatar-editor-15.1.0.tgz",
+ "integrity": "sha512-Zto7u9l6Wd5LPPtjeFJ+7uwoT4bs01OSgkN2kxD18lWl8IiZ0GY3nWCbKPx4qIU7Au1vENsMJm19rfVWHHayaQ==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "react": "^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react-colorful": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.7.0.tgz",
+ "integrity": "sha512-fuesYIemttah97XmsIHmz4OORDHiSFzyc9HMAIrCHJou2jaRQmL8cFJ76K4zQhhj8jzwOBlOi4BaGTjjOZCfTg==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
+ "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.7"
+ }
+ },
+ "node_modules/react-draggable": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.7.0.tgz",
+ "integrity": "sha512-kTpANmKWVnFXiZ76Ag2ZowiFStuBYnJ606PI1TbUsOg29/400/JNIxI9+CuenhiAqFuXWJffz6F4UI3R51kUug==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": ">= 16.3.0",
+ "react-dom": ">= 16.3.0"
+ }
+ },
+ "node_modules/react-dropzone": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.1.0.tgz",
+ "integrity": "sha512-iBYHA1rbopIvtzokEX4QubO6qk5IF/x3BtKGu74rF2JkQDXnwC4uO/lHKpaw4PJIV6iIAYOlwLv2FpiGyqHNog==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "attr-accept": "^2.2.2",
+ "file-selector": "^0.5.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">= 10.13"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8"
+ }
+ },
+ "node_modules/react-error-boundary": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.2.tgz",
+ "integrity": "sha512-3DpCr5HVdZ0caUjYE/kIHBEJN0mNP3ZCgf16c48uJ5TbWjorKVp+YG8W3XqlJ7vJAVNw6wNIImyPXmFydwmyng==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/react-hotkeys-hook": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-5.3.2.tgz",
+ "integrity": "sha512-DDDy9xK6mbTQ6aPlQvIl0dA/a90T/AWml4Rm21JXFDLlRHalIg4/Rv3equUQYs5xPTWq+oEl6RD7mi/nBpU3Uw==",
+ "license": "MIT",
+ "peer": true,
+ "workspaces": [
+ "packages/*"
+ ],
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/react-icons": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
+ "integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
+ "node_modules/react-markdown": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
+ "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "html-url-attributes": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "unified": "^11.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18",
+ "react": ">=18"
+ }
+ },
+ "node_modules/react-merge-refs": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-3.0.2.tgz",
+ "integrity": "sha512-MSZAfwFfdbEvwkKWP5EI5chuLYnNUxNS7vyS0i1Jp+wtd8J4Ga2ddzhaE68aMol2Z4vCnRM/oGOo1a3V75UPlw==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-rnd": {
+ "version": "10.5.3",
+ "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.5.3.tgz",
+ "integrity": "sha512-s/sIT3pGZnQ+57egijkTp9mizjIWrJz68Pq6yd+F/wniFY3IriML18dUXnQe/HP9uMiJ+9MAp44hljG99fZu6Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "re-resizable": "^6.11.2",
+ "react-draggable": "^4.5.0",
+ "tslib": "2.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0",
+ "react-dom": ">=16.3.0"
+ }
+ },
+ "node_modules/react-rnd/node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "license": "0BSD",
+ "peer": true
+ },
+ "node_modules/react-zoom-pan-pinch": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.7.0.tgz",
+ "integrity": "sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8",
+ "npm": ">=5"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/recma-build-jsx": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz",
+ "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-util-build-jsx": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/recma-jsx": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz",
+ "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn-jsx": "^5.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "recma-parse": "^1.0.0",
+ "recma-stringify": "^1.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/recma-parse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz",
+ "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "esast-util-from-js": "^2.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/recma-stringify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz",
+ "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz",
+ "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-recursion": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz",
+ "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-utilities": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz",
+ "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
+ "license": "MIT"
+ },
+ "node_modules/rehype-github-alerts": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/rehype-github-alerts/-/rehype-github-alerts-4.2.0.tgz",
+ "integrity": "sha512-6di6kEu9WUHKLKrkKG2xX6AOuaCMGghg0Wq7MEuM/jBYUPVIq6PJpMe00dxMfU+/YSBtDXhffpDimgDi+BObIQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@primer/octicons": "^19.20.0",
+ "hast-util-from-html": "^2.0.3",
+ "hast-util-is-element": "^3.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/chrisweb"
+ }
+ },
+ "node_modules/rehype-katex": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz",
+ "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/katex": "^0.16.0",
+ "hast-util-from-html-isomorphic": "^2.0.0",
+ "hast-util-to-text": "^4.0.0",
+ "katex": "^0.16.0",
+ "unist-util-visit-parents": "^6.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-raw": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz",
+ "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-raw": "^9.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-recma": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz",
+ "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "hast-util-to-estree": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-breaks": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz",
+ "integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-newline-to-break": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-cjk-friendly": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/remark-cjk-friendly/-/remark-cjk-friendly-2.3.1.tgz",
+ "integrity": "sha512-f+pKZRxCRwNEGFBKNRAZAqU91GIK1SAo3ZyFHWRUgC9zcxRR0BXKd6YwqgSsxtW0rNpUDtONj7H5nje2WL3fcA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "mdast-util-to-markdown-cjk-friendly": "1.0.0",
+ "micromark-extension-cjk-friendly": "2.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/mdast": "^4.0.0",
+ "unified": "^11.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/mdast": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-github": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/remark-github/-/remark-github-12.0.0.tgz",
+ "integrity": "sha512-ByefQKFN184LeiGRCabfl7zUJsdlMYWEhiLX1gpmQ11yFg6xSuOTW7LVCv0oc1x+YvUMJW23NU36sJX2RWGgvg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "to-vfile": "^8.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-math": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz",
+ "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-math": "^3.0.0",
+ "micromark-extension-math": "^3.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-mdx": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz",
+ "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-mdx": "^3.0.0",
+ "micromark-extension-mdxjs": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-rehype": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remend": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/remend/-/remend-1.3.0.tgz",
+ "integrity": "sha512-iIhggPkhW3hFImKtB10w0dz4EZbs28mV/dmbcYVonWEJ6UGHHpP+bFZnTh6GNWJONg5m+U56JrL+8IxZRdgWjw==",
+ "license": "Apache-2.0",
+ "peer": true
+ },
+ "node_modules/reselect": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz",
+ "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/resolve": {
+ "version": "1.22.12",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/robust-predicates": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",
+ "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",
+ "license": "Unlicense",
+ "peer": true
+ },
+ "node_modules/rolldown": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
+ "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.133.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.3",
+ "@rolldown/binding-darwin-arm64": "1.0.3",
+ "@rolldown/binding-darwin-x64": "1.0.3",
+ "@rolldown/binding-freebsd-x64": "1.0.3",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.3",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.3",
+ "@rolldown/binding-linux-arm64-musl": "1.0.3",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.3",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.3",
+ "@rolldown/binding-linux-x64-gnu": "1.0.3",
+ "@rolldown/binding-linux-x64-musl": "1.0.3",
+ "@rolldown/binding-openharmony-arm64": "1.0.3",
+ "@rolldown/binding-wasm32-wasi": "1.0.3",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.3",
+ "@rolldown/binding-win32-x64-msvc": "1.0.3"
+ }
+ },
+ "node_modules/roughjs": {
+ "version": "4.6.6",
+ "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",
+ "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "hachure-fill": "^0.5.2",
+ "path-data-parser": "^0.1.0",
+ "points-on-curve": "^0.2.0",
+ "points-on-path": "^0.2.1"
+ }
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/router/node_modules/path-to-regexp": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
+ "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "license": "BSD-3-Clause",
+ "peer": true
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/screenfull": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz",
+ "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/scroll-into-view-if-needed": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
+ "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "compute-scroll-into-view": "^3.0.2"
+ }
+ },
+ "node_modules/section-matter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
+ "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
+ "license": "MIT",
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "devOptional": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver-compare": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
+ "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.1",
+ "mime-types": "^3.0.2",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
+ "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/server-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
+ "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==",
+ "license": "MIT"
+ },
+ "node_modules/set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "devOptional": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shiki": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.2.0.tgz",
+ "integrity": "sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "4.2.0",
+ "@shikijs/engine-javascript": "4.2.0",
+ "@shikijs/engine-oniguruma": "4.2.0",
+ "@shikijs/langs": "4.2.0",
+ "@shikijs/themes": "4.2.0",
+ "@shikijs/types": "4.2.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/shiki-stream": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/shiki-stream/-/shiki-stream-0.1.5.tgz",
+ "integrity": "sha512-DzkqVlqf02Tp4zTFNgJp+3rOG2RkuoONBq+Pm2sHslAlJ5M0QbR1devn4dr9SgcBTrtHTf6Rqyj3wVJi0g16Bw==",
+ "deprecated": "shiki-stream is now @shikijs/stream, please migrate by renaming the package",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@shikijs/stream": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "react": "^19.0.0",
+ "solid-js": "^1.9.0",
+ "vue": "^3.2.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "solid-js": {
+ "optional": true
+ },
+ "vue": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/source-map": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+ "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/split-on-first": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz",
+ "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split-string/node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/std-env": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
+ "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-convert": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
+ "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
+ "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz",
+ "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "anynum": "^1.0.1"
+ }
+ },
+ "node_modules/style-to-js": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "style-to-object": "1.0.14"
+ }
+ },
+ "node_modules/style-to-object": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.7"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",
+ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",
+ "license": "MIT"
+ },
+ "node_modules/supports-color": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/swr": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/swr/-/swr-2.4.1.tgz",
+ "integrity": "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "dequal": "^2.0.3",
+ "use-sync-external-store": "^1.6.0"
+ },
+ "peerDependencies": {
+ "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/tabbable": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz",
+ "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/tailwind-merge": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz",
+ "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz",
+ "integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==",
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.16.9",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.9.tgz",
+ "integrity": "sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/throttle-debounce": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+ "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12.22"
+ }
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
+ "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/to-vfile": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-8.0.0.tgz",
+ "integrity": "sha512-IcmH1xB5576MJc9qcfEC/m/nQCFt3fzMHz45sSlgJyTWjRbKW1HAkJpuf3DgE57YzIlZcwcBZA5ENQbBo4aLkg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/ts-dedent": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz",
+ "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=6.10"
+ }
+ },
+ "node_modules/ts-md5": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-2.0.1.tgz",
+ "integrity": "sha512-yF35FCoEOFBzOclSkMNEUbFQZuv89KEQ+5Xz03HrMSGUGB1+r+El+JiGOFwsP4p9RFNzwlrydYoTLvPOuICl9w==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/ts-tqdm": {
+ "version": "0.8.6",
+ "resolved": "https://registry.npmjs.org/ts-tqdm/-/ts-tqdm-0.8.6.tgz",
+ "integrity": "sha512-3X3M1PZcHtgQbnwizL+xU8CAgbYbeLHrrDwL9xxcZZrV5J+e7loJm1XrXozHjSkl44J0Zg0SgA8rXbh83kCkcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/type-is": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz",
+ "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^2.0.0",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/type-is/node_modules/content-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
+ "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
+ "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.18.1"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unenv": {
+ "version": "2.0.0-rc.24",
+ "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz",
+ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pathe": "^2.0.3"
+ }
+ },
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-find-after": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz",
+ "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz",
+ "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-remove-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz",
+ "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/url-join": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
+ "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/urlpattern-polyfill": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz",
+ "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/use-merge-value": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-merge-value/-/use-merge-value-1.2.0.tgz",
+ "integrity": "sha512-DXgG0kkgJN45TcyoXL49vJnn55LehnrmoHc7MbKi+QDBvr8dsesqws8UlyIWGHMR+JXgxc1nvY+jDGMlycsUcw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">= 16.x"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz",
+ "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "uuid": "dist-node/bin/uuid"
+ }
+ },
+ "node_modules/v8n": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/v8n/-/v8n-1.5.1.tgz",
+ "integrity": "sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-location": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+ "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/virtua": {
+ "version": "0.49.1",
+ "resolved": "https://registry.npmjs.org/virtua/-/virtua-0.49.1.tgz",
+ "integrity": "sha512-6f79msqg3jzNFdqJiS0FSzhRN1EHlDhR7EvW7emp6z5qQ22VdsReiDHflkpMEMhoAyUuYr69nwT0aagiM7NrUg==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "react": ">=16.14.0",
+ "react-dom": ">=16.14.0",
+ "solid-js": ">=1.0",
+ "svelte": ">=5.0",
+ "vue": ">=3.2"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "solid-js": {
+ "optional": true
+ },
+ "svelte": {
+ "optional": true
+ },
+ "vue": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz",
+ "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.1.9",
+ "@vitest/mocker": "4.1.9",
+ "@vitest/pretty-format": "4.1.9",
+ "@vitest/runner": "4.1.9",
+ "@vitest/snapshot": "4.1.9",
+ "@vitest/spy": "4.1.9",
+ "@vitest/utils": "4.1.9",
+ "es-module-lexer": "^2.0.0",
+ "expect-type": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^4.0.0-rc.1",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.1.0",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.1.9",
+ "@vitest/browser-preview": "4.1.9",
+ "@vitest/browser-webdriverio": "4.1.9",
+ "@vitest/coverage-istanbul": "4.1.9",
+ "@vitest/coverage-v8": "4.1.9",
+ "@vitest/ui": "4.1.9",
+ "happy-dom": "*",
+ "jsdom": "*",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ },
+ "vite": {
+ "optional": false
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/@vitest/mocker": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz",
+ "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.1.9",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/vite": {
+ "version": "8.0.16",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
+ "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.15",
+ "rolldown": "1.0.3",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.18",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/web": {
+ "resolved": "web",
+ "link": true
+ },
+ "node_modules/web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "4.0.0-beta.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
+ "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/web-vitals": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.3.0.tgz",
+ "integrity": "sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+ "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^3.1.1"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/workerd": {
+ "version": "1.20260617.1",
+ "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260617.1.tgz",
+ "integrity": "sha512-Re5pl6pdowt3ZmWUzGlOuB7jbRIIPetgKalmo4cYmucQnVhpo7/3e4MfpekbhLi2EhZZz5EY9NWRu8zFzuEZew==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "workerd": "bin/workerd"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "@cloudflare/workerd-darwin-64": "1.20260617.1",
+ "@cloudflare/workerd-darwin-arm64": "1.20260617.1",
+ "@cloudflare/workerd-linux-64": "1.20260617.1",
+ "@cloudflare/workerd-linux-arm64": "1.20260617.1",
+ "@cloudflare/workerd-windows-64": "1.20260617.1"
+ }
+ },
+ "node_modules/wrangler": {
+ "version": "4.103.0",
+ "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.103.0.tgz",
+ "integrity": "sha512-3Lv1P5t2xcSEkSTKtG+Lz+3JFryuU7YPLkaCUj7gNe+CJsjZJLtUwqsh1x595QBxkIbCE0GAvDx2DCJUU4+oqw==",
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "dependencies": {
+ "@cloudflare/kv-asset-handler": "0.5.0",
+ "@cloudflare/unenv-preset": "2.16.1",
+ "blake3-wasm": "2.1.5",
+ "esbuild": "0.28.1",
+ "miniflare": "4.20260617.1",
+ "path-to-regexp": "6.3.0",
+ "unenv": "2.0.0-rc.24",
+ "workerd": "1.20260617.1"
+ },
+ "bin": {
+ "cf-wrangler": "bin/cf-wrangler.js",
+ "wrangler": "bin/wrangler.js",
+ "wrangler2": "bin/wrangler.js"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.3"
+ },
+ "peerDependencies": {
+ "@cloudflare/workers-types": "^4.20260617.1"
+ },
+ "peerDependenciesMeta": {
+ "@cloudflare/workers-types": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
+ "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/android-arm": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
+ "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/android-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
+ "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/android-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
+ "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
+ "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
+ "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-arm": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
+ "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
+ "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
+ "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
+ "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
+ "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
+ "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
+ "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
+ "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/linux-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
+ "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
+ "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
+ "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
+ "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/@esbuild/win32-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
+ "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/wrangler/node_modules/esbuild": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
+ "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.1",
+ "@esbuild/android-arm": "0.28.1",
+ "@esbuild/android-arm64": "0.28.1",
+ "@esbuild/android-x64": "0.28.1",
+ "@esbuild/darwin-arm64": "0.28.1",
+ "@esbuild/darwin-x64": "0.28.1",
+ "@esbuild/freebsd-arm64": "0.28.1",
+ "@esbuild/freebsd-x64": "0.28.1",
+ "@esbuild/linux-arm": "0.28.1",
+ "@esbuild/linux-arm64": "0.28.1",
+ "@esbuild/linux-ia32": "0.28.1",
+ "@esbuild/linux-loong64": "0.28.1",
+ "@esbuild/linux-mips64el": "0.28.1",
+ "@esbuild/linux-ppc64": "0.28.1",
+ "@esbuild/linux-riscv64": "0.28.1",
+ "@esbuild/linux-s390x": "0.28.1",
+ "@esbuild/linux-x64": "0.28.1",
+ "@esbuild/netbsd-arm64": "0.28.1",
+ "@esbuild/netbsd-x64": "0.28.1",
+ "@esbuild/openbsd-arm64": "0.28.1",
+ "@esbuild/openbsd-x64": "0.28.1",
+ "@esbuild/openharmony-arm64": "0.28.1",
+ "@esbuild/sunos-x64": "0.28.1",
+ "@esbuild/win32-arm64": "0.28.1",
+ "@esbuild/win32-ia32": "0.28.1",
+ "@esbuild/win32-x64": "0.28.1"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ws": {
+ "version": "8.21.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
+ "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-naming": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
+ "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
+ "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/youch": {
+ "version": "4.1.0-beta.10",
+ "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz",
+ "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@poppinss/colors": "^4.1.5",
+ "@poppinss/dumper": "^0.6.4",
+ "@speed-highlight/core": "^1.2.7",
+ "cookie": "^1.0.2",
+ "youch-core": "^0.3.3"
+ }
+ },
+ "node_modules/youch-core": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz",
+ "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@poppinss/exception": "^1.2.2",
+ "error-stack-parser-es": "^1.0.5"
+ }
+ },
+ "node_modules/zustand": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
+ "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "web": {
+ "version": "0.0.1",
+ "dependencies": {
+ "@lobehub/icons": "^5.10.0",
+ "@mdx-js/mdx": "^3.1.1",
+ "@posthog/next": "^0.4.61",
+ "@radix-ui/react-slot": "^1.2.4",
+ "@tailwindcss/postcss": "^4.2.2",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "gray-matter": "^4.0.3",
+ "lucide-react": "^1.0.1",
+ "next": "16.2.7",
+ "postcss": "^8.5.8",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "react-icons": "^5.6.0",
+ "remark-gfm": "^4.0.1",
+ "shiki": "^4.0.2",
+ "tailwind-merge": "^3.5.0",
+ "tailwindcss": "^4.2.2"
+ },
+ "devDependencies": {
+ "@opennextjs/cloudflare": "^1.19.10",
+ "@types/node": "^22.19.3",
+ "@types/react": "^19.2.0",
+ "@types/react-dom": "^19.2.0",
+ "typescript": "^5.9.3",
+ "vitest": "^4.1.0",
+ "wrangler": "^4.91.0"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..7da5e6bf
--- /dev/null
+++ b/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "agentrelay.com",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "workspaces": [
+ "web"
+ ],
+ "scripts": {
+ "dev": "npm --workspace web run dev",
+ "build": "npm --workspace web run build",
+ "test": "npm --workspace web run test",
+ "preview": "npm --workspace web run preview",
+ "deploy": "npm --workspace web run deploy"
+ }
+}
diff --git a/web/app/DurableDeliveryTimeline.tsx b/web/app/DurableDeliveryTimeline.tsx
new file mode 100644
index 00000000..1a95046f
--- /dev/null
+++ b/web/app/DurableDeliveryTimeline.tsx
@@ -0,0 +1,172 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+import { AgentToolLogo, type AgentTool } from '../components/AgentToolLogos';
+import s from './landing.module.css';
+
+type AgentTimelineItem = {
+ agent: string;
+ kind: 'agent';
+ provider: AgentTool;
+ status: 'offline' | 'online';
+};
+
+type MessageTimelineItem = {
+ event: 'message.delivered' | 'message.received';
+ kind: 'event';
+ latency: string;
+};
+
+type TimelineTemplate = AgentTimelineItem | MessageTimelineItem;
+type TimelineItem = TimelineTemplate & { id: number };
+
+const VISIBLE_TIMELINE_ITEMS = 12;
+const INITIAL_TIMELINE_CURSOR = 2;
+const FIRST_CATCH_UP_COMPLETE_INDEX = 7;
+const FIRST_CATCH_UP_HOLD_MS = 5200;
+const CATCH_UP_HOLD_MS = 3400;
+const RECEIVED_STEP_DELAYS_MS = [1900, 2600, 2200, 2800];
+const DELIVERED_STEP_DELAYS_MS = [1300, 1700, 1200, 1500];
+const RECOVERY_COMPLETE_SEQUENCE_INDICES = [7, 13, 24, 31, 37, 41];
+
+const TIMELINE_SEQUENCE = [
+ { agent: 'Claude', kind: 'agent', provider: 'claude', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '24ms' },
+ { agent: 'Codex', kind: 'agent', provider: 'codex', status: 'offline' },
+ { event: 'message.received', kind: 'event', latency: '+1.4s' },
+ { event: 'message.received', kind: 'event', latency: '+1.9s' },
+ { agent: 'Codex', kind: 'agent', provider: 'codex', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '31ms' },
+ { event: 'message.delivered', kind: 'event', latency: '37ms' },
+ { agent: 'Claude', kind: 'agent', provider: 'claude', status: 'offline' },
+ { event: 'message.received', kind: 'event', latency: '+2.2s' },
+ { event: 'message.received', kind: 'event', latency: '+2.7s' },
+ { agent: 'Claude', kind: 'agent', provider: 'claude', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '28ms' },
+ { event: 'message.delivered', kind: 'event', latency: '43ms' },
+ { agent: 'Codex', kind: 'agent', provider: 'codex', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '36ms' },
+ { agent: 'Claude', kind: 'agent', provider: 'claude', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '41ms' },
+ { agent: 'Codex', kind: 'agent', provider: 'codex', status: 'offline' },
+ { event: 'message.received', kind: 'event', latency: '+2.8s' },
+ { event: 'message.received', kind: 'event', latency: '+3.2s' },
+ { event: 'message.received', kind: 'event', latency: '+3.6s' },
+ { agent: 'Codex', kind: 'agent', provider: 'codex', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '33ms' },
+ { event: 'message.delivered', kind: 'event', latency: '52ms' },
+ { agent: 'Claude', kind: 'agent', provider: 'claude', status: 'offline' },
+ { event: 'message.received', kind: 'event', latency: '+1.6s' },
+ { event: 'message.received', kind: 'event', latency: '+2.1s' },
+ { agent: 'Claude', kind: 'agent', provider: 'claude', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '22ms' },
+ { event: 'message.delivered', kind: 'event', latency: '39ms' },
+ { event: 'message.delivered', kind: 'event', latency: '45ms' },
+ { agent: 'Codex', kind: 'agent', provider: 'codex', status: 'offline' },
+ { event: 'message.received', kind: 'event', latency: '+1.8s' },
+ { event: 'message.received', kind: 'event', latency: '+2.4s' },
+ { agent: 'Codex', kind: 'agent', provider: 'codex', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '29ms' },
+ { event: 'message.delivered', kind: 'event', latency: '34ms' },
+ { agent: 'Claude', kind: 'agent', provider: 'claude', status: 'offline' },
+ { event: 'message.received', kind: 'event', latency: '+3.1s' },
+ { agent: 'Claude', kind: 'agent', provider: 'claude', status: 'online' },
+ { event: 'message.delivered', kind: 'event', latency: '47ms' },
+] satisfies TimelineTemplate[];
+
+const DELIVERED_DELAY_CLASSES = [s.durableDeliveredOne, s.durableDeliveredTwo, s.durableDeliveredThree];
+
+function normalizeTimelineIndex(index: number) {
+ return ((index % TIMELINE_SEQUENCE.length) + TIMELINE_SEQUENCE.length) % TIMELINE_SEQUENCE.length;
+}
+
+function getTimelineItem(index: number): TimelineItem {
+ const template = TIMELINE_SEQUENCE[normalizeTimelineIndex(index)];
+
+ return { ...template, id: index };
+}
+
+function getTimelineDelay(index: number) {
+ const sequenceIndex = normalizeTimelineIndex(index);
+ const item = TIMELINE_SEQUENCE[sequenceIndex];
+
+ if (index === FIRST_CATCH_UP_COMPLETE_INDEX) {
+ return FIRST_CATCH_UP_HOLD_MS;
+ }
+
+ if (RECOVERY_COMPLETE_SEQUENCE_INDICES.includes(sequenceIndex)) {
+ return CATCH_UP_HOLD_MS;
+ }
+
+ if (item.kind === 'agent') {
+ return item.status === 'offline' ? 2300 : 1450;
+ }
+
+ if (item.event === 'message.received') {
+ return RECEIVED_STEP_DELAYS_MS[sequenceIndex % RECEIVED_STEP_DELAYS_MS.length];
+ }
+
+ return DELIVERED_STEP_DELAYS_MS[sequenceIndex % DELIVERED_STEP_DELAYS_MS.length];
+}
+
+export function DurableDeliveryTimeline() {
+ const [cursor, setCursor] = useState(INITIAL_TIMELINE_CURSOR);
+ const items = Array.from({ length: VISIBLE_TIMELINE_ITEMS }, (_, offset) =>
+ getTimelineItem(cursor - VISIBLE_TIMELINE_ITEMS + 1 + offset)
+ );
+
+ useEffect(() => {
+ const timeoutId = window.setTimeout(() => {
+ setCursor((current) => current + 1);
+ }, getTimelineDelay(cursor));
+
+ return () => window.clearTimeout(timeoutId);
+ }, [cursor]);
+
+ return (
+
+
+ {items.map((item) => {
+ if (item.kind === 'agent') {
+ return (
+
+
+
{item.agent}
+
+
+ {item.status}
+
+
+ );
+ }
+
+ if (item.event === 'message.delivered') {
+ return (
+
+ ✓
+ message.delivered
+ {item.latency}
+
+ );
+ }
+
+ return (
+
+
+ message.received
+ {item.latency}
+
+ );
+ })}
+
+ );
+}
diff --git a/web/app/PostHogPageView.tsx b/web/app/PostHogPageView.tsx
new file mode 100644
index 00000000..f50be9b2
--- /dev/null
+++ b/web/app/PostHogPageView.tsx
@@ -0,0 +1,11 @@
+'use client';
+
+import { PostHogPageView as PostHogPageViewComponent } from '@posthog/next';
+import { usePathname } from 'next/navigation';
+
+import { getWebsiteAnalyticsPage } from '../lib/site-analytics';
+
+export function WebsitePostHogPageView() {
+ const pathname = usePathname();
+ return getWebsiteAnalyticsPage(pathname) ? : null;
+}
diff --git a/web/app/RealtimeEventFeed.tsx b/web/app/RealtimeEventFeed.tsx
new file mode 100644
index 00000000..dff8c3e4
--- /dev/null
+++ b/web/app/RealtimeEventFeed.tsx
@@ -0,0 +1,197 @@
+'use client';
+
+import { useEffect, useRef, useState } from 'react';
+
+import { AgentToolLogo, type AgentTool } from '../components/AgentToolLogos';
+import s from './landing.module.css';
+
+const REALTIME_EVENT_ACTIVITY = [
+ { name: 'session.started', agent: 'Planner', action: 'joined #dev', provider: 'claude', signal: 'live' },
+ {
+ name: 'message.sent',
+ agent: 'Builder',
+ action: 'sent a channel update',
+ provider: 'codex',
+ signal: 'live',
+ },
+ {
+ name: 'reaction.added',
+ agent: 'Reviewer',
+ action: 'reacted with :+1:',
+ provider: 'claude',
+ signal: 'live',
+ },
+ {
+ name: 'thread.started',
+ agent: 'Planner',
+ action: 'started a review thread',
+ provider: 'claude',
+ signal: 'live',
+ },
+ { name: 'status.idle', agent: 'Builder', action: 'went idle', provider: 'codex', signal: 'idle' },
+ { name: 'action.invoked', agent: 'Ops', action: 'called spawn', provider: 'openclaw', signal: 'live' },
+ { name: 'message.sent', agent: 'Designer', action: 'sent a DM', provider: 'codex', signal: 'live' },
+ {
+ name: 'status.offline',
+ agent: 'Reviewer',
+ action: 'disconnected',
+ provider: 'claude',
+ signal: 'disconnected',
+ },
+ {
+ name: 'channel.left',
+ agent: 'Ops',
+ action: 'left #deploy',
+ provider: 'openclaw',
+ signal: 'disconnected',
+ },
+ {
+ name: 'action.invoked',
+ agent: 'Planner',
+ action: 'called ui.update',
+ provider: 'claude',
+ signal: 'live',
+ },
+ { name: 'session.started', agent: 'QA', action: 'joined #release', provider: 'codex', signal: 'live' },
+ { name: 'status.idle', agent: 'Ops', action: 'went idle', provider: 'openclaw', signal: 'idle' },
+ {
+ name: 'message.sent',
+ agent: 'Reviewer',
+ action: 'sent thread reply',
+ provider: 'claude',
+ signal: 'live',
+ },
+ { name: 'reaction.added', agent: 'QA', action: 'reacted with :eyes:', provider: 'codex', signal: 'live' },
+] satisfies Array<{
+ action: string;
+ agent: string;
+ name: string;
+ provider: AgentTool | 'openclaw';
+ signal: 'disconnected' | 'idle' | 'live';
+}>;
+
+const EVENT_LATENCIES = ['12ms', '18ms', '21ms', '24ms', '31ms', '37ms', '42ms', '49ms'];
+const BATCH_PATTERN = [1, 2, 1, 1, 2, 1, 3, 1, 2, 1, 1, 2];
+const DELAY_PATTERN = [1700, 2600, 1300, 2200, 3100, 1500, 2400, 1800, 2900, 1200, 2500, 1600];
+const INITIAL_EVENT_COUNT = 4;
+const MAX_VISIBLE_EVENTS = 5;
+
+type FeedEvent = {
+ action: string;
+ agent: string;
+ id: number;
+ name: string;
+ provider: AgentTool | 'openclaw';
+ signal: 'disconnected' | 'idle' | 'live';
+ latency: string;
+};
+
+function OpenClawLogo({ className }: { className: string }) {
+ return (
+
+
+
+
+
+
+ );
+}
+
+function HarnessLogo({ provider }: { provider: FeedEvent['provider'] }) {
+ if (provider === 'openclaw') return ;
+
+ return ;
+}
+
+function makeFeedEvent(sequence: number, id: number): FeedEvent {
+ const activity = REALTIME_EVENT_ACTIVITY[sequence % REALTIME_EVENT_ACTIVITY.length];
+
+ return {
+ action: activity.action,
+ agent: activity.agent,
+ id,
+ name: activity.name,
+ provider: activity.provider,
+ signal: activity.signal,
+ latency: EVENT_LATENCIES[sequence % EVENT_LATENCIES.length],
+ };
+}
+
+function initialEvents() {
+ return Array.from({ length: INITIAL_EVENT_COUNT }, (_, index) => makeFeedEvent(index, index));
+}
+
+export function RealtimeEventFeed() {
+ const [events, setEvents] = useState(initialEvents);
+ const sequenceRef = useRef(INITIAL_EVENT_COUNT);
+ const idRef = useRef(INITIAL_EVENT_COUNT);
+ const stepRef = useRef(0);
+
+ useEffect(() => {
+ let active = true;
+ let timeoutId: number | undefined;
+
+ const scheduleNext = () => {
+ const delay = DELAY_PATTERN[stepRef.current % DELAY_PATTERN.length];
+
+ timeoutId = window.setTimeout(() => {
+ if (!active) return;
+
+ const batchSize = BATCH_PATTERN[stepRef.current % BATCH_PATTERN.length];
+ const additions = Array.from({ length: batchSize }, () => {
+ const item = makeFeedEvent(sequenceRef.current, idRef.current);
+ sequenceRef.current += 1;
+ idRef.current += 1;
+ return item;
+ });
+
+ setEvents((current) => [...current, ...additions].slice(-MAX_VISIBLE_EVENTS));
+ stepRef.current += 1;
+ scheduleNext();
+ }, delay);
+ };
+
+ scheduleNext();
+
+ return () => {
+ active = false;
+ window.clearTimeout(timeoutId);
+ };
+ }, []);
+
+ return (
+
+
+ {events.map((event) => (
+
+
+
+ {event.name}
+
+
+ {event.agent}
+ {event.action}
+
+
+
{event.latency}
+
+ ))}
+
+
+ );
+}
diff --git a/web/app/SearchPreviewAnimation.tsx b/web/app/SearchPreviewAnimation.tsx
new file mode 100644
index 00000000..c54f378f
--- /dev/null
+++ b/web/app/SearchPreviewAnimation.tsx
@@ -0,0 +1,112 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+import s from './landing.module.css';
+
+const SEARCH_QUERY = 'handoff token';
+
+const RESULTS = [
+ {
+ label: '#dev',
+ time: '4m ago',
+ before: 'Builder shared the ',
+ match: 'handoff token',
+ after: ' in the release handoff thread',
+ },
+ {
+ label: '@reviewer',
+ time: '8m ago',
+ before: 'Reviewer referenced the same ',
+ match: 'handoff token',
+ after: ' during deploy review',
+ },
+ {
+ label: '#ops',
+ time: '12m ago',
+ before: 'Deployment note links the token to the final rollout checklist',
+ match: '',
+ after: '',
+ },
+];
+
+function visibleResultCount(length: number) {
+ if (length >= SEARCH_QUERY.length) return RESULTS.length;
+ if (length >= 8) return 2;
+ if (length >= 4) return 1;
+ return 0;
+}
+
+export function SearchPreviewAnimation() {
+ const [typedLength, setTypedLength] = useState(0);
+
+ useEffect(() => {
+ const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+
+ if (reducedMotion) {
+ setTypedLength(SEARCH_QUERY.length);
+ return;
+ }
+
+ let active = true;
+ let timeoutId: number | undefined;
+
+ const tick = (nextLength: number) => {
+ const atEnd = nextLength >= SEARCH_QUERY.length;
+ const delay = atEnd ? 2200 : nextLength === 0 ? 650 : 105;
+
+ timeoutId = window.setTimeout(() => {
+ if (!active) return;
+
+ const next = atEnd ? 0 : nextLength + 1;
+ setTypedLength(next);
+ tick(next);
+ }, delay);
+ };
+
+ setTypedLength(0);
+ tick(0);
+
+ return () => {
+ active = false;
+ window.clearTimeout(timeoutId);
+ };
+ }, []);
+
+ const query = SEARCH_QUERY.slice(0, typedLength);
+ const resultCount = visibleResultCount(typedLength);
+
+ return (
+
+
+
+
+
+
+
+ {query}
+
+
+
+
+ {RESULTS.map((result, index) => (
+
+
+ {result.label}
+ {result.time}
+
+
+ {result.before}
+ {result.match && {result.match} }
+ {result.after}
+
+
+ ))}
+
+
+ );
+}
diff --git a/web/app/agents.md/route.ts b/web/app/agents.md/route.ts
new file mode 100644
index 00000000..02f5eb8f
--- /dev/null
+++ b/web/app/agents.md/route.ts
@@ -0,0 +1,37 @@
+// Markdown endpoint for /agents, relocated from app/agents/route.ts so that
+// app/agents/page.tsx can serve the HTML gallery. Serves the AgentWorkforce/relay
+// README so any external LLM tooling that fetched the markdown keeps working at
+// /agents.md.
+//
+// The README lives in a different (public) repo, so it's fetched from GitHub at
+// build / revalidation rather than read from the local filesystem — which also
+// suits the Cloudflare Workers runtime, where node:fs is unavailable.
+
+export const revalidate = 86400;
+
+const RELAY_README_URL =
+ 'https://raw.githubusercontent.com/AgentWorkforce/relay/main/README.md';
+
+const FALLBACK =
+ '# Agent Relay\n\nReal-time messaging between AI agents.\n\nSee https://github.com/AgentWorkforce/relay for full documentation.';
+
+async function loadReadme(): Promise {
+ try {
+ const response = await fetch(RELAY_README_URL);
+ if (response.ok) {
+ return await response.text();
+ }
+ } catch {
+ // Network failure at build/revalidation — serve the fallback below.
+ }
+ return FALLBACK;
+}
+
+export async function GET() {
+ return new Response(await loadReadme(), {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Cache-Control': 'public, max-age=86400',
+ },
+ });
+}
diff --git a/web/app/agents/AgentsGallery.tsx b/web/app/agents/AgentsGallery.tsx
new file mode 100644
index 00000000..5331a3cf
--- /dev/null
+++ b/web/app/agents/AgentsGallery.tsx
@@ -0,0 +1,137 @@
+'use client';
+
+import Link from 'next/link';
+import { Box, Database, Zap } from 'lucide-react';
+
+import { FadeIn } from '../../components/FadeIn';
+import { AgentArt } from '../../components/agents/AgentArt';
+import { BuildYourOwn } from '../../components/agents/BuildYourOwn';
+import { AGENTS, INTEGRATION_LABELS } from '../../lib/agents';
+import s from './agents.module.css';
+
+const CAPABILITIES = [
+ {
+ Icon: Box,
+ title: 'Its own sandbox',
+ text: 'Every agent runs isolated in its own cloud sandbox — no shared state, no blast radius.',
+ },
+ {
+ Icon: Database,
+ title: 'Persistent memory',
+ text: 'Agents remember context across runs with scoped, expiring workspace memory.',
+ },
+ {
+ Icon: Zap,
+ title: 'One-click deploy',
+ text: 'Launch on Agent Relay Cloud, or fork the source and run it yourself.',
+ },
+];
+
+export function AgentsGallery() {
+ return (
+
+
+
+
+
+
+ AGENT GALLERY
+
+
+
+
+
+ Proactive agents,
+
+ ready to deploy
+
+
+
+
+
+ A gallery of open-source agents that watch your repos, inbox, and stack — then act. Pick one,
+ fork it, and launch it on Agent Relay in one click.
+
+
+
+
+
+
+ Browse use cases
+
+
+ View on GitHub
+
+
+
+
+
+
+
+ {CAPABILITIES.map(({ Icon, ...cap }, i) => (
+
+
+
+
+
+
+
{cap.title}
+
{cap.text}
+
+
+
+ ))}
+
+
+
+
+ {AGENTS.map((agent, i) => (
+
+
+
+
+
{agent.name}
+
{agent.tagline}
+
+ {agent.integrations.map((integration) => (
+
+ {INTEGRATION_LABELS[integration]}
+
+ ))}
+
+
+ View agent →
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
Powered by Agent Relay
+
+ Every agent runs on the Agent Relay platform — identity, shared files, messaging, and scheduling
+ out of the box. Fork one to your repo and make it yours.
+
+
+ See what they can do →
+
+
+
+
+
+ );
+}
diff --git a/web/app/agents/[slug]/AgentDetail.tsx b/web/app/agents/[slug]/AgentDetail.tsx
new file mode 100644
index 00000000..4ebc9511
--- /dev/null
+++ b/web/app/agents/[slug]/AgentDetail.tsx
@@ -0,0 +1,145 @@
+'use client';
+
+import Link from 'next/link';
+
+import { FadeIn } from '../../../components/FadeIn';
+import { AgentArt } from '../../../components/agents/AgentArt';
+import { ForkAgentButton } from '../../../components/agents/ForkAgentButton';
+import { INTEGRATION_LABELS, sourceUrl, type Agent } from '../../../lib/agents';
+import s from '../agents.module.css';
+
+export function AgentDetail({ agent }: { agent: Agent }) {
+ return (
+
+
+
+ ← All agents
+
+
+
+
+
+
+
+
+
+
+
+
{agent.tagline}
+
+ {agent.integrations.map((integration) => (
+
+ {INTEGRATION_LABELS[integration]}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What it does
+
{agent.description}
+
+
Highlights
+
+ {agent.highlights.map((highlight) => (
+
+
+ {highlight}
+
+ ))}
+
+
+
+
+
+
+
+
Trigger
+
{agent.trigger.summary}
+
+
+ {agent.trigger.kind === 'schedule' ? 'Schedule' : 'Event'}
+
+ {agent.trigger.detail}
+
+
+
+ Runtime
+ {agent.runtime}
+
+
+
Integrations
+
+ {agent.integrations.map((integration) => (
+
+ {INTEGRATION_LABELS[integration]}
+
+ ))}
+
+
+ {agent.inputs.length > 0 && (
+
+
Configure
+
+ {agent.inputs.map((input) => (
+
+ {input}
+
+ ))}
+
+
+ )}
+
+
+
+
+
+
+
+
+
Ready to run it?
+
+ Launch {agent.name} on Agent Relay in one click, or fork the source and tailor it to your team.
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/agents/[slug]/page.tsx b/web/app/agents/[slug]/page.tsx
new file mode 100644
index 00000000..8b8b7024
--- /dev/null
+++ b/web/app/agents/[slug]/page.tsx
@@ -0,0 +1,53 @@
+import type { Metadata } from 'next';
+import { notFound } from 'next/navigation';
+
+import { GitHubStarsBadge } from '../../../components/GitHubStars';
+import { SiteFooter } from '../../../components/SiteFooter';
+import { SiteNav } from '../../../components/SiteNav';
+import { agentAsset, allAgentSlugs, getAgent } from '../../../lib/agents';
+import { absoluteUrl } from '../../../lib/site';
+import { AgentDetail } from './AgentDetail';
+
+type PageProps = {
+ params: Promise<{ slug: string }>;
+};
+
+export async function generateStaticParams() {
+ return allAgentSlugs().map((slug) => ({ slug }));
+}
+
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { slug } = await params;
+ const agent = getAgent(slug);
+ if (!agent) return { title: 'Not Found' };
+
+ const title = `${agent.name} — Agent Relay`;
+ const description = agent.tagline;
+ const canonical = absoluteUrl(`/agents/${agent.slug}`);
+
+ return {
+ title,
+ description,
+ alternates: { canonical },
+ openGraph: {
+ title,
+ description,
+ url: canonical,
+ ...(agent.hasCustomArt ? { images: [{ url: absoluteUrl(agentAsset(agent.slug, 'card')) }] } : {}),
+ },
+ };
+}
+
+export default async function AgentPage({ params }: PageProps) {
+ const { slug } = await params;
+ const agent = getAgent(slug);
+ if (!agent) notFound();
+
+ return (
+ <>
+ } />
+
+
+ >
+ );
+}
diff --git a/web/app/agents/agents.module.css b/web/app/agents/agents.module.css
new file mode 100644
index 00000000..6261b1c1
--- /dev/null
+++ b/web/app/agents/agents.module.css
@@ -0,0 +1,1022 @@
+/* Shared styles for the /agents gallery, detail pages, and use-cases page.
+ Mirrors the design language of app/primitives (token-driven, Sora headings,
+ radial hero gradients, global .btn CTAs). */
+
+.page {
+ min-height: 100vh;
+ background:
+ radial-gradient(
+ ellipse 36% 28% at 10% 8%,
+ color-mix(in srgb, var(--primary) 16%, white 22%),
+ transparent 72%
+ ),
+ radial-gradient(
+ ellipse 34% 26% at 90% 10%,
+ color-mix(in srgb, var(--primary) 14%, white 20%),
+ transparent 70%
+ ),
+ var(--landing-hero-bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+:global(html[data-theme='dark']) .page {
+ background:
+ radial-gradient(circle at 14% 0%, color-mix(in srgb, var(--primary) 18%, transparent), transparent 36%),
+ radial-gradient(circle at 78% 14%, color-mix(in srgb, var(--primary) 14%, transparent), transparent 30%),
+ radial-gradient(circle at 50% 22%, color-mix(in srgb, var(--primary) 8%, transparent), transparent 44%),
+ var(--landing-hero-bg);
+}
+
+/* ── Hero ── */
+
+.heroSection {
+ position: relative;
+ margin-top: -60px;
+ padding-top: 60px;
+}
+
+.hero {
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 72px 40px 64px;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 26px;
+}
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--line);
+ padding: 6px 16px;
+ border-radius: 100px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ color: var(--primary);
+}
+
+.badgeDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 6px color-mix(in srgb, var(--primary) 50%, transparent);
+ animation: pulse 2s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.45;
+ }
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2.8rem, 6vw, 4.4rem);
+ font-weight: 500;
+ line-height: 1.06;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0;
+}
+
+:global(html[data-theme='dark']) .headline {
+ opacity: 1;
+}
+
+.subtitle {
+ font-size: 1.12rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 60ch;
+ margin: 0;
+}
+
+.pillNav {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 12px;
+ margin-top: 6px;
+}
+
+.pill {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 10px 22px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg);
+ font-size: 0.92rem;
+ font-weight: 600;
+ text-decoration: none;
+ transition:
+ border-color 0.2s,
+ color 0.2s,
+ transform 0.15s,
+ box-shadow 0.2s;
+}
+
+.pill:hover {
+ border-color: var(--primary);
+ color: var(--primary);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 16px color-mix(in srgb, var(--primary) 10%, transparent);
+}
+
+/* ── Capabilities strip ── */
+
+.capabilities {
+ max-width: 1100px;
+ margin: 0 auto;
+ padding: 8px 40px 4px;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 18px;
+}
+
+.capCard {
+ display: flex;
+ align-items: flex-start;
+ gap: 14px;
+ height: 100%;
+ padding: 18px 20px;
+ border-radius: 14px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+}
+
+.capIcon {
+ flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 38px;
+ height: 38px;
+ border-radius: 10px;
+ background: color-mix(in srgb, var(--primary) 12%, transparent);
+ color: var(--primary);
+}
+
+.capIcon svg {
+ width: 19px;
+ height: 19px;
+}
+
+.capTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 0.98rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0 0 3px;
+}
+
+.capText {
+ font-size: 0.86rem;
+ line-height: 1.5;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+@media (max-width: 860px) {
+ .capabilities {
+ grid-template-columns: 1fr;
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+}
+
+/* ── Gallery grid ── */
+
+.gallery {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 24px 40px 96px;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 24px;
+}
+
+.col {
+ display: flex;
+}
+
+.card {
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+ width: 100%;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ overflow: hidden;
+ text-decoration: none;
+ color: inherit;
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 4%, transparent);
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.2s;
+}
+
+.card:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 14px 36px color-mix(in srgb, var(--primary) 12%, transparent);
+ transform: translateY(-3px);
+}
+
+.cardMedia {
+ position: relative;
+ aspect-ratio: 16 / 9;
+ width: 100%;
+ overflow: hidden;
+ border-bottom: 1px solid var(--line);
+}
+
+.cardMedia img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+}
+
+.cardBody {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 20px 22px 22px;
+ flex: 1;
+}
+
+.cardName {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.18rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0;
+}
+
+.cardTagline {
+ font-size: 0.94rem;
+ line-height: 1.55;
+ color: var(--fg-muted);
+ margin: 0;
+ flex: 1;
+}
+
+.cardFooter {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ margin-top: 4px;
+}
+
+.cardArrow {
+ font-size: 0.9rem;
+ font-weight: 600;
+ color: var(--primary);
+ white-space: nowrap;
+}
+
+/* ── Integration / meta chips ── */
+
+.chips {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 7px;
+}
+
+.chip {
+ display: inline-flex;
+ align-items: center;
+ padding: 3px 11px;
+ border-radius: 100px;
+ border: 1px solid color-mix(in srgb, var(--primary) 16%, transparent);
+ background: color-mix(in srgb, var(--primary) 7%, transparent);
+ font-size: 0.74rem;
+ font-weight: 600;
+ letter-spacing: 0.01em;
+ color: var(--primary);
+}
+
+/* ── Detail page ── */
+
+.detail {
+ max-width: 1120px;
+ margin: 0 auto;
+ padding: 0 40px 40px;
+}
+
+.backRow {
+ max-width: 1120px;
+ margin: 0 auto;
+ padding: 20px 40px 14px;
+}
+
+.backLink {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ font-size: 0.85rem;
+ font-weight: 600;
+ color: var(--fg-faint);
+ text-decoration: none;
+ transition: color 0.2s;
+}
+
+.backLink:hover {
+ color: var(--primary);
+}
+
+.detailBanner {
+ position: relative;
+ width: 100%;
+ aspect-ratio: 1400 / 392;
+ border-radius: 22px;
+ overflow: hidden;
+ border: 1px solid var(--line);
+ box-shadow: 0 18px 48px color-mix(in srgb, var(--primary) 10%, transparent);
+}
+
+.detailBanner img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+}
+
+.detailHead {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ margin-top: 28px;
+}
+
+.titleRow {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+}
+
+.titleGitHub {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--fg-faint);
+ transition:
+ color 0.2s,
+ transform 0.15s;
+}
+
+.titleGitHub:hover {
+ color: var(--fg);
+ transform: translateY(-1px);
+}
+
+.detailTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2rem, 4vw, 2.9rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.08;
+ color: var(--fg);
+ margin: 0;
+}
+
+.detailTagline {
+ font-size: 1.12rem;
+ line-height: 1.55;
+ color: var(--fg-muted);
+ margin: 0;
+ max-width: 60ch;
+}
+
+.ctaRow {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 14px;
+ margin-top: 30px;
+}
+
+.ctaPrimary {
+ composes: btn btn-primary from global;
+}
+
+.ctaSecondary {
+ composes: btn btn-secondary from global;
+}
+
+.detailBody {
+ display: grid;
+ grid-template-columns: 1.6fr 1fr;
+ gap: 40px;
+ margin-top: 48px;
+}
+
+.sectionTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.4rem;
+ font-weight: 600;
+ letter-spacing: -0.02em;
+ color: var(--fg);
+ margin: 0 0 14px;
+}
+
+.lead {
+ font-size: 1.05rem;
+ line-height: 1.7;
+ color: var(--fg-muted);
+ margin: 0 0 28px;
+}
+
+.highlights {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.highlight {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ font-size: 0.98rem;
+ line-height: 1.55;
+ color: var(--fg);
+}
+
+.highlightDot {
+ flex-shrink: 0;
+ margin-top: 7px;
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 6px color-mix(in srgb, var(--primary) 50%, transparent);
+}
+
+.metaCard {
+ align-self: start;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ padding: 24px;
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 4%, transparent);
+ display: flex;
+ flex-direction: column;
+ gap: 18px;
+}
+
+.metaRow {
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+}
+
+.metaLabel {
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--fg-faint);
+}
+
+.metaValue {
+ font-size: 0.94rem;
+ line-height: 1.5;
+ color: var(--fg);
+}
+
+.codeChips {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 7px;
+}
+
+.codeChip {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.74rem;
+ padding: 3px 9px;
+ border-radius: 7px;
+ border: 1px solid var(--line);
+ background: var(--section-bg, var(--card-bg));
+ color: var(--fg-muted);
+}
+
+.triggerKind {
+ display: inline-flex;
+ align-items: center;
+ padding: 3px 10px;
+ border-radius: 100px;
+ font-size: 0.68rem;
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ border: 1px solid color-mix(in srgb, var(--primary) 22%, transparent);
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
+ color: var(--primary);
+}
+
+.triggerKind[data-kind='schedule'] {
+ border-color: color-mix(in srgb, var(--secondary-500) 30%, transparent);
+ background: color-mix(in srgb, var(--secondary-500) 12%, transparent);
+ color: var(--secondary-600);
+}
+
+/* ── Use cases ── */
+
+.bands {
+ padding-bottom: 24px;
+}
+
+.band {
+ border-top: 1px solid var(--line);
+ background: var(--bg);
+ scroll-margin-top: 80px;
+}
+
+.bandAlt {
+ background: var(--section-bg);
+}
+
+.bandInner {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 72px 40px 76px;
+}
+
+.bandHeader {
+ max-width: 64ch;
+ margin-bottom: 36px;
+}
+
+.bandTheme {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.7rem, 3.4vw, 2.4rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.12;
+ color: var(--fg);
+ margin: 0 0 10px;
+}
+
+.bandBlurb {
+ font-size: 1.02rem;
+ line-height: 1.6;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.useGrid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 20px;
+}
+
+.useCard {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 24px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--surface-strong) 96%, transparent) 0%,
+ color-mix(in srgb, var(--surface) 98%, transparent) 100%
+ );
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 4%, transparent);
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.2s;
+}
+
+.useCard:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 10px 28px color-mix(in srgb, var(--primary) 8%, transparent);
+ transform: translateY(-2px);
+}
+
+.useTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.1rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0;
+}
+
+.useDesc {
+ font-size: 0.95rem;
+ line-height: 1.6;
+ color: var(--fg-muted);
+ margin: 0;
+ flex: 1;
+}
+
+.agentRefs {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-top: 4px;
+}
+
+.agentRef {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 12px;
+ border-radius: 100px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ font-size: 0.8rem;
+ font-weight: 600;
+ color: var(--fg);
+ text-decoration: none;
+ transition:
+ border-color 0.2s,
+ color 0.2s;
+}
+
+a.agentRef:hover {
+ border-color: var(--primary);
+ color: var(--primary);
+}
+
+.agentRefDot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+/* ── Build your own ── */
+
+.buildWrapper {
+ border-top: 1px solid var(--line);
+ background: var(--section-bg);
+ padding: 0 40px;
+}
+
+.buildSection {
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 80px 0 88px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+
+.buildHeader {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ gap: 16px;
+}
+
+.buildTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.6vw, 2.6rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.12;
+ color: var(--fg);
+ margin: 0;
+}
+
+.buildSubtitle {
+ font-size: 1.05rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 62ch;
+ margin: 0;
+}
+
+.buildSubtitle code {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.92em;
+ padding: 1px 6px;
+ border-radius: 6px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg);
+}
+
+.buildStep {
+ width: 100%;
+ max-width: 760px;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.buildStepLabel {
+ display: block;
+ margin-bottom: 10px;
+ text-align: center;
+ font-size: 0.76rem;
+ font-weight: 700;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ color: var(--fg-faint);
+}
+
+.buildStep .buildStepLabel {
+ margin-bottom: 0;
+ text-align: left;
+}
+
+.installCmd {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ width: 100%;
+ min-width: 0;
+ padding: 11px 12px 11px 16px;
+ border-radius: 12px;
+ border: 1px solid var(--console-line);
+ background: var(--console-bg);
+ overflow-x: auto;
+}
+
+.installCmdText {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.82rem;
+ line-height: 1.4;
+ color: var(--console-fg);
+ white-space: nowrap;
+}
+
+.installCmdCopy {
+ flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 30px;
+ height: 30px;
+ border-radius: 8px;
+ border: 1px solid var(--console-line);
+ background: transparent;
+ color: var(--console-muted);
+ cursor: pointer;
+ transition:
+ color 0.2s,
+ border-color 0.2s;
+}
+
+.installCmdCopy:hover {
+ color: var(--console-fg);
+ border-color: var(--console-fg);
+}
+
+.installCmdCopy svg {
+ width: 15px;
+ height: 15px;
+}
+
+.installOr {
+ align-self: center;
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--fg-faint);
+}
+
+.promptCard {
+ width: 100%;
+ max-width: 760px;
+ min-width: 0;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ padding: 20px 22px;
+ border-radius: 16px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 5%, transparent);
+ text-align: left;
+}
+
+.promptPreview {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.86rem;
+ line-height: 1.5;
+ color: var(--fg-muted);
+}
+
+.promptPanel {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ min-width: 0;
+ padding: 16px;
+ border-radius: 12px;
+ border: 1px solid var(--line);
+ background: var(--section-bg, var(--bg));
+}
+
+.promptPanelTitle {
+ font-size: 0.7rem;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--fg-faint);
+}
+
+.promptPanelBody {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.82rem;
+ line-height: 1.6;
+ color: var(--fg);
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+ word-break: break-word;
+}
+
+.promptActions {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.promptShow,
+.promptCta {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 0.85rem;
+ font-weight: 600;
+ color: var(--fg-muted);
+}
+
+.promptShow,
+.promptCta {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0;
+ transition: color 0.2s;
+}
+
+.promptShow:hover {
+ color: var(--primary);
+}
+
+.promptCta {
+ color: var(--primary);
+}
+
+.promptCta:hover {
+ color: var(--primary-700);
+}
+
+.promptShow svg,
+.promptCta svg {
+ width: 15px;
+ height: 15px;
+}
+
+.buildLinks {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 12px;
+}
+
+/* ── Powered footer card ── */
+
+.poweredWrapper {
+ background: var(--bg);
+ padding: 0 40px 96px;
+ border-top: 1px solid var(--line);
+ padding-top: 80px;
+}
+
+.poweredCard {
+ max-width: 820px;
+ margin: 0 auto;
+ padding: 36px 32px;
+ text-align: center;
+ border-radius: 22px;
+ border: 1px solid var(--line);
+ background:
+ radial-gradient(circle at top, color-mix(in srgb, var(--primary) 10%, transparent), transparent 58%),
+ var(--card-bg);
+ box-shadow: 0 14px 40px color-mix(in srgb, var(--primary) 6%, transparent);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 16px;
+}
+
+.poweredEyebrow {
+ display: inline-block;
+ font-size: 0.75rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.poweredText {
+ margin: 0;
+ font-size: 1rem;
+ line-height: 1.7;
+ color: var(--fg-muted);
+ max-width: 52ch;
+}
+
+/* ── Responsive ── */
+
+@media (max-width: 1100px) {
+ .grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 960px) {
+ .hero {
+ padding: 56px 28px 52px;
+ }
+
+ .gallery {
+ padding: 16px 24px 80px;
+ }
+
+ .detail,
+ .backRow {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+
+ .detailBody {
+ grid-template-columns: 1fr;
+ gap: 32px;
+ }
+
+ .bandInner {
+ padding: 56px 24px 60px;
+ }
+
+ .poweredWrapper {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+}
+
+@media (max-width: 600px) {
+ .grid,
+ .useGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .headline {
+ font-size: clamp(2.2rem, 10vw, 3.6rem);
+ }
+
+ .detailHead {
+ flex-direction: column;
+ gap: 16px;
+ }
+
+ .ctaRow {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .ctaPrimary,
+ .ctaSecondary {
+ width: 100%;
+ justify-content: center;
+ }
+}
diff --git a/web/app/agents/page.tsx b/web/app/agents/page.tsx
new file mode 100644
index 00000000..cc3cb9db
--- /dev/null
+++ b/web/app/agents/page.tsx
@@ -0,0 +1,34 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { absoluteUrl } from '../../lib/site';
+import { AgentsGallery } from './AgentsGallery';
+
+const title = 'Agents — Proactive agents ready to deploy';
+const description =
+ 'A gallery of open-source proactive agents that review PRs, triage issues, monitor releases, and digest your work. Fork one and launch it on Agent Relay in one click.';
+
+export const metadata: Metadata = {
+ title,
+ description,
+ alternates: {
+ canonical: absoluteUrl('/agents'),
+ },
+ openGraph: {
+ title,
+ description,
+ url: absoluteUrl('/agents'),
+ },
+};
+
+export default function AgentsPage() {
+ return (
+ <>
+ } />
+
+
+ >
+ );
+}
diff --git a/web/app/agents/use-cases/UseCasesContent.tsx b/web/app/agents/use-cases/UseCasesContent.tsx
new file mode 100644
index 00000000..42f14bb5
--- /dev/null
+++ b/web/app/agents/use-cases/UseCasesContent.tsx
@@ -0,0 +1,119 @@
+'use client';
+
+import Link from 'next/link';
+
+import { FadeIn } from '../../../components/FadeIn';
+import { getAgent, NON_PUBLIC_AGENT_LABELS, USE_CASE_GROUPS } from '../../../lib/agents';
+import s from '../agents.module.css';
+
+function slugifyTheme(theme: string): string {
+ return theme
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, '-')
+ .replace(/(^-|-$)/g, '');
+}
+
+function AgentRef({ slug }: { slug: string }) {
+ const agent = getAgent(slug);
+ if (agent) {
+ return (
+
+
+ {agent.name}
+
+ );
+ }
+ const label = NON_PUBLIC_AGENT_LABELS[slug] ?? slug;
+ return {label} ;
+}
+
+export function UseCasesContent() {
+ return (
+
+
+
+
+
+
+ USE CASES
+
+
+
+
+
+ What proactive agents
+
+ do for you
+
+
+
+
+
+ These agents do not wait to be asked. They watch the work where it happens — pull requests,
+ issues, calls, release feeds — and take the next step on their own.
+
+
+
+
+
+ {USE_CASE_GROUPS.map((group) => (
+
+ {group.theme}
+
+ ))}
+
+
+
+
+
+
+ {USE_CASE_GROUPS.map((group, gi) => (
+
+
+
+
+
{group.theme}
+
{group.blurb}
+
+
+
+
+ {group.cases.map((useCase, ci) => (
+
+
+
{useCase.title}
+
{useCase.description}
+
+ {useCase.agents.map((slug) => (
+
+ ))}
+
+
+
+ ))}
+
+
+
+ ))}
+
+
+
+
+
+
Powered by Agent Relay
+
+ Browse the full gallery and launch any of these agents on Agent Relay in one click.
+
+
+ Explore all agents →
+
+
+
+
+
+ );
+}
diff --git a/web/app/agents/use-cases/page.tsx b/web/app/agents/use-cases/page.tsx
new file mode 100644
index 00000000..7fc96a52
--- /dev/null
+++ b/web/app/agents/use-cases/page.tsx
@@ -0,0 +1,34 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../../../components/GitHubStars';
+import { SiteFooter } from '../../../components/SiteFooter';
+import { SiteNav } from '../../../components/SiteNav';
+import { absoluteUrl } from '../../../lib/site';
+import { UseCasesContent } from './UseCasesContent';
+
+const title = 'Agent Use Cases — What proactive agents do for you';
+const description =
+ 'Concrete proactive use cases: auto-review and merge PRs, prevent codebase entropy, turn requests into PRs, monitor releases, and ship daily digests — mapped to the agents that deliver them.';
+
+export const metadata: Metadata = {
+ title,
+ description,
+ alternates: {
+ canonical: absoluteUrl('/agents/use-cases'),
+ },
+ openGraph: {
+ title,
+ description,
+ url: absoluteUrl('/agents/use-cases'),
+ },
+};
+
+export default function UseCasesPage() {
+ return (
+ <>
+ } />
+
+
+ >
+ );
+}
diff --git a/web/app/auth/RelayauthAnimation.tsx b/web/app/auth/RelayauthAnimation.tsx
new file mode 100644
index 00000000..a78cc0f1
--- /dev/null
+++ b/web/app/auth/RelayauthAnimation.tsx
@@ -0,0 +1,1091 @@
+'use client';
+
+import { useCallback, useEffect, useRef, useState, type JSX } from 'react';
+
+import s from '../../components/node-relay.module.css';
+
+type ModelProvider = 'claude' | 'gemini' | 'codex' | 'copilot' | 'opencode';
+type AuthEventKind = 'allowed' | 'denied' | 'token' | 'revoked';
+
+interface AgentNode {
+ id: string;
+ name: string;
+ provider: ModelProvider;
+ model: string;
+ state: 'IDLE' | 'AWAITING TASK' | 'PROCESSING' | 'RELAYING' | 'COMPLETE';
+ statusText: string;
+ glowing: boolean;
+ glowOpacity: number;
+ baseX: number;
+ baseY: number;
+ x: number;
+ y: number;
+ driftPhase: number;
+ driftSpeed: number;
+ driftAmplitudeX: number;
+ driftAmplitudeY: number;
+ active: boolean;
+ opacity: number;
+}
+
+interface Message {
+ from: number;
+ to: number;
+ t: number;
+ speed: number;
+ kind: AuthEventKind;
+ trail: { x: number; y: number; age: number }[];
+ branches?: number[];
+ isSpawn?: boolean;
+ blockedAge?: number;
+}
+
+interface LandingToast {
+ x: number;
+ y: number;
+ text: string;
+ kind: Exclude;
+ age: number;
+ maxAge: number;
+ alpha: number;
+}
+
+interface BlockedMark {
+ x: number;
+ y: number;
+ age: number;
+ maxAge: number;
+}
+
+const IDLE_TEXTS: Record = {
+ claude: ['Awaiting auth request...', 'Ready'],
+ gemini: ['Awaiting auth request...', 'Ready'],
+ codex: ['Awaiting auth request...', 'Ready'],
+ copilot: ['Awaiting auth request...', 'Ready'],
+ opencode: ['Awaiting auth request...', 'Ready'],
+};
+
+const WORKING_TEXTS: Record = {
+ claude: ['Verifying scope...', 'Checking RBAC...', 'Validating token...'],
+ gemini: ['Verifying scope...', 'Checking RBAC...', 'Validating token...'],
+ codex: ['Verifying scope...', 'Checking RBAC...', 'Validating token...'],
+ copilot: ['Verifying scope...', 'Checking RBAC...', 'Validating token...'],
+ opencode: ['Verifying scope...', 'Checking RBAC...', 'Validating token...'],
+};
+
+const RELAYING_TEXTS: Record = {
+ allowed: ['Scope verified ✓', 'RBAC: allowed', 'Identity confirmed'],
+ denied: ['Scope check FAILED', 'RBAC denied ✗', 'Token expired'],
+ token: ['Issuing JWT...', 'Signing token...', 'Rotating keys...'],
+ revoked: ['Revoking token...', 'Suspending identity...'],
+};
+
+const TOAST_TEXTS: Record, string[]> = {
+ allowed: ['✓ Scope: channel:read', '✓ Identity verified', '✓ RBAC: admin'],
+ token: ['Token issued (exp: 1h)', 'JWT signed (RS256)', 'Refresh token created'],
+ revoked: ['Token revoked', 'Session terminated', 'Identity suspended'],
+};
+
+const KIND_COLORS: Record = {
+ allowed: '99, 209, 139',
+ denied: '255, 95, 87',
+ token: '74, 144, 194',
+ revoked: '254, 188, 46',
+};
+
+const NODE_POOL: { name: string; provider: ModelProvider; model: string }[] = [
+ { name: 'AuthGateway', provider: 'claude', model: 'Opus' },
+ { name: 'Issuer', provider: 'claude', model: 'Sonnet' },
+ { name: 'Verifier', provider: 'claude', model: 'Haiku' },
+ { name: 'Policy', provider: 'codex', model: 'Codex-1' },
+ { name: 'Auditor', provider: 'gemini', model: '2.5 Pro' },
+ { name: 'Frontend', provider: 'copilot', model: 'GPT-4.1' },
+ { name: 'Worker', provider: 'gemini', model: 'Flash' },
+ { name: 'Admin', provider: 'claude', model: 'Sonnet' },
+];
+
+type ScriptEvent =
+ | { tick: number; type: 'spawn'; from: number; to: number }
+ | { tick: number; type: 'message'; from: number; to: number };
+
+const SCRIPT: ScriptEvent[] = [
+ { tick: 3, type: 'spawn', from: 0, to: 1 },
+ { tick: 6, type: 'spawn', from: 0, to: 2 },
+ { tick: 10, type: 'spawn', from: 0, to: 3 },
+ { tick: 14, type: 'message', from: 1, to: 2 },
+ { tick: 17, type: 'message', from: 2, to: 1 },
+ { tick: 21, type: 'spawn', from: 2, to: 4 },
+ { tick: 25, type: 'spawn', from: 2, to: 5 },
+ { tick: 29, type: 'spawn', from: 0, to: 6 },
+ { tick: 33, type: 'message', from: 4, to: 3 },
+ { tick: 35, type: 'message', from: 5, to: 3 },
+ { tick: 38, type: 'message', from: 3, to: 0 },
+ { tick: 42, type: 'spawn', from: 3, to: 7 },
+ { tick: 46, type: 'message', from: 0, to: 6 },
+ { tick: 49, type: 'message', from: 7, to: 2 },
+];
+
+const MAX_NODES = 8;
+const CARD_W = 160;
+const CARD_H = 88;
+
+const NODE_POSITIONS = (() => {
+ const cx = 0.38;
+ const cy = 0.38;
+ const positions: { x: number; y: number }[] = [{ x: cx, y: cy }];
+
+ const rings = [{ count: 7, radius: 0.32 }];
+
+ for (const ring of rings) {
+ for (let i = 0; i < ring.count; i++) {
+ const angle = (i / ring.count) * Math.PI * 2 - Math.PI * 0.1;
+ const jx = Math.sin(positions.length * 7.3) * 0.01;
+ const jy = Math.cos(positions.length * 5.1) * 0.008;
+ positions.push({
+ x: cx + Math.cos(angle) * ring.radius + jx,
+ y: cy + Math.sin(angle) * ring.radius * 0.85 + jy,
+ });
+ }
+ }
+
+ return positions;
+})();
+
+function buildConnections(count: number): [number, number][] {
+ const conns: [number, number][] = [];
+ const threshold = 0.38;
+ for (let i = 0; i < count; i++) {
+ for (let j = i + 1; j < count; j++) {
+ const dx = NODE_POSITIONS[i].x - NODE_POSITIONS[j].x;
+ const dy = NODE_POSITIONS[i].y - NODE_POSITIONS[j].y;
+ if (Math.sqrt(dx * dx + dy * dy) < threshold) {
+ conns.push([i, j]);
+ }
+ }
+ }
+ return conns;
+}
+
+function lerp(a: number, b: number, t: number) {
+ return a + (b - a) * t;
+}
+
+function easeInOut(t: number) {
+ return t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
+}
+
+function pick(arr: T[]): T {
+ return arr[Math.floor(Math.random() * arr.length)];
+}
+
+function isReadyNode(node: AgentNode) {
+ return node.active && node.opacity >= 0.9;
+}
+
+function pickAuthKind(): AuthEventKind {
+ const value = Math.random();
+ if (value < 0.5) {
+ return 'allowed';
+ }
+ if (value < 0.7) {
+ return 'denied';
+ }
+ if (value < 0.9) {
+ return 'token';
+ }
+ return 'revoked';
+}
+
+function speedForKind(kind: AuthEventKind) {
+ if (kind === 'denied') {
+ return 0.005 + Math.random() * 0.003;
+ }
+
+ return 0.007 + Math.random() * 0.005;
+}
+
+function enqueueMessage(messages: Message[], message: Omit) {
+ messages.push({
+ ...message,
+ t: 0,
+ trail: [],
+ });
+}
+
+function setNodeRelaying(node: AgentNode, statusText: string) {
+ node.state = 'RELAYING';
+ node.statusText = statusText;
+ node.glowing = true;
+}
+
+function setNodeDenied(node: AgentNode) {
+ node.state = 'RELAYING';
+ node.statusText = 'Access denied';
+ node.glowing = true;
+}
+
+function handleScriptEvents(tick: number, nodes: AgentNode[], messages: Message[]) {
+ for (const event of SCRIPT) {
+ if (event.tick !== tick) {
+ continue;
+ }
+
+ if (event.type === 'spawn') {
+ setNodeRelaying(nodes[event.from], `Spawning ${nodes[event.to].name}...`);
+ enqueueMessage(messages, {
+ from: event.from,
+ to: event.to,
+ speed: 0.02 + Math.random() * 0.008,
+ isSpawn: true,
+ kind: 'token',
+ });
+ continue;
+ }
+
+ const sender = nodes[event.from];
+ if (!isReadyNode(sender)) {
+ continue;
+ }
+
+ const kind = pickAuthKind();
+ setNodeRelaying(sender, pick(RELAYING_TEXTS[kind]));
+ enqueueMessage(messages, {
+ from: event.from,
+ to: event.to,
+ speed: speedForKind(kind),
+ kind,
+ });
+ }
+}
+
+function findReadyIndices(nodes: AgentNode[]) {
+ return nodes.map((node, index) => (isReadyNode(node) ? index : -1)).filter((index) => index >= 0);
+}
+
+function getActiveNeighbors(senderIdx: number, connections: [number, number][], nodes: AgentNode[]) {
+ return connections
+ .filter(([a, b]) => a === senderIdx || b === senderIdx)
+ .map(([a, b]) => (a === senderIdx ? b : a))
+ .filter((index) => isReadyNode(nodes[index]) && index !== senderIdx);
+}
+
+function pickRelayTargets(activeNeighbors: number[]) {
+ if (activeNeighbors.length >= 2 && Math.random() < 0.3) {
+ return [...activeNeighbors].sort(() => Math.random() - 0.5).slice(0, 2);
+ }
+
+ return [pick(activeNeighbors)];
+}
+
+function maybeQueueRandomRelay(
+ tick: number,
+ nodes: AgentNode[],
+ messages: Message[],
+ connections: [number, number][]
+) {
+ const readyIndices = findReadyIndices(nodes);
+ const scriptDone = tick > (SCRIPT.length > 0 ? SCRIPT[SCRIPT.length - 1].tick + 5 : 0);
+ if (!scriptDone || tick % 3 !== 0 || readyIndices.length < 2) {
+ return;
+ }
+
+ const senderIdx = pick(readyIndices);
+ const activeNeighbors = getActiveNeighbors(senderIdx, connections, nodes);
+ if (activeNeighbors.length === 0) {
+ return;
+ }
+
+ const kind = pickAuthKind();
+ const sender = nodes[senderIdx];
+ setNodeRelaying(sender, pick(RELAYING_TEXTS[kind]));
+
+ for (const target of pickRelayTargets(activeNeighbors)) {
+ const allowBranch = kind !== 'denied' && Math.random() < 0.25 && readyIndices.length > 1;
+ enqueueMessage(messages, {
+ from: senderIdx,
+ to: target,
+ speed: speedForKind(kind),
+ kind,
+ branches: allowBranch ? [pick(readyIndices)] : undefined,
+ });
+ }
+}
+
+function cycleNodeStates(nodes: AgentNode[], tick: number) {
+ for (const node of nodes) {
+ if (node.state === 'COMPLETE') {
+ node.state = 'AWAITING TASK';
+ node.statusText = pick(IDLE_TEXTS[node.provider]);
+ }
+
+ if (node.state === 'RELAYING' && tick % 3 === 0) {
+ node.state = 'COMPLETE';
+ node.statusText = 'Done';
+ }
+
+ if (node.state === 'PROCESSING' && Math.random() < 0.15) {
+ node.statusText = pick(WORKING_TEXTS[node.provider]);
+ }
+ }
+}
+
+function updateNodePositions(nodes: AgentNode[], now: number) {
+ for (const node of nodes) {
+ const t = now * node.driftSpeed + node.driftPhase;
+ node.x = node.baseX + Math.sin(t) * node.driftAmplitudeX + Math.cos(t * 0.7) * node.driftAmplitudeX * 0.5;
+ node.y =
+ node.baseY + Math.cos(t * 1.3) * node.driftAmplitudeY + Math.sin(t * 0.5) * node.driftAmplitudeY * 0.4;
+
+ node.opacity = node.active ? Math.min(node.opacity + 0.08, 1) : Math.max(node.opacity - 0.03, 0);
+
+ node.glowOpacity = node.glowing
+ ? Math.min(node.glowOpacity + 0.06, 1)
+ : Math.max(node.glowOpacity - 0.02, 0);
+
+ if (node.glowOpacity > 0.85) {
+ node.glowing = false;
+ }
+ }
+}
+
+type NodeCenter = { cx: number; cy: number; opacity: number };
+
+function buildCenters(nodes: AgentNode[], w: number, h: number): NodeCenter[] {
+ return nodes.map((node) => ({
+ cx: node.x * w + CARD_W / 2,
+ cy: node.y * h + CARD_H / 2,
+ opacity: node.opacity,
+ }));
+}
+
+function drawConnectionLines(
+ ctx: CanvasRenderingContext2D,
+ centers: NodeCenter[],
+ connections: [number, number][]
+) {
+ for (const [i, j] of connections) {
+ const ci = centers[i];
+ const cj = centers[j];
+ if (!ci || !cj) {
+ continue;
+ }
+
+ const lineOpacity = Math.min(ci.opacity, cj.opacity) * 0.1;
+ if (lineOpacity < 0.01) {
+ continue;
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(ci.cx, ci.cy);
+ ctx.lineTo(cj.cx, cj.cy);
+ ctx.strokeStyle = `rgba(45, 79, 62, ${lineOpacity})`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+}
+
+function trailAlphaForMessage(message: Message, pointAge: number) {
+ let alpha = Math.max(0, 0.24 - pointAge * 0.0035);
+
+ if (message.kind === 'denied' && typeof message.blockedAge === 'number' && message.blockedAge >= 10) {
+ const fade = Math.max(0, 1 - (message.blockedAge - 10) / 30);
+ alpha *= fade;
+ }
+
+ return alpha;
+}
+
+function drawMessageTrail(ctx: CanvasRenderingContext2D, message: Message) {
+ const color = KIND_COLORS[message.kind];
+
+ for (let trailIndex = message.trail.length - 1; trailIndex >= 0; trailIndex--) {
+ const point = message.trail[trailIndex];
+ point.age++;
+ const alpha = trailAlphaForMessage(message, point.age);
+
+ if (alpha <= 0) {
+ message.trail.splice(trailIndex, 1);
+ continue;
+ }
+
+ const trailSize = Math.max(0.8, 3.2 - point.age * 0.03);
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, trailSize, 0, Math.PI * 2);
+ ctx.fillStyle = `rgba(${color}, ${alpha})`;
+ ctx.fill();
+ }
+}
+
+function drawShieldShape(
+ ctx: CanvasRenderingContext2D,
+ x: number,
+ y: number,
+ scale: number,
+ strokeStyle: string,
+ alpha: number
+) {
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.scale(scale, scale);
+ ctx.beginPath();
+ ctx.moveTo(0, -11);
+ ctx.bezierCurveTo(5.5, -10.5, 8.5, -8.2, 8.5, -4.8);
+ ctx.lineTo(8.5, 0.5);
+ ctx.bezierCurveTo(8.5, 4.8, 5.2, 8.7, 0, 11.2);
+ ctx.bezierCurveTo(-5.2, 8.7, -8.5, 4.8, -8.5, 0.5);
+ ctx.lineTo(-8.5, -4.8);
+ ctx.bezierCurveTo(-8.5, -8.2, -5.5, -10.5, 0, -11);
+ ctx.closePath();
+ ctx.strokeStyle = strokeStyle;
+ ctx.globalAlpha = alpha;
+ ctx.lineWidth = 2.1;
+ ctx.lineJoin = 'round';
+ ctx.stroke();
+ ctx.restore();
+}
+
+function drawAuthPulse(ctx: CanvasRenderingContext2D, x: number, y: number, kind: AuthEventKind, alpha = 1) {
+ const color = KIND_COLORS[kind];
+ const glow = ctx.createRadialGradient(x, y, 0, x, y, 30);
+ glow.addColorStop(0, `rgba(${color}, ${0.34 * alpha})`);
+ glow.addColorStop(0.5, `rgba(${color}, ${0.09 * alpha})`);
+ glow.addColorStop(1, `rgba(${color}, 0)`);
+ ctx.beginPath();
+ ctx.arc(x, y, 30, 0, Math.PI * 2);
+ ctx.fillStyle = glow;
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(x, y, 16, 0, Math.PI * 2);
+ ctx.fillStyle = `rgba(15, 27, 41, ${0.72 * alpha})`;
+ ctx.fill();
+
+ if (kind === 'allowed' || kind === 'denied') {
+ drawShieldShape(ctx, x, y, 1, `rgba(${color}, ${0.92 * alpha})`, alpha);
+ ctx.strokeStyle =
+ kind === 'allowed' ? `rgba(234, 230, 221, ${0.95 * alpha})` : `rgba(255, 220, 220, ${0.95 * alpha})`;
+ ctx.lineWidth = 2.2;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+ ctx.beginPath();
+ if (kind === 'allowed') {
+ ctx.moveTo(x - 4.5, y + 0.5);
+ ctx.lineTo(x - 1.2, y + 4);
+ ctx.lineTo(x + 5.3, y - 3.5);
+ } else {
+ ctx.moveTo(x - 4.5, y - 4.5);
+ ctx.lineTo(x + 4.5, y + 4.5);
+ ctx.moveTo(x + 4.5, y - 4.5);
+ ctx.lineTo(x - 4.5, y + 4.5);
+ }
+ ctx.stroke();
+ return;
+ }
+
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.strokeStyle = `rgba(${color}, ${0.95 * alpha})`;
+ ctx.fillStyle = `rgba(${color}, ${0.95 * alpha})`;
+ ctx.lineWidth = 2.1;
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ ctx.beginPath();
+ ctx.arc(-5, -1, 4.6, 0, Math.PI * 2);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(-0.5, -1);
+ ctx.lineTo(7, -1);
+ ctx.lineTo(7, -4);
+ ctx.lineTo(10, -4);
+ ctx.lineTo(10, -1);
+ ctx.lineTo(12.5, -1);
+ ctx.lineTo(12.5, 2);
+ ctx.lineTo(7, 2);
+ ctx.lineTo(7, 5);
+ ctx.lineTo(4, 5);
+ ctx.lineTo(4, 2);
+ ctx.lineTo(-0.5, 2);
+ ctx.stroke();
+
+ if (kind === 'revoked') {
+ ctx.strokeStyle = `rgba(255, 245, 200, ${0.95 * alpha})`;
+ ctx.beginPath();
+ ctx.moveTo(-1, -3);
+ ctx.lineTo(2, 0);
+ ctx.lineTo(0, 2);
+ ctx.lineTo(3, 5);
+ ctx.stroke();
+
+ ctx.strokeStyle = `rgba(${color}, ${0.88 * alpha})`;
+ ctx.beginPath();
+ ctx.moveTo(-9, 9);
+ ctx.lineTo(11, -11);
+ ctx.stroke();
+ }
+
+ ctx.restore();
+}
+
+function spawnLandingToast(
+ toasts: LandingToast[],
+ x: number,
+ y: number,
+ kind: Exclude
+) {
+ toasts.push({
+ x,
+ y: y - 30,
+ text: pick(TOAST_TEXTS[kind]),
+ kind,
+ age: 0,
+ maxAge: 100,
+ alpha: 0,
+ });
+}
+
+function spawnBlockedMark(blockedMarks: BlockedMark[], x: number, y: number) {
+ blockedMarks.push({
+ x,
+ y,
+ age: 0,
+ maxAge: 42,
+ });
+}
+
+function handleMessageArrival(message: Message, nodes: AgentNode[], messages: Message[]) {
+ const receiver = nodes[message.to];
+ if (!receiver) {
+ return;
+ }
+
+ if (message.isSpawn && !receiver.active) {
+ receiver.active = true;
+ receiver.state = 'AWAITING TASK';
+ receiver.statusText = pick(IDLE_TEXTS[receiver.provider]);
+ receiver.glowing = true;
+ } else if (receiver.active) {
+ receiver.state = 'PROCESSING';
+ receiver.statusText = pick(WORKING_TEXTS[receiver.provider]);
+ receiver.glowing = true;
+ }
+
+ if (!message.branches || !receiver.active) {
+ return;
+ }
+
+ const branchTargets = message.branches.filter(
+ (branchTarget) =>
+ branchTarget !== message.to && branchTarget !== message.from && nodes[branchTarget]?.active
+ );
+
+ if (branchTargets.length === 0) {
+ return;
+ }
+
+ for (const branchTarget of branchTargets) {
+ const kind = pickAuthKind();
+ if (kind === 'denied') {
+ continue;
+ }
+
+ enqueueMessage(messages, {
+ from: message.to,
+ to: branchTarget,
+ speed: speedForKind(kind),
+ kind,
+ });
+ }
+
+ receiver.state = 'RELAYING';
+ receiver.statusText = pick(RELAYING_TEXTS[message.kind]);
+}
+
+function drawDeniedLabel(ctx: CanvasRenderingContext2D, x: number, y: number, alpha: number) {
+ ctx.save();
+ ctx.globalAlpha = alpha;
+ ctx.font = '700 10px var(--font-geist-mono), monospace';
+ const text = 'ACCESS DENIED';
+ const width = ctx.measureText(text).width + 14;
+ const height = 20;
+ const left = x - width / 2;
+ const top = y - 26;
+ const radius = 10;
+
+ ctx.beginPath();
+ ctx.moveTo(left + radius, top);
+ ctx.lineTo(left + width - radius, top);
+ ctx.arcTo(left + width, top, left + width, top + radius, radius);
+ ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
+ ctx.lineTo(left + radius, top + height);
+ ctx.arcTo(left, top + height, left, top + radius, radius);
+ ctx.arcTo(left, top, left + radius, top, radius);
+ ctx.closePath();
+ ctx.fillStyle = 'rgba(31, 11, 13, 0.92)';
+ ctx.fill();
+ ctx.strokeStyle = `rgba(${KIND_COLORS.denied}, 0.55)`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+
+ ctx.fillStyle = `rgba(${KIND_COLORS.denied}, 0.98)`;
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.fillText(text, x, top + height / 2 + 0.5);
+ ctx.restore();
+}
+
+function advanceMessage(message: Message) {
+ if (message.kind === 'denied') {
+ if (message.t < 0.7) {
+ message.t = Math.min(message.t + message.speed, 0.7);
+ return;
+ }
+
+ message.blockedAge = (message.blockedAge ?? 0) + 1;
+ return;
+ }
+
+ message.t += message.speed;
+}
+
+function handleDeniedMessageFrame(
+ ctx: CanvasRenderingContext2D,
+ nodes: AgentNode[],
+ message: Message,
+ blockedMarks: BlockedMark[],
+ x: number,
+ y: number
+): 'active' | 'remove' | 'ignore' {
+ if (message.kind !== 'denied' || message.t < 0.7) {
+ return 'ignore';
+ }
+
+ const blockedAge = message.blockedAge ?? 0;
+ const blinkAlpha =
+ blockedAge < 10 ? (blockedAge % 2 === 0 ? 1 : 0.3) : Math.max(0, 1 - (blockedAge - 10) / 30);
+
+ drawAuthPulse(ctx, x, y, message.kind, blinkAlpha);
+
+ if (blockedAge === 0) {
+ setNodeDenied(nodes[message.from]);
+ spawnBlockedMark(blockedMarks, x, y);
+ }
+
+ if (blockedAge >= 10) {
+ drawDeniedLabel(ctx, x, y, Math.max(0, 1 - (blockedAge - 10) / 30));
+ }
+
+ return blockedAge >= 40 ? 'remove' : 'active';
+}
+
+function drawMessages(
+ ctx: CanvasRenderingContext2D,
+ nodes: AgentNode[],
+ messages: Message[],
+ centers: NodeCenter[],
+ landingToasts: LandingToast[],
+ blockedMarks: BlockedMark[]
+) {
+ for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex--) {
+ const message = messages[messageIndex];
+ advanceMessage(message);
+
+ const from = centers[message.from];
+ const to = centers[message.to];
+ if (!from || !to) {
+ messages.splice(messageIndex, 1);
+ continue;
+ }
+
+ const progress = easeInOut(Math.min(message.t, 1));
+ const px = lerp(from.cx, to.cx, progress);
+ const py = lerp(from.cy, to.cy, progress);
+
+ message.trail.push({ x: px, y: py, age: 0 });
+ drawMessageTrail(ctx, message);
+
+ const deniedState = handleDeniedMessageFrame(ctx, nodes, message, blockedMarks, px, py);
+ if (deniedState !== 'ignore') {
+ if (deniedState === 'remove') {
+ messages.splice(messageIndex, 1);
+ }
+
+ continue;
+ }
+
+ if (message.t <= 1) {
+ drawAuthPulse(ctx, px, py, message.kind);
+ continue;
+ }
+
+ const arrivalKind = message.kind as Exclude;
+ spawnLandingToast(landingToasts, to.cx, to.cy, arrivalKind);
+ handleMessageArrival(message, nodes, messages);
+
+ messages.splice(messageIndex, 1);
+ }
+}
+
+function drawGlowRings(ctx: CanvasRenderingContext2D, nodes: AgentNode[], centers: NodeCenter[]) {
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+ if (node.glowOpacity <= 0.01 || node.opacity <= 0.1) {
+ continue;
+ }
+
+ const center = centers[i];
+ const radius = 56;
+ const alpha = node.glowOpacity * node.opacity * 0.07;
+ const grad = ctx.createRadialGradient(center.cx, center.cy, radius * 0.6, center.cx, center.cy, radius);
+ grad.addColorStop(0, `rgba(45, 79, 62, ${alpha})`);
+ grad.addColorStop(1, 'rgba(45, 79, 62, 0)');
+ ctx.beginPath();
+ ctx.arc(center.cx, center.cy, radius, 0, Math.PI * 2);
+ ctx.fillStyle = grad;
+ ctx.fill();
+ }
+}
+
+function drawLandingToasts(ctx: CanvasRenderingContext2D, landingToasts: LandingToast[]) {
+ for (let i = landingToasts.length - 1; i >= 0; i--) {
+ const toast = landingToasts[i];
+ toast.age++;
+
+ if (toast.age < 12) {
+ toast.alpha = toast.age / 12;
+ } else if (toast.age > toast.maxAge - 18) {
+ toast.alpha = (toast.maxAge - toast.age) / 18;
+ } else {
+ toast.alpha = 1;
+ }
+
+ toast.y -= 0.3;
+
+ if (toast.age >= toast.maxAge) {
+ landingToasts.splice(i, 1);
+ continue;
+ }
+
+ if (toast.alpha <= 0.01) {
+ continue;
+ }
+
+ const color = KIND_COLORS[toast.kind];
+ ctx.save();
+ ctx.globalAlpha = toast.alpha * 0.9;
+ ctx.font = '600 11px var(--font-geist-mono), monospace';
+ const textWidth = ctx.measureText(toast.text).width;
+ const pillWidth = textWidth + 20;
+ const pillHeight = 24;
+ const left = toast.x - pillWidth / 2;
+ const top = toast.y - pillHeight / 2;
+ const radius = pillHeight / 2;
+
+ ctx.beginPath();
+ ctx.moveTo(left + radius, top);
+ ctx.lineTo(left + pillWidth - radius, top);
+ ctx.arcTo(left + pillWidth, top, left + pillWidth, top + radius, radius);
+ ctx.arcTo(left + pillWidth, top + pillHeight, left + pillWidth - radius, top + pillHeight, radius);
+ ctx.lineTo(left + radius, top + pillHeight);
+ ctx.arcTo(left, top + pillHeight, left, top + radius, radius);
+ ctx.arcTo(left, top, left + radius, top, radius);
+ ctx.closePath();
+ ctx.fillStyle = 'rgba(15, 27, 41, 0.9)';
+ ctx.fill();
+ ctx.strokeStyle = `rgba(${color}, 0.35)`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+
+ ctx.fillStyle = `rgba(${color}, 0.97)`;
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.fillText(toast.text, toast.x, toast.y);
+ ctx.restore();
+ }
+}
+
+function drawBlockedMarks(ctx: CanvasRenderingContext2D, blockedMarks: BlockedMark[]) {
+ for (let i = blockedMarks.length - 1; i >= 0; i--) {
+ const mark = blockedMarks[i];
+ mark.age++;
+
+ if (mark.age >= mark.maxAge) {
+ blockedMarks.splice(i, 1);
+ continue;
+ }
+
+ const alpha = Math.max(0, 1 - mark.age / mark.maxAge);
+ ctx.save();
+ ctx.globalAlpha = alpha * 0.95;
+ ctx.strokeStyle = `rgba(${KIND_COLORS.denied}, 1)`;
+ ctx.lineWidth = 2.5;
+ ctx.lineCap = 'round';
+ ctx.beginPath();
+ ctx.moveTo(mark.x - 5, mark.y - 5);
+ ctx.lineTo(mark.x + 5, mark.y + 5);
+ ctx.moveTo(mark.x + 5, mark.y - 5);
+ ctx.lineTo(mark.x - 5, mark.y + 5);
+ ctx.stroke();
+ ctx.restore();
+ }
+}
+
+function drawAuthBoundary(ctx: CanvasRenderingContext2D, w: number, h: number) {
+ const y = h * 0.5;
+ ctx.save();
+ ctx.setLineDash([8, 6]);
+ ctx.strokeStyle = 'rgba(255, 95, 87, 0.12)';
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(24, y);
+ ctx.lineTo(w - 24, y);
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ const x = w * 0.5;
+ drawShieldShape(ctx, x, y, 0.72, 'rgba(255, 95, 87, 0.15)', 0.15);
+ ctx.restore();
+}
+
+function ClaudeLogo() {
+ return (
+
+
+
+ );
+}
+
+function CodexLogo() {
+ return (
+
+
+
+
+
+ );
+}
+
+function CopilotLogo() {
+ return (
+
+
+
+ );
+}
+
+function GeminiLogo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const LOGO_MAP: Record JSX.Element> = {
+ claude: ClaudeLogo,
+ codex: CodexLogo,
+ copilot: CopilotLogo,
+ opencode: GeminiLogo,
+ gemini: GeminiLogo,
+};
+
+export function RelayauthAnimation() {
+ const containerRef = useRef(null);
+ const canvasRef = useRef(null);
+ const nodesRef = useRef([]);
+ const messagesRef = useRef([]);
+ const connectionsRef = useRef<[number, number][]>([]);
+ const landingToastsRef = useRef([]);
+ const blockedMarksRef = useRef([]);
+ const rafRef = useRef(0);
+ const tickRef = useRef(0);
+ const timeRef = useRef(0);
+ const [renderNodes, setRenderNodes] = useState([]);
+
+ const initNodes = useCallback(() => {
+ const created: AgentNode[] = NODE_POOL.map((def, i) => ({
+ id: `node-${i}`,
+ name: def.name,
+ provider: def.provider,
+ model: def.model,
+ state: i === 0 ? 'PROCESSING' : 'IDLE',
+ statusText: i === 0 ? pick(WORKING_TEXTS[def.provider]) : '',
+ glowing: i === 0,
+ glowOpacity: 0,
+ baseX: NODE_POSITIONS[i].x,
+ baseY: NODE_POSITIONS[i].y,
+ x: NODE_POSITIONS[i].x,
+ y: NODE_POSITIONS[i].y,
+ driftPhase: Math.random() * Math.PI * 2,
+ driftSpeed: 0.0003 + Math.random() * 0.0004,
+ driftAmplitudeX: 0.008 + Math.random() * 0.01,
+ driftAmplitudeY: 0.006 + Math.random() * 0.008,
+ active: i === 0,
+ opacity: i === 0 ? 1 : 0,
+ }));
+ nodesRef.current = created;
+ connectionsRef.current = buildConnections(MAX_NODES);
+ messagesRef.current = [];
+ landingToastsRef.current = [];
+ blockedMarksRef.current = [];
+ setRenderNodes([...created]);
+ }, []);
+
+ useEffect(() => {
+ initNodes();
+ }, [initNodes]);
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ const tick = tickRef.current++;
+ const ns = nodesRef.current;
+ const ms = messagesRef.current;
+ handleScriptEvents(tick, ns, ms);
+ maybeQueueRandomRelay(tick, ns, ms, connectionsRef.current);
+ cycleNodeStates(ns, tick);
+
+ setRenderNodes([...ns]);
+ }, 250);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ const container = containerRef.current;
+ if (!canvas || !container) {
+ return;
+ }
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) {
+ return;
+ }
+
+ const resize = () => {
+ const rect = container.getBoundingClientRect();
+ const dpr = window.devicePixelRatio || 1;
+ canvas.width = rect.width * dpr;
+ canvas.height = rect.height * dpr;
+ canvas.style.width = `${rect.width}px`;
+ canvas.style.height = `${rect.height}px`;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ };
+
+ resize();
+ window.addEventListener('resize', resize);
+
+ const draw = () => {
+ const rect = container.getBoundingClientRect();
+ const w = rect.width;
+ const h = rect.height;
+ const ns = nodesRef.current;
+ const ms = messagesRef.current;
+ const now = timeRef.current++;
+
+ ctx.clearRect(0, 0, w, h);
+ updateNodePositions(ns, now);
+ const centers = buildCenters(ns, w, h);
+ drawAuthBoundary(ctx, w, h);
+ drawConnectionLines(ctx, centers, connectionsRef.current);
+ drawMessages(ctx, ns, ms, centers, landingToastsRef.current, blockedMarksRef.current);
+ drawGlowRings(ctx, ns, centers);
+ drawLandingToasts(ctx, landingToastsRef.current);
+ drawBlockedMarks(ctx, blockedMarksRef.current);
+
+ rafRef.current = requestAnimationFrame(draw);
+ };
+
+ rafRef.current = requestAnimationFrame(draw);
+
+ return () => {
+ window.removeEventListener('resize', resize);
+ cancelAnimationFrame(rafRef.current);
+ };
+ }, []);
+
+ return (
+
+
+ {renderNodes.map((node) => {
+ const Logo = LOGO_MAP[node.provider];
+ const isBusy = node.state === 'PROCESSING' || node.state === 'RELAYING';
+ const isDenied = node.statusText === 'Access denied';
+
+ return (
+
0
+ ? `0 0 ${18 * node.glowOpacity}px rgba(45, 79, 62, ${0.15 * node.glowOpacity}), 0 2px 10px rgba(0,0,0,0.08)`
+ : '0 2px 8px rgba(0,0,0,0.06)',
+ pointerEvents: node.opacity < 0.1 ? 'none' : undefined,
+ }}
+ >
+
+
+
+ {node.name}
+
+
{node.model}
+
+
+ {isBusy && }
+
+ {node.statusText}
+
+
+
+ );
+ })}
+
+ );
+}
diff --git a/web/app/auth/RelayauthContent.tsx b/web/app/auth/RelayauthContent.tsx
new file mode 100644
index 00000000..017f6c7c
--- /dev/null
+++ b/web/app/auth/RelayauthContent.tsx
@@ -0,0 +1,540 @@
+'use client';
+
+import Link from 'next/link';
+import { useMemo, useState } from 'react';
+
+import { FadeIn } from '../../components/FadeIn';
+import s from './relayauth.module.css';
+
+type SdkTab = 'typescript' | 'python' | 'curl';
+
+const featureGroups = [
+ {
+ title: 'JWT Tokens',
+ description:
+ 'Issue short-lived access tokens with sponsor chains, workspace context, and edge-verifiable claims.',
+ },
+ {
+ title: 'Scope-Based Access',
+ description: 'Grant exact permissions with plane, resource, action, and optional path constraints.',
+ },
+ {
+ title: 'RBAC Policies',
+ description:
+ 'Bundle scopes into named roles and layer deny-first policies from org to workspace to agent.',
+ },
+ {
+ title: 'Audit Trails',
+ description: 'Track every token use, scope decision, and admin action back to a responsible human.',
+ },
+ {
+ title: 'Token Revocation',
+ description: 'Invalidate credentials globally in under a second with edge-aware revocation checks.',
+ },
+ {
+ title: 'Budget Enforcement',
+ description: 'Cap spend, rate, and risky actions before an agent runs away with production access.',
+ },
+];
+
+const toolBadges = [
+ 'Claude Code',
+ 'Codex',
+ 'Gemini',
+ 'GitHub Copilot',
+ 'OpenCode',
+ 'Custom MCP servers',
+ 'CI workers',
+ 'Internal tools',
+];
+
+const whyCards = [
+ {
+ title: 'Zero infrastructure',
+ description:
+ 'No auth service to stitch together, no token broker to run, no callback validator to babysit.',
+ },
+ {
+ title: 'Instant setup',
+ description:
+ 'Create an identity, issue a token, and protect a route in minutes instead of designing an IAM stack.',
+ },
+ {
+ title: 'Framework-agnostic',
+ description:
+ 'Use the same token model across Workers, Node, Python services, edge middleware, and MCP tools.',
+ },
+];
+
+const getStartedSteps = [
+ {
+ title: 'Create an identity',
+ code: `curl -X POST https://api.relayauth.dev/v1/identities \\
+ -H "content-type: application/json" \\
+ -d '{
+ "name": "billing-bot",
+ "org_id": "org_acme",
+ "workspace_id": "ws_prod",
+ "sponsor_id": "user_jane"
+ }'`,
+ },
+ {
+ title: 'Issue a token',
+ code: `curl -X POST https://api.relayauth.dev/v1/tokens \\
+ -H "content-type: application/json" \\
+ -d '{
+ "identity_id": "agent_8x2k",
+ "scopes": ["stripe:orders:read", "relaycast:channel:write:#billing"],
+ "ttl": "1h"
+ }'`,
+ },
+ {
+ title: 'Verify at the edge',
+ code: `curl https://api.relayauth.dev/.well-known/jwks.json
+
+# Then validate locally and enforce:
+# stripe:orders:read`,
+ },
+];
+
+const sdkCode: Record = {
+ typescript: `import { RelayAuthClient } from "@agent-relay/auth";
+
+const auth = new RelayAuthClient({
+ apiKey: process.env.RELAYAUTH_API_KEY,
+});
+
+const identity = await auth.identities.create({
+ name: "billing-bot",
+ orgId: "org_acme",
+ workspaceId: "ws_prod",
+ sponsorId: "user_jane",
+});
+
+const token = await auth.tokens.issue({
+ identityId: identity.id,
+ scopes: [
+ "stripe:orders:read",
+ "relaycast:channel:write:#billing",
+ ],
+ ttl: "1h",
+});
+
+const claims = await auth.tokens.verify(token.accessToken);
+await auth.authorize({
+ token: token.accessToken,
+ scope: "stripe:orders:read",
+});`,
+ python: `from relayauth import RelayAuthClient
+
+auth = RelayAuthClient(api_key=os.environ["RELAYAUTH_API_KEY"])
+
+identity = auth.identities.create(
+ name="billing-bot",
+ org_id="org_acme",
+ workspace_id="ws_prod",
+ sponsor_id="user_jane",
+)
+
+token = auth.tokens.issue(
+ identity_id=identity["id"],
+ scopes=[
+ "stripe:orders:read",
+ "relaycast:channel:write:#billing",
+ ],
+ ttl="1h",
+)
+
+claims = auth.tokens.verify(token["access_token"])
+auth.authorize(
+ token=token["access_token"],
+ scope="stripe:orders:read",
+)`,
+ curl: `curl -X POST https://api.relayauth.dev/v1/identities \\
+ -H "authorization: Bearer $RELAYAUTH_API_KEY" \\
+ -H "content-type: application/json" \\
+ -d '{
+ "name": "billing-bot",
+ "org_id": "org_acme",
+ "workspace_id": "ws_prod",
+ "sponsor_id": "user_jane"
+ }'
+
+curl -X POST https://api.relayauth.dev/v1/tokens \\
+ -H "authorization: Bearer $RELAYAUTH_API_KEY" \\
+ -H "content-type: application/json" \\
+ -d '{
+ "identity_id": "agent_8x2k",
+ "scopes": ["stripe:orders:read"],
+ "ttl": "1h"
+ }'
+
+curl https://api.relayauth.dev/.well-known/jwks.json`,
+};
+
+function highlight(code: string, tab: SdkTab) {
+ const keywords = tab === 'python' ? /\b(from|import|await)\b/g : /\b(import|from|const|await|new)\b/g;
+ const types = /\b(RelayAuthClient)\b/g;
+ const methods = /\.(\w+)\(/g;
+
+ const escaped = code.replace(/&/g, '&').replace(//g, '>');
+
+ const parts: string[] = [];
+ let lastIndex = 0;
+ const stringMatches = [...escaped.matchAll(/(["'`])(?:(?!\1).)*\1/g)];
+
+ if (stringMatches.length === 0) {
+ return highlightNonString(escaped, keywords, types, methods);
+ }
+
+ for (const match of stringMatches) {
+ const before = escaped.slice(lastIndex, match.index);
+ parts.push(highlightNonString(before, keywords, types, methods));
+ parts.push(`${match[0]} `);
+ lastIndex = match.index! + match[0].length;
+ }
+
+ parts.push(highlightNonString(escaped.slice(lastIndex), keywords, types, methods));
+ return parts.join('');
+}
+
+function highlightNonString(text: string, keywords: RegExp, types: RegExp, methods: RegExp) {
+ return text
+ .replace(types, `$1 `)
+ .replace(keywords, `$& `)
+ .replace(methods, `.$1 (`);
+}
+
+function RelayauthAnimation() {
+ return (
+
+
+
+
+
+
+
+
+
relayauth control plane
+
+
+
+
+
+ Identity
+ agent_8x2k
+
+
billing-bot
+
sponsor: jane@acme.com
+
+
+
+
+
+
+
+
+ Token
+ active
+
+
TTL 1h
+
scoped JWT + refresh chain
+
+
+
+
+ Policy evaluation
+ allow
+
+
+ stripe:orders:read
+ granted
+
+
+ stripe:orders:approve
+ denied
+
+
+ budget.maxCostPerDay
+ $5,000
+
+
+
+
+
+
+ Issued token for billing-bot
+
+
+
+ Verified sponsor chain user_jane → agent_8x2k
+
+
+
+ Blocked approve scope escalation
+
+
+
+
+
+ );
+}
+
+export function RelayauthContent() {
+ const [tab, setTab] = useState('typescript');
+
+ const highlightedCode = useMemo(() => highlight(sdkCode[tab], tab), [tab]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RELAYAUTH
+
+
+
+ Auth
+
+ for agents
+
+
+
+ Tokens, scopes, RBAC, policies, and audit trails for multi-agent systems. Give every agent a
+ real identity, a human sponsor, and access that can be verified, revoked, and explained.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Everything an agent identity layer needs
+
+ Start with issuance and verification. Scale to budgets, policies, audit, and global revocation.
+
+
+
+
+ {featureGroups.map((feature, index) => (
+
+
+ {feature.title}
+ {feature.description}
+
+ ))}
+
+
+
+
+
+
+ Works with every AI tool
+
+ Use the same identity plane across local coding agents, cloud workers, approval bots, and
+ internal platforms.
+
+
+
+ {toolBadges.map((tool) => (
+
+ {tool}
+
+ ))}
+
+
+
+
+
+
+
+ Same auth flow, any SDK
+
+ Create identities, issue scoped tokens, verify claims, and enforce permissions from the same
+ control plane.
+
+
+
+
+
+
+ setTab('typescript')}
+ >
+ TypeScript
+
+ setTab('python')}
+ >
+ Python
+
+ setTab('curl')}
+ >
+ cURL
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Why Relayauth
+
+ Ship authorization for agents without building an identity platform from scratch.
+
+
+
+
+ {whyCards.map((card, index) => (
+
+ {card.title}
+ {card.description}
+
+ ))}
+
+
+
+
+
+
+
+ Get started in three requests
+
+ Create the agent, mint the token, and publish verification keys for every service that needs to
+ trust it.
+
+
+
+
+ {getStartedSteps.map((step, index) => (
+
+ {index + 1}
+ {step.title}
+
+ {step.code}
+
+
+ ))}
+
+
+
+
+
+
+ Powered by Agent Relay
+
+ One identity layer for Relaycast, files, cloud jobs, MCP servers, and the rest of your agent
+ stack.
+
+
+
+
+ );
+}
diff --git a/web/app/auth/page.tsx b/web/app/auth/page.tsx
new file mode 100644
index 00000000..55e66f04
--- /dev/null
+++ b/web/app/auth/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function AuthPage() {
+ redirect('/primitives#auth');
+}
diff --git a/web/app/auth/relayauth.module.css b/web/app/auth/relayauth.module.css
new file mode 100644
index 00000000..d37adb01
--- /dev/null
+++ b/web/app/auth/relayauth.module.css
@@ -0,0 +1,884 @@
+.page {
+ min-height: 100vh;
+ background:
+ radial-gradient(
+ ellipse 36% 28% at 10% 8%,
+ color-mix(in srgb, var(--primary) 16%, white 22%),
+ transparent 72%
+ ),
+ radial-gradient(
+ ellipse 34% 26% at 90% 10%,
+ color-mix(in srgb, var(--secondary-400) 12%, white 28%),
+ transparent 72%
+ ),
+ var(--landing-hero-bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+.heroSection {
+ position: relative;
+ background: transparent;
+ overflow: hidden;
+ margin-top: -60px;
+ padding-top: 60px;
+}
+
+:global(html[data-theme='dark']) .page {
+ background:
+ radial-gradient(circle at 14% 0%, color-mix(in srgb, var(--primary) 18%, transparent), transparent 36%),
+ radial-gradient(
+ circle at 82% 12%,
+ color-mix(in srgb, var(--secondary-400) 16%, transparent),
+ transparent 32%
+ ),
+ radial-gradient(circle at 50% 22%, color-mix(in srgb, var(--primary) 8%, transparent), transparent 44%),
+ var(--landing-hero-bg);
+}
+
+.heroSection > * {
+ position: relative;
+ z-index: 2;
+}
+
+.heroBgSvg {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 0;
+ pointer-events: none;
+ opacity: 0.96;
+ filter: saturate(1.1) contrast(1.04);
+}
+
+:global(html[data-theme='dark']) .heroBgSvg {
+ opacity: 0.88;
+ filter: none;
+}
+
+.hero {
+ display: grid;
+ grid-template-columns: 1fr 1.2fr;
+ align-items: start;
+ gap: 32px;
+ max-width: 1280px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 28px 40px 48px;
+}
+
+.heroLeft {
+ display: flex;
+ flex-direction: column;
+ gap: 28px;
+}
+
+.heroRight {
+ position: relative;
+ align-self: stretch;
+ min-height: 580px;
+}
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--line);
+ padding: 6px 16px;
+ border-radius: 100px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ color: var(--primary);
+ width: fit-content;
+}
+
+.badgeDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--secondary-500);
+ box-shadow: 0 0 10px color-mix(in srgb, var(--secondary-500) 45%, transparent);
+ animation: pulse 2s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.4;
+ }
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(3.2rem, 7vw, 5.5rem);
+ font-weight: 500;
+ line-height: 1.04;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ opacity: 0.88;
+ margin: 0;
+}
+
+:global(html[data-theme='dark']) .headline {
+ opacity: 1;
+}
+
+.subtitle {
+ font-size: 1.1rem;
+ line-height: 1.68;
+ color: var(--fg-muted);
+ max-width: 50ch;
+ font-weight: 400;
+ margin: 0;
+}
+
+.ctas {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding-top: 4px;
+}
+
+.ctaPrimary {
+ composes: btn btn-primary from global;
+}
+
+.ctaSecondary {
+ composes: btn btn-secondary from global;
+}
+
+.authVisual {
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 24px 0;
+}
+
+.authFrame {
+ width: min(100%, 640px);
+ border-radius: 22px;
+ overflow: hidden;
+ background:
+ linear-gradient(180deg, color-mix(in srgb, var(--surface-strong) 92%, transparent), var(--surface)),
+ var(--surface);
+ border: 1px solid var(--line);
+ box-shadow:
+ 0 22px 60px color-mix(in srgb, black 14%, transparent),
+ inset 0 1px 0 rgba(255, 255, 255, 0.22);
+}
+
+.authFrameHeader {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 14px 18px;
+ background: color-mix(in srgb, var(--surface) 80%, white 20%);
+ border-bottom: 1px solid var(--line);
+}
+
+.previewDots {
+ display: flex;
+ gap: 6px;
+}
+
+.previewDots span {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+}
+
+.authFrameLabel {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ color: var(--fg-faint);
+}
+
+.authFlowGrid {
+ display: grid;
+ grid-template-columns: 1fr 52px 1fr;
+ gap: 18px;
+ padding: 24px;
+ align-items: center;
+}
+
+.authCard,
+.policyPanel,
+.auditPanel {
+ border-radius: 16px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ box-shadow: 0 10px 24px color-mix(in srgb, var(--primary) 6%, transparent);
+}
+
+.authCard {
+ padding: 18px;
+}
+
+.authCardTop,
+.policyHeader,
+.policyRow,
+.auditRow {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.authCardTop {
+ margin-bottom: 12px;
+}
+
+.authCardLabel {
+ font-size: 0.7rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--fg-faint);
+}
+
+.authCardHint {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ color: var(--primary);
+}
+
+.authCardTitle {
+ display: block;
+ margin: 0 0 8px;
+ font-size: 1.05rem;
+ color: var(--fg);
+}
+
+.authCardMeta {
+ font-size: 0.82rem;
+ color: var(--fg-muted);
+}
+
+.authConnector {
+ position: relative;
+ height: 2px;
+ border-radius: 999px;
+ background: linear-gradient(90deg, var(--primary), var(--secondary-500));
+ overflow: visible;
+}
+
+.connectorPulse {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ background: var(--secondary-500);
+ transform: translate(-10%, -50%);
+ box-shadow: 0 0 0 8px color-mix(in srgb, var(--secondary-500) 14%, transparent);
+ animation: flow 2.6s ease-in-out infinite;
+}
+
+@keyframes flow {
+ 0% {
+ left: 0;
+ opacity: 0.6;
+ }
+ 50% {
+ left: calc(100% - 10px);
+ opacity: 1;
+ }
+ 100% {
+ left: 0;
+ opacity: 0.6;
+ }
+}
+
+.policyPanel {
+ grid-column: 1 / span 2;
+ padding: 18px;
+}
+
+.policyHeader {
+ padding-bottom: 12px;
+ margin-bottom: 12px;
+ border-bottom: 1px solid var(--line);
+ font-weight: 600;
+ color: var(--fg);
+}
+
+.policyRow {
+ padding: 10px 0;
+ border-bottom: 1px solid color-mix(in srgb, var(--line) 72%, transparent);
+ font-size: 0.83rem;
+ color: var(--fg-muted);
+}
+
+.policyRow:last-child {
+ border-bottom: none;
+}
+
+.policyRow code {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ color: var(--code-fg);
+}
+
+.auditPanel {
+ padding: 18px;
+}
+
+.auditPanel {
+ display: grid;
+ gap: 10px;
+}
+
+.auditRow {
+ justify-content: flex-start;
+ font-size: 0.82rem;
+ line-height: 1.5;
+ color: var(--fg-muted);
+}
+
+.auditDot {
+ width: 8px;
+ height: 8px;
+ flex: 0 0 auto;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 10px color-mix(in srgb, var(--primary) 35%, transparent);
+}
+
+.authAllowed {
+ color: var(--status-green);
+}
+.authDenied {
+ color: var(--status-red);
+}
+
+.featuresWrapper,
+.whyWrapper {
+ background: var(--bg);
+}
+
+.sdkWrapper,
+.getStartedWrapper {
+ background: var(--section-bg);
+}
+
+.toolsWrapper {
+ background: var(--showcase-bg);
+ position: relative;
+ overflow: hidden;
+}
+
+.toolsWrapper::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 40% 50% at 20% 30%, rgba(255, 255, 255, 0.36) 0%, transparent 70%),
+ radial-gradient(ellipse 35% 40% at 75% 65%, rgba(255, 255, 255, 0.18) 0%, transparent 70%);
+ pointer-events: none;
+}
+
+.featuresHeader,
+.toolsSection,
+.sdkSection,
+.whySection,
+.getStartedSection,
+.poweredSection {
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.featuresHeader {
+ padding: 72px 40px 48px;
+ text-align: center;
+}
+
+.featuresTitle,
+.sectionTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.4vw, 2.6rem);
+ font-weight: 600;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ opacity: 0.88;
+ margin: 0 0 12px;
+}
+
+.featuresSubtitle,
+.sectionSubtitle {
+ font-size: 1.04rem;
+ line-height: 1.64;
+ color: var(--fg-muted);
+ margin: 0 auto;
+ max-width: 58ch;
+}
+
+.featuresSection {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 22px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 0 40px 84px;
+}
+
+.featureCard {
+ position: relative;
+ padding: 24px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background:
+ linear-gradient(180deg, color-mix(in srgb, var(--surface-strong) 80%, transparent), var(--card-bg)),
+ var(--card-bg);
+ overflow: hidden;
+ min-height: 186px;
+}
+
+.featureCardAccent {
+ position: absolute;
+ inset: 0 auto auto 0;
+ width: 100%;
+ height: 3px;
+ background: linear-gradient(90deg, var(--primary), var(--secondary-500));
+ opacity: 0.8;
+}
+
+.featureTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.08rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0 0 10px;
+}
+
+.featureDesc {
+ font-size: 0.92rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.toolsSection {
+ padding: 80px 40px 92px;
+ text-align: center;
+ position: relative;
+ z-index: 1;
+}
+
+.toolsText {
+ margin-bottom: 40px;
+}
+
+.toolBadgeGrid {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 14px;
+}
+
+.toolBadge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 12px 18px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg);
+ font-size: 0.9rem;
+ font-weight: 600;
+ box-shadow: 0 8px 20px color-mix(in srgb, var(--primary) 5%, transparent);
+}
+
+.sdkSection {
+ display: grid;
+ grid-template-columns: 0.9fr 1.1fr;
+ align-items: center;
+ gap: 48px;
+ padding: 84px 40px;
+}
+
+.sdkText {
+ display: flex;
+ flex-direction: column;
+}
+
+.sdkCodeWrap {
+ min-width: 0;
+}
+
+.codeBlock {
+ border-radius: 18px;
+ overflow: hidden;
+ background: var(--terminal-bg);
+ border: 1px solid var(--terminal-line);
+ box-shadow: 0 18px 44px color-mix(in srgb, black 22%, transparent);
+}
+
+.tabs {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 14px 14px 0;
+ background: var(--terminal-panel);
+ border-bottom: 1px solid var(--terminal-line);
+}
+
+.tab {
+ border: 1px solid transparent;
+ background: transparent;
+ color: var(--terminal-muted);
+ font-size: 0.8rem;
+ font-weight: 600;
+ padding: 10px 12px;
+ border-radius: 10px 10px 0 0;
+ cursor: pointer;
+ transition:
+ color 0.2s ease,
+ background 0.2s ease,
+ border-color 0.2s ease;
+}
+
+.tab:hover {
+ color: var(--terminal-fg);
+}
+
+.tabActive {
+ color: var(--terminal-fg);
+ background: color-mix(in srgb, var(--terminal-bg) 88%, white 12%);
+ border-color: var(--terminal-line);
+ border-bottom-color: color-mix(in srgb, var(--terminal-bg) 88%, white 12%);
+}
+
+.pre {
+ margin: 0;
+ padding: 18px;
+ min-height: 420px;
+ overflow: auto;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.78rem;
+ line-height: 1.68;
+ color: var(--terminal-fg);
+}
+
+.codeKeyword {
+ color: var(--terminal-keyword);
+}
+.codeString {
+ color: var(--terminal-string);
+}
+.codeType {
+ color: var(--terminal-type);
+}
+.codeMethod {
+ color: var(--terminal-method);
+}
+
+.whySection {
+ padding: 84px 40px;
+ text-align: center;
+}
+
+.whyCards {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 22px;
+ margin-top: 42px;
+}
+
+.whyCard {
+ padding: 28px;
+ text-align: left;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+}
+
+.whyCardTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.14rem;
+ color: var(--fg);
+ margin: 0 0 10px;
+}
+
+.whyCardText {
+ font-size: 0.95rem;
+ line-height: 1.64;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.getStartedSection {
+ padding: 84px 40px 96px;
+ text-align: center;
+}
+
+.stepsGrid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 22px;
+ margin-top: 40px;
+}
+
+.stepCard {
+ padding: 24px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ text-align: left;
+}
+
+.stepNumber {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 34px;
+ height: 34px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, var(--primary), var(--secondary-500));
+ color: #fff;
+ font-size: 0.9rem;
+ font-weight: 700;
+ margin-bottom: 14px;
+}
+
+.stepTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.05rem;
+ color: var(--fg);
+ margin: 0 0 14px;
+}
+
+.stepCode {
+ margin: 0;
+ padding: 16px;
+ border-radius: 12px;
+ background: var(--code-bg);
+ color: var(--code-fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ line-height: 1.62;
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+}
+
+.poweredWrapper {
+ background: var(--bg);
+ border-top: 1px solid var(--line);
+}
+
+.poweredSection {
+ padding: 42px 40px 56px;
+ text-align: center;
+}
+
+.poweredLabel {
+ margin: 0 0 8px;
+ font-size: 0.84rem;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.poweredCopy {
+ margin: 0 auto;
+ max-width: 56ch;
+ font-size: 0.96rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+}
+
+@media (max-width: 1100px) {
+ .featuresSection,
+ .stepsGrid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .whyCards {
+ grid-template-columns: 1fr;
+ }
+
+ .sdkSection {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 960px) {
+ .hero {
+ grid-template-columns: 1fr;
+ padding: 52px 32px;
+ gap: 24px;
+ }
+
+ .heroLeft {
+ gap: 22px;
+ }
+
+ .heroRight {
+ min-height: 420px;
+ }
+
+ .headline {
+ font-size: clamp(2.85rem, 10.5vw, 4.35rem);
+ }
+
+ .featuresHeader,
+ .featuresSection,
+ .toolsSection,
+ .sdkSection,
+ .whySection,
+ .getStartedSection,
+ .poweredSection {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+
+ .featuresSection {
+ gap: 18px;
+ }
+
+ .authFlowGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .authConnector {
+ width: 2px;
+ height: 42px;
+ margin: 0 auto;
+ background: linear-gradient(180deg, var(--primary), var(--secondary-500));
+ }
+
+ .connectorPulse {
+ left: 50%;
+ top: 0;
+ transform: translate(-50%, -10%);
+ animation-name: flowVertical;
+ }
+
+ .policyPanel {
+ grid-column: auto;
+ }
+}
+
+@keyframes flowVertical {
+ 0% {
+ top: 0;
+ opacity: 0.6;
+ }
+ 50% {
+ top: calc(100% - 10px);
+ opacity: 1;
+ }
+ 100% {
+ top: 0;
+ opacity: 0.6;
+ }
+}
+
+@media (max-width: 720px) {
+ .featuresSection,
+ .stepsGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .toolBadgeGrid {
+ gap: 10px;
+ }
+
+ .toolBadge {
+ width: 100%;
+ }
+
+ .tabs {
+ flex-wrap: wrap;
+ }
+
+ .pre {
+ min-height: 360px;
+ }
+}
+
+@media (max-width: 600px) {
+ .hero {
+ padding: 64px 20px 56px;
+ gap: 28px;
+ }
+
+ .heroLeft {
+ gap: 24px;
+ }
+
+ .headline {
+ font-size: clamp(2.4rem, 11.5vw, 4.5rem);
+ line-height: 1.08;
+ }
+
+ .ctas {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .ctaPrimary,
+ .ctaSecondary {
+ width: 100%;
+ padding: 12px 20px;
+ font-size: 0.92rem;
+ justify-content: center;
+ }
+
+ .subtitle {
+ font-size: 1.08rem;
+ }
+
+ .heroRight {
+ min-height: 360px;
+ }
+
+ .authFrameHeader,
+ .authFlowGrid {
+ padding-left: 16px;
+ padding-right: 16px;
+ }
+
+ .featuresHeader,
+ .featuresSection,
+ .toolsSection,
+ .sdkSection,
+ .whySection,
+ .getStartedSection,
+ .poweredSection {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+
+ .pre {
+ font-size: 0.72rem;
+ min-height: 360px;
+ padding: 14px;
+ }
+
+ .stepCode {
+ font-size: 0.7rem;
+ }
+
+ .toolBadge {
+ padding: 10px 14px;
+ font-size: 0.84rem;
+ }
+
+ .whyCards {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/web/app/blog/[slug]/og.png/route.tsx b/web/app/blog/[slug]/og.png/route.tsx
new file mode 100644
index 00000000..a551fa3f
--- /dev/null
+++ b/web/app/blog/[slug]/og.png/route.tsx
@@ -0,0 +1,65 @@
+import { ImageResponse } from 'next/og';
+import { notFound } from 'next/navigation';
+
+import { getAllPosts, getPost } from '../../../../lib/blog';
+import { BlogVariant, loadBrandFonts, OG_SIZE } from '../../../../lib/og/template';
+
+export const runtime = 'nodejs';
+// Prerender one card per post at build time. The post set is fixed per deploy,
+// so there is no need to render (or re-fetch the brand fonts) per request.
+export const dynamic = 'force-static';
+
+type RouteContext = {
+ params: Promise<{ slug: string }>;
+};
+
+/** Prerender a card for every published post. */
+export function generateStaticParams() {
+ return getAllPosts().map((post) => ({ slug: post.slug }));
+}
+
+function formatDate(value: string): string | undefined {
+ if (!value) return undefined;
+ const parsed = new Date(/^\d{4}-\d{2}-\d{2}$/.test(value) ? `${value}T00:00:00Z` : value);
+ if (Number.isNaN(parsed.getTime())) return value;
+ return parsed.toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ timeZone: 'UTC',
+ });
+}
+
+/**
+ * Per-post Open Graph card: the blog variant (title, author, date, reading
+ * time). Served as a `.png` route handler so the URL ends in a real image
+ * extension. `app/blog/[slug]/page.tsx` points its `openGraph.images` here
+ * (unless the post declares its own `coverImage`).
+ */
+export async function GET(_request: Request, { params }: RouteContext) {
+ const { slug } = await params;
+ const post = getPost(slug);
+
+ if (!post) {
+ notFound();
+ }
+
+ const { fonts, headingFamily, bodyFamily } = await loadBrandFonts();
+ const { frontmatter } = post;
+
+ return new ImageResponse(
+ ,
+ {
+ ...OG_SIZE,
+ ...(fonts.length > 0 ? { fonts } : {}),
+ }
+ );
+}
diff --git a/web/app/blog/[slug]/page.tsx b/web/app/blog/[slug]/page.tsx
new file mode 100644
index 00000000..ebe376a2
--- /dev/null
+++ b/web/app/blog/[slug]/page.tsx
@@ -0,0 +1,362 @@
+import type { Metadata } from 'next';
+import Link from 'next/link';
+import { notFound } from 'next/navigation';
+import { evaluate } from '@mdx-js/mdx';
+import { CalendarDays, Clock3, Rss } from 'lucide-react';
+import { Fragment, isValidElement, type HTMLAttributes, type ReactNode } from 'react';
+import { FaGithub, FaLinkedinIn, FaXTwitter } from 'react-icons/fa6';
+import { jsx, jsxs } from 'react/jsx-runtime';
+import remarkGfm from 'remark-gfm';
+
+import { BlogTableOfContents } from '../../../components/blog/BlogTableOfContents';
+import styles from '../../../components/blog/blog.module.css';
+import { HighlightedPre } from '../../../components/docs/HighlightedCode';
+import { GitHubStarsBadge } from '../../../components/GitHubStars';
+import { Waitlist } from '../../../components/home';
+import { SiteFooter } from '../../../components/SiteFooter';
+import { SiteNav } from '../../../components/SiteNav';
+import { getAllPosts, getPost, slugifyHeading } from '../../../lib/blog';
+import { getAuthorInitials, getBlogAuthor } from '../../../lib/blog-authors';
+import { OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH } from '../../../lib/og-meta';
+import { absoluteUrl, SITE_NAME, SITE_URL } from '../../../lib/site';
+
+type PageProps = {
+ params: Promise<{ slug: string }>;
+};
+
+function extractText(node: ReactNode): string {
+ if (typeof node === 'string' || typeof node === 'number') {
+ return String(node);
+ }
+
+ if (Array.isArray(node)) {
+ return node.map((item) => extractText(item)).join('');
+ }
+
+ if (isValidElement<{ children?: ReactNode }>(node)) {
+ return extractText(node.props.children);
+ }
+
+ return '';
+}
+
+function HeadingWithId(level: 2 | 3) {
+ return function Heading({ children, ...props }: HTMLAttributes) {
+ const id = slugifyHeading(extractText(children));
+ const Tag = `h${level}` as const;
+
+ return (
+
+ {children}
+
+ );
+ };
+}
+
+const mdxComponents = {
+ pre: HighlightedPre,
+ h2: HeadingWithId(2),
+ h3: HeadingWithId(3),
+};
+
+export async function generateStaticParams() {
+ return getAllPosts().map((post) => ({ slug: post.slug }));
+}
+
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { slug } = await params;
+ const post = getPost(slug);
+ if (!post) return { title: 'Not Found' };
+ const postUrl = absoluteUrl(`/blog/${slug}`);
+ const usingGeneratedCard = !post.frontmatter.coverImage;
+ const imageUrl = post.frontmatter.coverImage || absoluteUrl(`/blog/${slug}/og.png`);
+
+ return {
+ title: post.frontmatter.title,
+ description: post.frontmatter.description,
+ keywords: [post.frontmatter.category, ...post.frontmatter.tags],
+ authors: [{ name: post.frontmatter.author }],
+ alternates: {
+ canonical: postUrl,
+ },
+ openGraph: {
+ title: post.frontmatter.title,
+ description: post.frontmatter.description,
+ url: postUrl,
+ type: 'article',
+ publishedTime: post.frontmatter.date,
+ modifiedTime: post.frontmatter.updatedAt ?? post.frontmatter.date,
+ authors: [post.frontmatter.author],
+ section: post.frontmatter.category,
+ tags: post.frontmatter.tags,
+ images: [
+ {
+ url: imageUrl,
+ alt: post.frontmatter.coverImageAlt || `${post.frontmatter.title} social card`,
+ ...(usingGeneratedCard
+ ? { width: OG_IMAGE_WIDTH, height: OG_IMAGE_HEIGHT, type: 'image/png' as const }
+ : {}),
+ },
+ ],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: post.frontmatter.title,
+ description: post.frontmatter.description,
+ images: [imageUrl],
+ },
+ };
+}
+
+function formatDate(dateStr: string): string {
+ const [year, month, day] = dateStr.split('-').map(Number);
+
+ return new Date(year, month - 1, day).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ });
+}
+
+function formatFooterReadTime(readTime: string): string {
+ return readTime.replace('min read', 'minute read');
+}
+
+export default async function BlogPostPage({ params }: PageProps) {
+ const { slug } = await params;
+ const post = getPost(slug);
+ if (!post) notFound();
+
+ const author = getBlogAuthor(post.frontmatter.author);
+ const otherPosts = getAllPosts()
+ .filter((candidate) => candidate.slug !== post.slug)
+ .slice(0, 4);
+ const postUrl = absoluteUrl(`/blog/${slug}`);
+ const imageUrl = post.frontmatter.coverImage || absoluteUrl(`/blog/${slug}/og.png`);
+ const structuredData = {
+ '@context': 'https://schema.org',
+ '@type': 'BlogPosting',
+ headline: post.frontmatter.title,
+ description: post.frontmatter.description,
+ datePublished: post.frontmatter.date,
+ dateModified: post.frontmatter.updatedAt ?? post.frontmatter.date,
+ author: {
+ '@type': 'Person',
+ name: author.name,
+ jobTitle: author.title,
+ ...(author.image ? { image: absoluteUrl(author.image) } : {}),
+ },
+ publisher: {
+ '@type': 'Organization',
+ name: SITE_NAME,
+ url: SITE_URL,
+ logo: {
+ '@type': 'ImageObject',
+ url: absoluteUrl('/favicon.svg'),
+ },
+ },
+ image: [imageUrl],
+ mainEntityOfPage: postUrl,
+ articleSection: post.frontmatter.category,
+ keywords: post.frontmatter.tags.join(', '),
+ about: post.frontmatter.tags.map((tag) => ({ '@type': 'Thing', name: tag })),
+ };
+ const breadcrumbData = {
+ '@context': 'https://schema.org',
+ '@type': 'BreadcrumbList',
+ itemListElement: [
+ {
+ '@type': 'ListItem',
+ position: 1,
+ name: 'Blog',
+ item: absoluteUrl('/blog'),
+ },
+ {
+ '@type': 'ListItem',
+ position: 2,
+ name: post.frontmatter.title,
+ item: postUrl,
+ },
+ ],
+ };
+
+ const { default: MDXContent } = await evaluate(post.content, {
+ Fragment,
+ jsx,
+ jsxs,
+ remarkPlugins: [remarkGfm],
+ } as Parameters[1]);
+ const authorInitials = getAuthorInitials(author.name);
+ const renderAuthorPanel = (headingId: string) => (
+
+
+ Written by
+
+
+
+ {author.image ? : authorInitials}
+
+
+ {author.name}
+ {author.title}
+ {(author.social?.linkedin || author.social?.x || author.social?.github) && (
+
+ {author.social.linkedin && (
+
+
+
+ )}
+ {author.social.x && (
+
+
+
+ )}
+ {author.social.github && (
+
+
+
+ )}
+
+ )}
+
+
+
+
+
+ {formatDate(post.frontmatter.date)}
+
+
+
+ {post.readTime}
+
+
+
+ );
+
+ return (
+
+
} />
+
+
+
+
{post.frontmatter.category}
+
{post.frontmatter.title}
+
{post.frontmatter.description}
+
+
+
+
+
+
+
+
+
+
+
+ {renderAuthorPanel('written-by-heading')}
+
+ {post.toc.length > 0 && (
+
+ )}
+
+
+
+
+
{renderAuthorPanel('mobile-written-by-heading')}
+
+ {post.toc.length > 0 && (
+
+ )}
+
+
+
+
+
+
+
+
+ {otherPosts.length > 0 && (
+
+
+
+
+
+ {otherPosts.map((relatedPost) => (
+
+
+
+ {relatedPost.frontmatter.title}
+
+ By {relatedPost.frontmatter.author} - {formatFooterReadTime(relatedPost.readTime)}
+
+
+
+ {formatDate(relatedPost.frontmatter.date)}
+
+
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/web/app/blog/page.tsx b/web/app/blog/page.tsx
new file mode 100644
index 00000000..09d66b6c
--- /dev/null
+++ b/web/app/blog/page.tsx
@@ -0,0 +1,156 @@
+import type { Metadata } from 'next';
+import Link from 'next/link';
+import { Rss } from 'lucide-react';
+
+import styles from '../../components/blog/blog.module.css';
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { getAllPosts } from '../../lib/blog';
+import { getAuthorInitials, getBlogAuthor } from '../../lib/blog-authors';
+import { defaultOgImage } from '../../lib/og-meta';
+import { absoluteUrl, SITE_NAME, SITE_URL } from '../../lib/site';
+
+export const metadata: Metadata = {
+ title: 'Blog',
+ description: 'News, tutorials, and insights from the Agent Relay team.',
+ alternates: {
+ canonical: absoluteUrl('/blog'),
+ },
+ openGraph: {
+ title: `${SITE_NAME} Blog`,
+ description: 'Essays, playbooks, and product thinking on multi-agent systems and AI coordination.',
+ url: absoluteUrl('/blog'),
+ type: 'website',
+ images: [defaultOgImage()],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: `${SITE_NAME} Blog`,
+ description: 'Essays, playbooks, and product thinking on multi-agent systems and AI coordination.',
+ images: [defaultOgImage().url],
+ },
+};
+
+function formatDate(dateStr: string): string {
+ const [year, month, day] = dateStr.split('-').map(Number);
+
+ return new Date(year, month - 1, day).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ });
+}
+
+export default function BlogIndexPage() {
+ const posts = getAllPosts();
+ const allPosts = posts;
+ const structuredData = {
+ '@context': 'https://schema.org',
+ '@type': 'CollectionPage',
+ name: `${SITE_NAME} Blog`,
+ url: absoluteUrl('/blog'),
+ description: 'Essays, playbooks, and product thinking on multi-agent systems and AI coordination.',
+ isPartOf: SITE_URL,
+ mainEntity: {
+ '@type': 'ItemList',
+ itemListElement: posts.map((post, index) => ({
+ '@type': 'ListItem',
+ position: index + 1,
+ name: post.frontmatter.title,
+ url: absoluteUrl(`/blog/${post.slug}`),
+ })),
+ },
+ };
+
+ return (
+
+
} />
+
+
+
+
+
+
+
Human thoughts on agentic software
+
+ Essays, playbooks, and product thinking on multi-agent systems, context rails, and autonomous
+ development.
+
+
+
+
+
+ {allPosts.length > 0 && (
+
+
+
+
+ {allPosts.map((post) => {
+ const postAuthor = getBlogAuthor(post.frontmatter.author);
+
+ return (
+
+
+
+
{post.frontmatter.title}
+
+
+ {postAuthor.image ? (
+
+ ) : (
+ getAuthorInitials(postAuthor.name)
+ )}
+
+
+ {postAuthor.name}
+
+
+ {formatDate(post.frontmatter.date)}
+
+
+ ·
+
+ {post.readTime}
+
+
+
+
+ {post.frontmatter.description}
+
+
+ );
+ })}
+
+
+ )}
+
+
+
+
+ );
+}
diff --git a/web/app/brand/BrandShowcase.tsx b/web/app/brand/BrandShowcase.tsx
new file mode 100644
index 00000000..1d0ca33b
--- /dev/null
+++ b/web/app/brand/BrandShowcase.tsx
@@ -0,0 +1,96 @@
+import Link from 'next/link';
+import { Download } from 'lucide-react';
+
+import s from './brand.module.css';
+
+type BrandKitAsset = {
+ file: string;
+ label: string;
+ darkPreview?: boolean;
+ altFile?: string;
+};
+
+const BRAND_KIT: BrandKitAsset[] = [
+ { file: 'agent-relay-logo.png', label: 'Logo' },
+ { file: 'agent-relay-logo-light.png', label: 'Logo · Light', darkPreview: true },
+ { file: 'agent-relay-logo-tight.png', label: 'Logo · Tight' },
+ { file: 'agent-relay-logo-light-tight.png', label: 'Logo · Light, Tight', darkPreview: true },
+ { file: 'agent-relay-logo-horizontal.png', label: 'Logo · Horizontal' },
+ { file: 'agent-relay-logo-horizontal-transparent.png', label: 'Logo · Horizontal, Transparent' },
+ { file: 'agent-relay-logo-light-horizontal.png', label: 'Logo · Light, Horizontal', darkPreview: true },
+ {
+ file: 'agent-relay-logo-light-horizontal-transparent.png',
+ label: 'Logo · Light, Horizontal, Transparent',
+ },
+ { file: 'agent-relay-logo-circle.png', label: 'Logo · Circle' },
+ { file: 'agent-relay-logo-light-circle.png', label: 'Logo · Light, Circle', darkPreview: true },
+ { file: 'agent-relay-mark.png', label: 'Mark' },
+ { file: 'agent-relay-mark-transparent.png', label: 'Mark · Transparent', altFile: 'agent-relay-mark.svg' },
+ { file: 'agent-relay-mark-circle.png', label: 'Mark · Circle' },
+ { file: 'agent-relay-mark-light-circle.png', label: 'Mark · Light, Circle', darkPreview: true },
+ { file: 'agent-relay-wordmark.svg', label: 'Wordmark · SVG', darkPreview: true },
+];
+
+export function BrandShowcase() {
+ return (
+
+
+
+
+
+ {BRAND_KIT.map((asset) => (
+
+
+
+
+
+
+
{asset.label}
+
{asset.file}
+
+
+
+
+ ))}
+
+
+
+
+
+ Explore the Agent Relay web theme →
+
+
+
+ );
+}
diff --git a/web/app/brand/brand.module.css b/web/app/brand/brand.module.css
new file mode 100644
index 00000000..ff417410
--- /dev/null
+++ b/web/app/brand/brand.module.css
@@ -0,0 +1,1223 @@
+.page {
+ min-height: 100vh;
+ background:
+ radial-gradient(
+ circle at top left,
+ color-mix(in srgb, var(--primary) 14%, transparent) 0%,
+ transparent 34%
+ ),
+ linear-gradient(180deg, var(--hero-gradient) 0%, var(--bg) 36%, var(--bg) 100%);
+ color: var(--fg);
+ padding-bottom: 96px;
+}
+
+.hero,
+.section {
+ width: min(1080px, calc(100% - 24px));
+ margin: 0 auto;
+}
+
+.hero {
+ padding: 64px 0 16px;
+}
+
+.heroIntro,
+.paletteCard,
+.heroCard,
+.tokenCard,
+.accentCard,
+.previewCard,
+.snippet {
+ border: 1px solid var(--card-border);
+ background: var(--card-bg);
+ backdrop-filter: blur(18px);
+ -webkit-backdrop-filter: blur(18px);
+ box-shadow: 0 14px 40px color-mix(in srgb, var(--fg) 7%, transparent);
+}
+
+.heroIntro,
+.paletteCard,
+.heroCard,
+.previewCard,
+.snippet {
+ border-radius: 24px;
+}
+
+.heroIntro {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 16px;
+ padding: 22px 24px;
+}
+
+.eyebrow,
+.sectionEyebrow {
+ font-size: 0.78rem;
+ font-weight: 700;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.title {
+ margin-top: 10px;
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2.4rem, 4.8vw, 4rem);
+ line-height: 1;
+ letter-spacing: -0.05em;
+}
+
+.lead,
+.sectionText,
+.tokenUse,
+.previewText,
+.chartCaption {
+ color: var(--fg-muted);
+ line-height: 1.65;
+}
+
+.lead {
+ margin-top: 14px;
+ max-width: 60ch;
+ font-size: 0.94rem;
+}
+
+.heroFacts {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+.factPill {
+ display: inline-flex;
+ align-items: center;
+ padding: 8px 12px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--bg-elevated);
+ color: var(--primary);
+ font-size: 0.8rem;
+ font-weight: 700;
+}
+
+.heroBoard {
+ display: grid;
+ grid-template-columns: 296px repeat(3, minmax(0, 1fr));
+ grid-auto-rows: minmax(152px, auto);
+ gap: 12px;
+ margin-top: 12px;
+}
+
+.heroPaletteColumn {
+ display: grid;
+ gap: 12px;
+ grid-row: span 3;
+}
+
+.paletteCard,
+.heroCard {
+ overflow: hidden;
+}
+
+.paletteTop {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 12px;
+ min-height: 108px;
+ padding: 14px 16px;
+ color: rgba(0, 0, 0, 0.78);
+ font-size: 0.88rem;
+ font-weight: 700;
+}
+
+.paletteTopLight {
+ color: rgba(255, 255, 255, 0.92);
+}
+
+.paletteTop code {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ font-weight: 500;
+}
+
+/* Fallback scale using color-mix (themes without explicit steps) */
+.paletteScale {
+ display: grid;
+ grid-template-columns: repeat(8, 1fr);
+ gap: 0;
+ padding: 0 0 10px;
+ background: color-mix(in srgb, var(--bg) 78%, black);
+}
+
+.paletteScale span {
+ height: 54px;
+ background: color-mix(in srgb, var(--tone) calc(12% + var(--step, 0) * 10%), white);
+}
+
+.paletteScale span:nth-child(1) {
+ background: color-mix(in srgb, var(--tone) 6%, black);
+}
+.paletteScale span:nth-child(2) {
+ background: color-mix(in srgb, var(--tone) 18%, black);
+}
+.paletteScale span:nth-child(3) {
+ background: color-mix(in srgb, var(--tone) 30%, white);
+}
+.paletteScale span:nth-child(4) {
+ background: color-mix(in srgb, var(--tone) 44%, white);
+}
+.paletteScale span:nth-child(5) {
+ background: color-mix(in srgb, var(--tone) 58%, white);
+}
+.paletteScale span:nth-child(6) {
+ background: color-mix(in srgb, var(--tone) 72%, white);
+}
+.paletteScale span:nth-child(7) {
+ background: color-mix(in srgb, var(--tone) 84%, white);
+}
+.paletteScale span:nth-child(8) {
+ background: color-mix(in srgb, var(--tone) 92%, white);
+}
+
+/* Explicit scale strip (themes with defined steps like 50–950) */
+.paletteScaleExplicit {
+ display: grid;
+ grid-template-columns: repeat(11, 1fr);
+ gap: 0;
+}
+
+.paletteScaleExplicit span {
+ height: 54px;
+}
+
+.heroCard {
+ padding: 18px;
+ display: grid;
+ align-items: center;
+ background: color-mix(in srgb, var(--card-bg) 92%, var(--bg));
+}
+
+.heroCardLabel {
+ align-self: start;
+ font-size: 0.82rem;
+ font-weight: 600;
+ color: var(--fg-faint);
+}
+
+.typeCard,
+.bodyCard,
+.logoBoardCard {
+ align-items: start;
+}
+
+.typeSpecimen,
+.bodySpecimen {
+ display: grid;
+ place-items: center;
+ height: 100%;
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(4rem, 8vw, 5.6rem);
+ line-height: 0.9;
+ letter-spacing: -0.06em;
+}
+
+.typeSpecimen {
+ color: var(--fg);
+}
+
+.bodySpecimen {
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 1rem;
+ line-height: 1.6;
+ letter-spacing: normal;
+ color: var(--fg-muted);
+}
+
+.buttonMatrix {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.brandCtaPrimary,
+.brandCtaSecondary {
+ min-height: 48px;
+ border-radius: 999px !important;
+ font-size: 0.92rem;
+ font-weight: 600;
+ letter-spacing: 0.01em;
+ transition:
+ background 0.2s ease,
+ border-color 0.2s ease,
+ color 0.2s ease,
+ transform 0.15s ease,
+ box-shadow 0.2s ease !important;
+}
+
+.brandCtaPrimary {
+ border: none !important;
+ background: var(--btn-bg) !important;
+ color: var(--primary-fg) !important;
+ box-shadow: 0 2px 12px color-mix(in srgb, var(--btn-bg) 25%, transparent) !important;
+}
+
+.brandCtaPrimary:hover,
+.brandCtaPrimary[data-state='hover'] {
+ background: var(--btn-hover) !important;
+ box-shadow: 0 2px 12px color-mix(in srgb, var(--btn-bg) 25%, transparent) !important;
+ transform: translateY(-1px);
+}
+
+.brandCtaPrimary[data-state='active'] {
+ background: color-mix(in srgb, var(--btn-hover) 88%, black 12%) !important;
+ box-shadow: 0 1px 8px color-mix(in srgb, var(--btn-bg) 24%, transparent) !important;
+ transform: translateY(0.5px) scale(0.985);
+}
+
+.brandCtaSecondary {
+ background: var(--secondary-surface) !important;
+ color: var(--fg) !important;
+ border: 1.5px solid var(--line) !important;
+}
+
+.brandCtaSecondary:hover,
+.brandCtaSecondary[data-state='hover'] {
+ border-color: var(--primary) !important;
+ color: var(--primary) !important;
+ background: color-mix(in srgb, var(--secondary-surface) 82%, var(--primary) 18%) !important;
+ transform: translateY(-1px);
+}
+
+.brandCtaSecondary[data-state='active'] {
+ border-color: color-mix(in srgb, var(--primary) 82%, black 18%) !important;
+ color: var(--primary) !important;
+ background: color-mix(in srgb, var(--secondary-surface) 72%, var(--primary) 28%) !important;
+ transform: translateY(0.5px) scale(0.985);
+}
+
+:global(html[data-theme='dark']) .brandCtaSecondary {
+ background: color-mix(in srgb, var(--surface-strong) 88%, var(--primary) 12%) !important;
+ border-color: color-mix(in srgb, var(--primary) 18%, transparent) !important;
+ color: var(--fg) !important;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.03),
+ 0 1px 0 rgba(0, 0, 0, 0.14) !important;
+}
+
+:global(html[data-theme='dark']) .brandCtaSecondary:hover,
+:global(html[data-theme='dark']) .brandCtaSecondary[data-state='hover'] {
+ background: color-mix(in srgb, var(--surface-strong) 76%, var(--primary) 24%) !important;
+ border-color: color-mix(in srgb, var(--primary) 36%, transparent) !important;
+ color: #d9efff !important;
+}
+
+:global(html[data-theme='dark']) .brandCtaSecondary[data-state='active'] {
+ background: color-mix(in srgb, var(--surface-strong) 68%, var(--primary) 32%) !important;
+ border-color: color-mix(in srgb, var(--primary) 48%, transparent) !important;
+}
+
+.section {
+ padding: 8px 0 28px;
+}
+
+.sectionHeader {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 16px;
+ margin-bottom: 14px;
+}
+
+.sectionTitle {
+ margin-top: 6px;
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.35rem, 2.6vw, 1.9rem);
+ letter-spacing: -0.04em;
+}
+
+.sectionText {
+ margin-top: 6px;
+ max-width: 72ch;
+ font-size: 0.95rem;
+}
+
+.pathPill {
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--bg-elevated);
+ padding: 8px 12px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ color: var(--primary);
+ text-decoration: none;
+}
+
+.searchField {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ height: 48px;
+ padding: 0 16px;
+ border: 1px solid var(--line);
+ border-radius: 12px;
+ background: var(--bg-elevated);
+ color: var(--fg-faint);
+ font-size: 0.88rem;
+ transition:
+ border-color 0.2s ease,
+ box-shadow 0.2s ease;
+}
+
+.searchField:hover {
+ border-color: color-mix(in srgb, var(--primary) 28%, transparent);
+}
+
+.searchField:focus-within {
+ border-color: color-mix(in srgb, var(--primary) 40%, transparent);
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 8%, transparent);
+}
+
+.searchField svg {
+ flex-shrink: 0;
+ opacity: 0.5;
+}
+
+.searchField input {
+ height: auto;
+ border: 0;
+ background: transparent;
+ padding: 0;
+ color: var(--fg);
+ font-size: inherit;
+}
+
+.searchField input:focus-visible {
+ border: 0;
+ box-shadow: none;
+}
+
+.searchField svg,
+.navPill svg,
+.iconChip svg,
+.iconChipDanger svg {
+ width: 18px;
+ height: 18px;
+}
+
+.rhythmLines {
+ display: grid;
+ gap: 14px;
+}
+
+.rhythmLines span {
+ height: 8px;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--fg) 10%, transparent);
+}
+
+.rhythmLinePrimary {
+ width: 76%;
+ background: color-mix(in srgb, var(--primary) 82%, white) !important;
+}
+
+.rhythmLineSecondary {
+ width: 92%;
+ background: color-mix(in srgb, var(--primary) 50%, white) !important;
+}
+
+.rhythmLineTertiary {
+ width: 58%;
+ background: color-mix(in srgb, var(--primary) 24%, white) !important;
+}
+
+.navPill {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ border-radius: 999px;
+ padding: 12px 16px;
+ background: color-mix(in srgb, var(--fg) 4%, var(--bg));
+}
+
+.navPillActive,
+.navPillIcon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 42px;
+ height: 42px;
+ border-radius: 0;
+ color: var(--fg-muted);
+}
+
+.navPillActive {
+ background: color-mix(in srgb, var(--primary) 24%, white);
+ color: color-mix(in srgb, var(--primary) 55%, black);
+}
+
+.heroLogoCard {
+ grid-column: 2 / -1;
+ padding: 18px;
+ display: grid;
+ grid-template-rows: 1fr auto;
+ gap: 12px;
+ align-items: stretch;
+}
+
+.heroLogoSurface {
+ display: grid;
+ place-items: center;
+ min-height: 140px;
+ padding: 18px;
+ border-radius: 12px;
+ border: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
+ background:
+ radial-gradient(
+ circle at top left,
+ color-mix(in srgb, var(--primary) 10%, transparent) 0%,
+ transparent 35%
+ ),
+ var(--section-bg);
+ color: var(--primary);
+}
+
+.heroLogoLockup {
+ display: inline-flex;
+ align-items: center;
+ gap: 14px;
+}
+
+.heroLogoLockup :global(svg:first-child) {
+ width: 46px;
+ height: auto;
+}
+
+.heroLogoLockup :global(svg:last-child) {
+ width: min(100%, 220px);
+ height: auto;
+}
+
+.heroLogoMeta {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.heroLogoHexes {
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+}
+
+.heroLogoHex {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.heroLogoHexChip {
+ width: 12px;
+ height: 12px;
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
+ flex-shrink: 0;
+}
+
+.logoBoardLockup {
+ display: inline-flex;
+ align-items: center;
+ gap: 14px;
+ color: var(--primary);
+ margin-top: auto;
+}
+
+.logoBoardLockup :global(svg:first-child) {
+ width: 42px;
+ height: auto;
+}
+
+.logoBoardLockup :global(svg:last-child) {
+ width: min(100%, 210px);
+ height: auto;
+}
+
+.iconRow {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+}
+
+.iconChip,
+.iconChipDanger {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 44px;
+ height: 44px;
+ border-radius: 0;
+}
+
+.iconChip {
+ background: color-mix(in srgb, var(--primary) 18%, white);
+ color: color-mix(in srgb, var(--primary) 58%, black);
+}
+
+.iconChipDanger {
+ background: color-mix(in srgb, #ffb4b4 68%, white);
+ color: #8d2a2a;
+}
+
+.tokenGrid,
+.accentGrid,
+.previewGrid {
+ display: grid;
+ gap: 14px;
+}
+
+.tokenGrid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.accentGrid {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.previewGrid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.tokenCard,
+.accentCard {
+ border-radius: 18px;
+ padding: 12px;
+}
+
+.tokenSwatch {
+ height: 90px;
+ border-radius: 12px;
+ border: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
+}
+
+.tokenMeta {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 12px;
+ margin-top: 10px;
+}
+
+.tokenName {
+ font-size: 0.9rem;
+ font-weight: 700;
+ color: var(--fg);
+}
+
+.tokenUse {
+ margin-top: 3px;
+ font-size: 0.8rem;
+}
+
+.tokenValue {
+ white-space: nowrap;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ color: var(--primary);
+}
+
+.accentCard {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+}
+
+/* ── Theme page link (on /brand) ── */
+
+.themeLinkSection {
+ width: min(1080px, calc(100% - 24px));
+ margin: 56px auto 0;
+ padding: 20px 0 0;
+ display: flex;
+ justify-content: center;
+ border-top: 1px solid var(--line);
+}
+
+.themeLink {
+ color: var(--fg-muted);
+ font-size: 0.88rem;
+ letter-spacing: 0.01em;
+ text-decoration: none;
+ transition: color 0.2s ease;
+}
+
+.themeLink:hover {
+ color: var(--primary);
+}
+
+/* ── Brand kit ── */
+
+.kitGrid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 14px;
+}
+
+.kitCard {
+ border: 1px solid var(--card-border);
+ background: var(--card-bg);
+ backdrop-filter: blur(18px);
+ -webkit-backdrop-filter: blur(18px);
+ border-radius: 18px;
+ padding: 12px;
+ box-shadow: 0 14px 40px color-mix(in srgb, var(--fg) 7%, transparent);
+}
+
+.kitPreview {
+ display: grid;
+ place-items: center;
+ aspect-ratio: 16 / 10;
+ padding: 18px;
+ border-radius: 12px;
+ border: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
+ background:
+ linear-gradient(
+ 45deg,
+ color-mix(in srgb, var(--fg) 5%, transparent) 25%,
+ transparent 25%,
+ transparent 75%,
+ color-mix(in srgb, var(--fg) 5%, transparent) 75%
+ ),
+ linear-gradient(
+ 45deg,
+ color-mix(in srgb, var(--fg) 5%, transparent) 25%,
+ transparent 25%,
+ transparent 75%,
+ color-mix(in srgb, var(--fg) 5%, transparent) 75%
+ ),
+ var(--bg-elevated, var(--bg));
+ background-size: 20px 20px;
+ background-position:
+ 0 0,
+ 10px 10px;
+ overflow: hidden;
+}
+
+.kitPreviewDark {
+ background:
+ radial-gradient(
+ circle at top left,
+ color-mix(in srgb, var(--primary) 28%, transparent) 0%,
+ transparent 50%
+ ),
+ #111827;
+ border-color: rgba(255, 255, 255, 0.08);
+}
+
+.kitPreview img {
+ max-width: 100%;
+ max-height: 100%;
+ width: auto;
+ height: auto;
+ object-fit: contain;
+}
+
+.kitMeta {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 12px;
+ margin-top: 12px;
+}
+
+.kitFile {
+ display: block;
+ margin-top: 3px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ color: var(--fg-faint);
+ word-break: break-all;
+}
+
+.kitDownloadRow {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ flex-shrink: 0;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+}
+
+.kitDownloadBtn,
+.kitDownloadAll {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ border-radius: 999px;
+ font-weight: 600;
+ text-decoration: none;
+ transition:
+ background 0.2s ease,
+ border-color 0.2s ease,
+ color 0.2s ease,
+ transform 0.15s ease;
+ white-space: nowrap;
+}
+
+.kitDownloadBtn {
+ padding: 8px 12px;
+ border: 1px solid var(--line);
+ background: var(--bg-elevated);
+ color: var(--fg);
+ font-size: 0.78rem;
+ flex-shrink: 0;
+}
+
+.kitDownloadBtn:hover {
+ border-color: var(--primary);
+ color: var(--primary);
+ transform: translateY(-1px);
+}
+
+.kitDownloadBtn svg {
+ width: 14px;
+ height: 14px;
+}
+
+.kitDownloadAll {
+ padding: 10px 16px;
+ background: var(--btn-bg, var(--primary));
+ color: var(--primary-fg, white);
+ font-size: 0.88rem;
+ box-shadow: 0 2px 12px color-mix(in srgb, var(--primary) 25%, transparent);
+}
+
+.kitDownloadAll:hover {
+ background: var(--btn-hover, var(--primary));
+ transform: translateY(-1px);
+}
+
+.kitDownloadAll svg {
+ width: 16px;
+ height: 16px;
+}
+
+.accentChip {
+ width: 24px;
+ height: 24px;
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
+ flex-shrink: 0;
+ margin-top: 2px;
+}
+
+.previewCard {
+ padding: 24px;
+}
+
+.previewTop,
+.buttonRow,
+.statRow {
+ display: flex;
+ align-items: center;
+}
+
+.previewTop,
+.statRow {
+ justify-content: space-between;
+}
+
+.previewTop {
+ gap: 8px;
+ padding-bottom: 16px;
+ border-bottom: 1px solid var(--line);
+}
+
+.badge,
+.badgeGhost {
+ display: inline-flex;
+ align-items: center;
+ border-radius: 6px;
+ padding: 5px 10px;
+ font-size: 0.75rem;
+ font-weight: 600;
+}
+
+.badge {
+ background: color-mix(in srgb, var(--primary) 12%, transparent);
+ color: var(--primary);
+}
+
+.badgeGhost {
+ background: color-mix(in srgb, var(--fg) 6%, transparent);
+ border: none;
+ color: var(--fg-muted);
+}
+
+.previewTitle {
+ margin-top: 18px;
+ font-size: 1.15rem;
+ font-weight: 650;
+ letter-spacing: -0.02em;
+ color: var(--fg);
+}
+
+.previewText {
+ margin-top: 6px;
+ font-size: 0.88rem;
+ line-height: 1.55;
+ color: var(--fg-muted);
+}
+
+.buttonRow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-top: 20px;
+}
+
+/* ── Button showcase ── */
+
+.buttonShowcase {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.buttonVariantCard {
+ border: 1px solid var(--card-border);
+ background: var(--card-bg);
+ backdrop-filter: blur(18px);
+ -webkit-backdrop-filter: blur(18px);
+ border-radius: 24px;
+ padding: 20px 24px;
+ box-shadow: 0 14px 40px color-mix(in srgb, var(--fg) 7%, transparent);
+}
+
+.buttonVariantLabel {
+ font-size: 0.82rem;
+ font-weight: 700;
+ color: var(--fg);
+ margin-bottom: 16px;
+}
+
+.buttonStateGrid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 14px;
+}
+
+.buttonStateCol {
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ gap: 8px;
+}
+
+.buttonStateLabel {
+ font-size: 0.72rem;
+ font-weight: 500;
+ color: var(--fg-faint);
+ text-align: center;
+}
+
+/* Simulated hover state via data-state */
+.buttonStateCol [data-state='hover'] {
+ transform: translateY(-1px);
+ filter: brightness(0.92);
+}
+
+/* Simulated active state via data-state */
+.buttonStateCol [data-state='active'] {
+ transform: translateY(0.5px) scale(0.98);
+ filter: brightness(0.85);
+}
+
+.badgeRow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-top: 12px;
+}
+
+.inputPreview {
+ position: relative;
+}
+
+.inputPreview input {
+ padding-left: 42px;
+}
+
+.inputPreviewIcon {
+ position: absolute;
+ top: 50%;
+ left: 14px;
+ width: 16px;
+ height: 16px;
+ color: var(--fg-faint);
+ transform: translateY(-50%);
+ pointer-events: none;
+}
+
+.primaryButton,
+.secondaryButton,
+.inverseButton,
+.outlineButton {
+ border-radius: 999px;
+ padding: 12px 16px;
+ font: inherit;
+ font-weight: 600;
+ cursor: pointer;
+ transition: transform 0.15s ease;
+}
+
+.primaryButton:hover,
+.secondaryButton:hover,
+.inverseButton:hover,
+.outlineButton:hover {
+ transform: translateY(-1px);
+}
+
+.primaryButton {
+ border: none;
+ background: var(--btn-bg);
+ color: var(--primary-fg);
+}
+
+.secondaryButton {
+ border: 1px solid var(--line);
+ background: var(--bg);
+ color: var(--fg);
+}
+
+.inverseButton {
+ border: none;
+ background: var(--fg);
+ color: var(--bg);
+}
+
+.outlineButton {
+ border: 1px solid color-mix(in srgb, var(--fg) 22%, transparent);
+ background: transparent;
+ color: var(--fg-muted);
+}
+
+.statLabel,
+.messageAuthor {
+ font-size: 0.78rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ color: var(--primary);
+}
+
+.statValue {
+ margin-top: 8px;
+ font-size: 1.8rem;
+ font-weight: 700;
+ letter-spacing: -0.04em;
+}
+
+.statusDot {
+ width: 10px;
+ height: 10px;
+ border-radius: 999px;
+ background: var(--primary);
+ box-shadow: 0 0 0 6px color-mix(in srgb, var(--primary) 12%, transparent);
+}
+
+.chart {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ align-items: end;
+ gap: 10px;
+ height: 110px;
+ margin-top: 18px;
+}
+
+.chart span {
+ border-radius: 6px 6px 2px 2px;
+ background: linear-gradient(180deg, color-mix(in srgb, var(--primary) 60%, white) 0%, var(--primary) 100%);
+ min-height: 8px;
+}
+
+.chartCaption {
+ margin-top: 14px;
+ font-size: 0.8rem;
+ line-height: 1.5;
+}
+
+.messageList {
+ display: grid;
+ gap: 10px;
+}
+
+.messageBubble,
+.inputShell {
+ border-radius: 14px;
+ border: 1px solid var(--line);
+ background: var(--bg-elevated);
+ padding: 14px 16px;
+}
+
+.messageBody {
+ margin-top: 5px;
+ line-height: 1.55;
+ color: var(--fg-muted);
+ font-size: 0.88rem;
+}
+
+.inputShell {
+ color: var(--fg-faint);
+ font-size: 0.88rem;
+}
+
+.codeCard {
+ background: var(--surface);
+}
+
+.terminalBar {
+ display: flex;
+ gap: 6px;
+}
+
+.dotRed,
+.dotYellow,
+.dotGreen {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+}
+
+.dotRed {
+ background: #ff5f57;
+}
+
+.dotYellow {
+ background: #febc2e;
+}
+
+.dotGreen {
+ background: #28c840;
+}
+
+.codeBlock,
+.snippet {
+ overflow-x: auto;
+ font-family: var(--font-geist-mono), monospace;
+}
+
+.codeBlock {
+ margin-top: 16px;
+ font-size: 0.8rem;
+ line-height: 1.7;
+}
+
+.snippet {
+ padding: 18px;
+ font-size: 0.84rem;
+ line-height: 1.7;
+ color: var(--code-fg);
+ background: var(--surface);
+}
+
+@media (max-width: 980px) {
+ .heroIntro {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .heroBoard {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .heroPaletteColumn {
+ grid-column: 1 / -1;
+ grid-row: auto;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .heroLogoCard {
+ grid-column: 1 / -1;
+ }
+
+ .tokenGrid,
+ .accentGrid,
+ .previewGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .kitGrid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .sectionHeader {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+}
+
+@media (max-width: 720px) {
+ .hero {
+ padding-top: 40px;
+ }
+
+ .heroIntro,
+ .paletteCard,
+ .heroCard,
+ .previewCard,
+ .snippet {
+ border-radius: 18px;
+ }
+
+ .heroIntro,
+ .heroCard,
+ .previewCard,
+ .snippet {
+ padding: 16px;
+ }
+
+ .heroBoard,
+ .heroPaletteColumn,
+ .buttonMatrix {
+ grid-template-columns: 1fr;
+ }
+
+ .typeSpecimen,
+ .bodySpecimen {
+ font-size: 4.3rem;
+ }
+
+ .tokenMeta {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .heroLogoMeta {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .buttonRow {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .buttonStateGrid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .kitGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .kitMeta {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+}
diff --git a/web/app/brand/deploy-button/DeployButtonShowcase.tsx b/web/app/brand/deploy-button/DeployButtonShowcase.tsx
new file mode 100644
index 00000000..f1ab73c2
--- /dev/null
+++ b/web/app/brand/deploy-button/DeployButtonShowcase.tsx
@@ -0,0 +1,86 @@
+import Link from 'next/link';
+
+import s from './styles.module.css';
+
+type Variant = {
+ id: string;
+ file: string;
+ title: string;
+ note: string;
+};
+
+const HOST = 'https://agentrelay.net';
+
+const VARIANTS: Variant[] = [
+ {
+ id: 'launch',
+ file: 'launch-agent.svg',
+ title: 'Launch Agent → (primary)',
+ note: 'Full brand lockup with CTA. The canonical button: mark + wordmark + divider + "Launch Agent →" on the baby-blue gradient.',
+ },
+ {
+ id: 'launch-small',
+ file: 'launch-agent_small.svg',
+ title: 'Launch Agent → (small)',
+ note: 'Compact 32px-tall version with just the logomark and CTA — no wordmark. Use in tight spaces, inline next to other badges, or wherever the full brand wordmark would be too heavy.',
+ },
+];
+
+function snippet(file: string): string {
+ const url = `${HOST}/${file}`;
+ return `[](${HOST}/launch?repo=…)`;
+}
+
+export function DeployButtonShowcase() {
+ return (
+
+
+ Brand · Launch Agent Button
+ Launch Agent button
+
+ Embedded by users in their own README files to launch an agent from a repo. Two locked-in variants —
+ full and compact — hosted as SVG at {HOST}/launch-agent.svg and
+ {HOST}/launch-agent_small.svg. SVG renders natively on GitHub; PNG fallbacks can be
+ exported if needed for surfaces that strip SVG.
+
+
+
+
+
+ {VARIANTS.map((v) => (
+
+
+
+
+
+
+
+
+
Light
+
+
+
+
+
+
Dark
+
+
+
+ {snippet(v.file)}
+
+ ))}
+
+
+
+ ← Back to brand kit
+
+
+
+ );
+}
diff --git a/web/app/brand/deploy-button/page.tsx b/web/app/brand/deploy-button/page.tsx
new file mode 100644
index 00000000..0157aa13
--- /dev/null
+++ b/web/app/brand/deploy-button/page.tsx
@@ -0,0 +1,29 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../../../components/GitHubStars';
+import { SiteFooter } from '../../../components/SiteFooter';
+import { SiteNav } from '../../../components/SiteNav';
+import { absoluteUrl } from '../../../lib/site';
+import { DeployButtonShowcase } from './DeployButtonShowcase';
+
+const title = 'Deploy Button';
+const description =
+ 'Design variants for the "Deploy on Agent Relay" embed button. Review on light and dark stages and pick a direction.';
+
+export const metadata: Metadata = {
+ title,
+ description,
+ alternates: {
+ canonical: absoluteUrl('/brand/deploy-button'),
+ },
+};
+
+export default function DeployButtonPage() {
+ return (
+ <>
+ } />
+
+
+ >
+ );
+}
diff --git a/web/app/brand/deploy-button/styles.module.css b/web/app/brand/deploy-button/styles.module.css
new file mode 100644
index 00000000..ee09edad
--- /dev/null
+++ b/web/app/brand/deploy-button/styles.module.css
@@ -0,0 +1,216 @@
+.page {
+ min-height: 100vh;
+ background:
+ radial-gradient(
+ circle at top left,
+ color-mix(in srgb, var(--primary) 14%, transparent) 0%,
+ transparent 34%
+ ),
+ linear-gradient(180deg, var(--hero-gradient) 0%, var(--bg) 36%, var(--bg) 100%);
+ color: var(--fg);
+ padding-bottom: 96px;
+}
+
+.section {
+ width: min(1080px, calc(100% - 24px));
+ margin: 0 auto;
+}
+
+.hero {
+ padding: 64px 0 24px;
+}
+
+.eyebrow {
+ font-size: 0.78rem;
+ font-weight: 700;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.title {
+ margin-top: 10px;
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2rem, 4vw, 3rem);
+ line-height: 1;
+ letter-spacing: -0.04em;
+}
+
+.lead {
+ margin-top: 14px;
+ max-width: 64ch;
+ font-size: 0.96rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+}
+
+.variantGrid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 16px;
+ margin-top: 16px;
+}
+
+.variantCard {
+ border: 1px solid var(--card-border);
+ background: var(--card-bg);
+ backdrop-filter: blur(18px);
+ -webkit-backdrop-filter: blur(18px);
+ border-radius: 22px;
+ padding: 18px;
+ box-shadow: 0 14px 40px color-mix(in srgb, var(--fg) 7%, transparent);
+}
+
+.variantHeader {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 12px;
+ margin-bottom: 14px;
+ flex-wrap: wrap;
+}
+
+.variantTitle {
+ font-size: 1rem;
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ color: var(--fg);
+}
+
+.variantNote {
+ font-size: 0.82rem;
+ color: var(--fg-muted);
+ max-width: 60ch;
+}
+
+.variantId {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ color: var(--fg-faint);
+ background: color-mix(in srgb, var(--fg) 5%, transparent);
+ padding: 4px 8px;
+ border-radius: 6px;
+}
+
+.stage {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 12px;
+ margin-bottom: 14px;
+}
+
+@media (max-width: 720px) {
+ .stage {
+ grid-template-columns: 1fr;
+ }
+}
+
+.stageCell {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 96px;
+ padding: 18px;
+ border-radius: 14px;
+ border: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
+}
+
+.stageLight {
+ background: linear-gradient(180deg, #ffffff 0%, #fafafa 100%);
+}
+
+.stageDark {
+ background:
+ radial-gradient(circle at top left, color-mix(in srgb, #4a90c2 28%, transparent) 0%, transparent 50%),
+ #0d1117;
+ border-color: rgba(255, 255, 255, 0.08);
+}
+
+.stageCell img {
+ max-width: 100%;
+ height: auto;
+ display: block;
+}
+
+.stageLabel {
+ position: absolute;
+ font-size: 0.7rem;
+ font-weight: 600;
+ color: var(--fg-faint);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+}
+
+.stageCellWrap {
+ position: relative;
+}
+
+.stageCellWrap .stageLabel {
+ top: 8px;
+ left: 12px;
+}
+
+.snippet {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.78rem;
+ line-height: 1.6;
+ background: var(--surface);
+ border: 1px solid var(--line);
+ border-radius: 10px;
+ padding: 12px 14px;
+ color: var(--code-fg);
+ overflow-x: auto;
+ white-space: pre;
+ margin: 0;
+}
+
+.notesGrid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 14px;
+ margin-top: 24px;
+}
+
+@media (max-width: 720px) {
+ .notesGrid {
+ grid-template-columns: 1fr;
+ }
+}
+
+.noteCard {
+ border: 1px solid var(--card-border);
+ background: var(--card-bg);
+ border-radius: 18px;
+ padding: 16px 18px;
+}
+
+.noteCard h3 {
+ font-size: 0.92rem;
+ font-weight: 700;
+ margin: 0 0 8px;
+}
+
+.noteCard p {
+ font-size: 0.86rem;
+ line-height: 1.55;
+ color: var(--fg-muted);
+ margin: 0 0 8px;
+}
+
+.noteCard p:last-child {
+ margin: 0;
+}
+
+.backLink {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ margin-top: 32px;
+ font-size: 0.88rem;
+ color: var(--fg-muted);
+ text-decoration: none;
+}
+
+.backLink:hover {
+ color: var(--primary);
+}
diff --git a/web/app/brand/page.tsx b/web/app/brand/page.tsx
new file mode 100644
index 00000000..d84bc87e
--- /dev/null
+++ b/web/app/brand/page.tsx
@@ -0,0 +1,58 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { absoluteUrl } from '../../lib/site';
+import { BrandShowcase } from './BrandShowcase';
+
+const title = 'Brand Kit';
+const description =
+ 'Agent Relay brand assets: downloadable logo, mark, and wordmark PNGs in every variant. Grab the kit as a zip or individual files.';
+
+export const metadata: Metadata = {
+ title,
+ description,
+ keywords: [
+ 'Agent Relay brand',
+ 'Agent Relay logo',
+ 'Agent Relay brand kit',
+ 'Agent Relay press kit',
+ 'Agent Relay color palette',
+ 'Agent Relay design system',
+ ],
+ alternates: {
+ canonical: absoluteUrl('/brand'),
+ },
+ openGraph: {
+ title: `${title} · Agent Relay`,
+ description,
+ url: absoluteUrl('/brand'),
+ type: 'website',
+ images: [
+ {
+ url: absoluteUrl('/brand-kit/agent-relay-logo-circle.png'),
+ width: 1024,
+ height: 1024,
+ type: 'image/png',
+ alt: 'Agent Relay logo',
+ },
+ ],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: `${title} · Agent Relay`,
+ description,
+ images: [absoluteUrl('/brand-kit/agent-relay-logo-circle.png')],
+ },
+};
+
+export default function BrandPage() {
+ return (
+ <>
+ } />
+
+
+ >
+ );
+}
diff --git a/web/app/brand/theme/ThemeShowcase.tsx b/web/app/brand/theme/ThemeShowcase.tsx
new file mode 100644
index 00000000..fa1a3b37
--- /dev/null
+++ b/web/app/brand/theme/ThemeShowcase.tsx
@@ -0,0 +1,568 @@
+'use client';
+
+import type { CSSProperties } from 'react';
+import { Home, Search, Shapes, Tag, Trash2, User, Wand2 } from 'lucide-react';
+
+import { LogoIcon, LogoWordmark } from '../../../components/SiteNav';
+import { Badge } from '../../../components/ui/badge';
+import { Button } from '../../../components/ui/button';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../../components/ui/card';
+import { Input } from '../../../components/ui/input';
+import s from '../brand.module.css';
+
+type ThemeToken = {
+ name: string;
+ value: string;
+ use: string;
+};
+
+type HeroPaletteCard = {
+ label: string;
+ value: string;
+ textLight?: boolean;
+ scale?: string[];
+};
+
+const THEME_TOKENS: ThemeToken[] = [
+ { name: 'bg', value: '#08111A', use: 'Page background' },
+ { name: 'fg', value: '#EDF4FB', use: 'Main text' },
+ { name: 'fg-muted', value: '#A8B8C8', use: 'Secondary text' },
+ { name: 'primary', value: '#74B8E2', use: 'Primary brand color (500)' },
+ { name: 'primary-hover', value: '#94CBEF', use: 'Button hover (600)' },
+ { name: 'primary-ink', value: '#234969', use: 'Deep blue text accent (800)' },
+ { name: 'secondary', value: '#C1674B', use: 'Secondary brand color (500)' },
+ { name: 'surface', value: '#0F1B29', use: 'Panels and code blocks' },
+ { name: 'card-bg', value: 'rgba(15, 27, 41, 0.84)', use: 'Raised cards' },
+];
+
+const HERO_PALETTE: HeroPaletteCard[] = [
+ {
+ label: 'Primary',
+ value: '#4A90C2',
+ textLight: true,
+ scale: [
+ '#F3F7FC',
+ '#E7EFF7',
+ '#C9DDEE',
+ '#99C2E0',
+ '#62A1CE',
+ '#4A90C2',
+ '#2D6A9C',
+ '#26557E',
+ '#234969',
+ '#223E58',
+ '#16283B',
+ ],
+ },
+ {
+ label: 'Secondary',
+ value: '#C1674B',
+ textLight: true,
+ scale: [
+ '#FBF5F1',
+ '#F5E8DF',
+ '#EACEBE',
+ '#DCAD95',
+ '#CD866A',
+ '#C1674B',
+ '#B45542',
+ '#964338',
+ '#793833',
+ '#62302C',
+ '#341716',
+ ],
+ },
+ { label: 'Surface', value: '#F3F4F6' },
+ {
+ label: 'Neutral',
+ value: '#a5836a',
+ textLight: true,
+ scale: [
+ '#f5f2ee',
+ '#efebe5',
+ '#dfd5c9',
+ '#cab9a7',
+ '#b49a83',
+ '#a5836a',
+ '#98735e',
+ '#7f5e4f',
+ '#684e44',
+ '#554139',
+ '#2d211d',
+ ],
+ },
+];
+
+const BRAND_ACCENTS = [
+ { name: 'Logo primary', value: '#4A90C2', use: 'Primary icon mark' },
+ { name: 'Logo secondary', value: 'rgba(74, 144, 194, 0.5)', use: 'Trailing icon facet' },
+ { name: 'Warm accent', value: '#C1674B', use: 'Secondary brand color (500)' },
+ { name: 'Warm accent deep', value: '#B45542', use: 'Secondary hover (600)' },
+ { name: 'Warm accent soft', value: '#CD866A', use: 'Secondary highlight (400)' },
+ { name: 'Status red', value: '#FF5F57', use: 'Terminal chrome' },
+ { name: 'Status yellow', value: '#FEBC2E', use: 'Terminal chrome' },
+ { name: 'Status green', value: '#28C840', use: 'Terminal chrome' },
+];
+
+const THEME_SNIPPET = `:root {
+ /* Surfaces */
+ --bg: #08111A;
+ --bg-elevated: rgba(12, 20, 30, 0.82);
+ --surface: #0F1B29;
+ --surface-strong: #132234;
+ --section-bg: #0B141F;
+
+ /* Text */
+ --fg: #EDF4FB;
+ --fg-muted: #A8B8C8;
+ --fg-faint: #77879A;
+
+ /* Primary */
+ --primary: #74B8E2;
+ --primary-hover: #94CBEF;
+ --primary-bg: #3B789F;
+ --primary-fg: #FFFFFF;
+
+ /* Secondary */
+ --secondary: #C1674B;
+ --secondary-bg: rgba(193, 103, 75, 0.14);
+ --secondary-fg: #F0B39F;
+
+ /* Lines and cards */
+ --line: rgba(116, 184, 226, 0.18);
+ --card-bg: rgba(15, 27, 41, 0.84);
+ --card-border: rgba(116, 184, 226, 0.16);
+ --card-hover-border: rgba(116, 184, 226, 0.32);
+
+ /* Code */
+ --code-bg: #0A182C;
+ --code-fg: #DBE5F0;
+ --inline-code-bg: rgba(116, 184, 226, 0.14);
+ --inline-code-fg: #C6E6FA;
+
+ /* Accents */
+ --note-bg: rgba(116, 184, 226, 0.1);
+ --link-underline: rgba(116, 184, 226, 0.35);
+ --btn-bg: #3B789F;
+ --btn-hover: #4D8FB8;
+}`;
+
+function brandButtonClass(variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive') {
+ if (variant === 'default' || variant === undefined) {
+ return s.brandCtaPrimary;
+ }
+
+ if (variant === 'secondary') {
+ return s.brandCtaSecondary;
+ }
+
+ return '';
+}
+
+export function ThemeShowcase() {
+ return (
+
+
+
+
+
Brand System
+
Color system
+
Palette, type scale, controls, and logo treatment.
+
+
+ /brand.css
+
+
+
+
+
+ {HERO_PALETTE.map((card) => (
+
+
+ {card.label}
+ {card.value}
+
+ {card.scale ? (
+
+ {card.scale.map((hex) => (
+
+ ))}
+
+ ) : (
+
+ {Array.from({ length: 8 }).map((_, index) => (
+
+ ))}
+
+ )}
+
+ ))}
+
+
+
+
+
+
Body
+
+ Spawn, coordinate, and connect AI agents in real-time from TypeScript or Python.
+
+
+
+
+
+
+
+ Primary
+
+
+ Secondary
+
+
+ Outline
+
+
+ Ghost
+
+
+ Destructive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Primary-color logo
+
The mark has two colors. The wordmark is white.
+
+
+
+
+ #75B8E2
+
+
+
+ #3F6A86
+
+
+
+
+
+
+
+
+
+
+
Theme
+
Agent Relay
+
The default public brand: airy blue, crisp ink, clean UI.
+
+
+
+
+ {THEME_TOKENS.map((token) => (
+
+
+
+
+
{token.name}
+
{token.use}
+
+
{token.value}
+
+
+ ))}
+
+
+
+
+
+
+
Shared Accents
+
Brand-supporting colors
+
+ These show up in the logo, illustrations, terminal chrome, and product demo surfaces.
+
+
+
+
+
+ {BRAND_ACCENTS.map((accent) => (
+
+
+
+
{accent.name}
+
{accent.use}
+
{accent.value}
+
+
+ ))}
+
+
+
+
+
+
+
Controls
+
Buttons in every state
+
+ How brand tokens render across primary, secondary, ghost, and destructive controls.
+
+
+
+
+
+ {(
+ [
+ { variant: 'default', label: 'Primary' },
+ { variant: 'secondary', label: 'Secondary' },
+ { variant: 'ghost', label: 'Ghost' },
+ { variant: 'destructive', label: 'Destructive' },
+ ] as const
+ ).map(({ variant, label }) => (
+
+ {label}
+
+
+
+ {label}
+
+ Default
+
+
+
+ {label}
+
+ Hover
+
+
+
+ {label}
+
+ Active
+
+
+
+ {label}
+
+ Disabled
+
+
+
+ ))}
+
+
+
+
+
+ Badges
+ Status and label variants.
+
+
+
+ Active
+ Secondary
+ Outline
+
+
+
+
+
+
+ Input and card
+ Search field and a standard shadcn card shell.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Component Preview
+
Common UI surfaces
+
+ This is the fastest way to judge whether the theme feels usable, not just attractive.
+
+
+
+
+
+
+
+ Relay Ready
+ Live Preview
+
+ Primary actions
+ Buttons, tags, and neutral containers using the semantic palette.
+
+
+ Read the Docs
+
+
+ Open GitHub
+
+
+
+
+
+
+
+
Observer Sessions
+
12,543
+
+
+
+
+
+
+
+
+
+
+
+ Muted data colors derive from the theme palette.
+
+
+
+
+
+
Planner
+
Split the rollout into API, UI, and review lanes.
+
+
+
Builder
+
Theme tokens are wired. Shipping the palette page next.
+
+
Message #brand-review
+
+
+
+
+
+
+
+
+
+
+ {'// semantic token example'}
+ {'\n'}
+ const
+ {' palette = {'}
+ {'\n'}
+ {' '}
+ primary
+ {': '}
+ {`'${THEME_TOKENS.find((t) => t.name === 'primary')?.value}'`}
+ {','}
+ {'\n'}
+ {' '}
+ surface
+ {': '}
+ {`'${THEME_TOKENS.find((t) => t.name === 'surface')?.value}'`}
+ {','}
+ {'\n'}
+ {'};'}
+ {'\n'}
+ export default
+ {' palette;'}
+
+
+
+
+
+
+
+
+
Copy/Paste
+
Theme snippet
+
+ Compact version of the theme tokens for quick copy-paste into another project.
+
+
+
+
+ {THEME_SNIPPET}
+
+
+ );
+}
diff --git a/web/app/brand/theme/page.tsx b/web/app/brand/theme/page.tsx
new file mode 100644
index 00000000..462daf65
--- /dev/null
+++ b/web/app/brand/theme/page.tsx
@@ -0,0 +1,50 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../../../components/GitHubStars';
+import { SiteFooter } from '../../../components/SiteFooter';
+import { SiteNav } from '../../../components/SiteNav';
+import { defaultOgImage } from '../../../lib/og-meta';
+import { absoluteUrl } from '../../../lib/site';
+import { ThemeShowcase } from './ThemeShowcase';
+
+const title = 'Web Theme';
+const description =
+ 'The Agent Relay web theme: full color palette, semantic tokens, type scale, button states, and ready-to-paste CSS snippet.';
+
+export const metadata: Metadata = {
+ title,
+ description,
+ keywords: [
+ 'Agent Relay theme',
+ 'Agent Relay color palette',
+ 'Agent Relay design system',
+ 'Agent Relay CSS tokens',
+ 'Agent Relay semantic colors',
+ ],
+ alternates: {
+ canonical: absoluteUrl('/brand/theme'),
+ },
+ openGraph: {
+ title: `${title} · Agent Relay`,
+ description,
+ url: absoluteUrl('/brand/theme'),
+ type: 'website',
+ images: [defaultOgImage()],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: `${title} · Agent Relay`,
+ description,
+ images: [defaultOgImage().url],
+ },
+};
+
+export default function BrandThemePage() {
+ return (
+ <>
+ } />
+
+
+ >
+ );
+}
diff --git a/web/app/docs/7.1.1/[slug]/page.tsx b/web/app/docs/7.1.1/[slug]/page.tsx
new file mode 100644
index 00000000..4eaefcf0
--- /dev/null
+++ b/web/app/docs/7.1.1/[slug]/page.tsx
@@ -0,0 +1,131 @@
+import type { Metadata } from 'next';
+import type React from 'react';
+import { notFound } from 'next/navigation';
+import { evaluate } from '@mdx-js/mdx';
+import { Fragment } from 'react';
+import { jsx, jsxs } from 'react/jsx-runtime';
+import remarkGfm from 'remark-gfm';
+
+import { BannerLink } from '../../../../components/docs/BannerLink';
+import { Card } from '../../../../components/docs/Card';
+import { CardGroup } from '../../../../components/docs/CardGroup';
+import { CodeGroup } from '../../../../components/docs/CodeGroup';
+import { HighlightedPre } from '../../../../components/docs/HighlightedCode';
+import { LegacySpawnOptionsTable } from '../../../../components/docs/LegacySpawnOptionsTable';
+import { Note } from '../../../../components/docs/Note';
+import { TableOfContents } from '../../../../components/docs/TableOfContents';
+import { Warning } from '../../../../components/docs/Warning';
+import styles from '../../../../components/docs/docs.module.css';
+import { getDoc } from '../../../../lib/docs';
+import { getAllLegacyDocSlugs } from '../../../../lib/docs-nav';
+import { defaultOgImage } from '../../../../lib/og-meta';
+import { absoluteUrl } from '../../../../lib/site';
+
+function slugify(text: string): string {
+ return text
+ .toLowerCase()
+ .replace(/`([^`]+)`/g, '$1')
+ .replace(/[^a-z0-9]+/g, '-')
+ .replace(/^-|-$/g, '');
+}
+
+function HeadingWithId(level: 2 | 3) {
+ return function Heading({ children, ...props }: React.HTMLAttributes) {
+ const text = typeof children === 'string' ? children : String(children);
+ const id = slugify(text);
+ const Tag = `h${level}` as const;
+ return (
+
+ {children}
+
+ );
+ };
+}
+
+const components = {
+ BannerLink,
+ Card,
+ CardGroup,
+ CodeGroup,
+ Note,
+ Warning,
+ SpawnOptionsTable: LegacySpawnOptionsTable,
+ pre: HighlightedPre,
+ h2: HeadingWithId(2),
+ h3: HeadingWithId(3),
+};
+
+type PageProps = {
+ params: Promise<{ slug: string }>;
+};
+
+export async function generateStaticParams() {
+ return getAllLegacyDocSlugs().map((slug) => ({ slug }));
+}
+
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { slug } = await params;
+ const doc = getDoc(slug, 'v7.1.1');
+
+ if (!doc) {
+ return { title: 'Not Found' };
+ }
+
+ return {
+ title: `${doc.frontmatter.title} - Version 7.1.1`,
+ description: doc.frontmatter.description,
+ alternates: {
+ canonical: absoluteUrl(`/docs/7.1.1/${slug}`),
+ },
+ openGraph: {
+ title: `${doc.frontmatter.title} - Version 7.1.1`,
+ description: doc.frontmatter.description,
+ url: absoluteUrl(`/docs/7.1.1/${slug}`),
+ type: 'article',
+ images: [defaultOgImage()],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: `${doc.frontmatter.title} - Version 7.1.1`,
+ description: doc.frontmatter.description,
+ images: [defaultOgImage().url],
+ },
+ };
+}
+
+export default async function LegacyDocsPage({ params }: PageProps) {
+ const { slug } = await params;
+ const doc = getDoc(slug, 'v7.1.1');
+
+ if (!doc) {
+ notFound();
+ }
+
+ const { default: MDXContent } = await evaluate(doc.content, {
+ Fragment,
+ jsx,
+ jsxs,
+ remarkPlugins: [remarkGfm],
+ } as Parameters[1]);
+
+ return (
+
+
+
+
+
{doc.frontmatter.title}
+
+
+ {doc.frontmatter.description && (
+ {doc.frontmatter.description}
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/docs/7.1.1/page.tsx b/web/app/docs/7.1.1/page.tsx
new file mode 100644
index 00000000..5b840232
--- /dev/null
+++ b/web/app/docs/7.1.1/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function LegacyDocsIndexPage() {
+ redirect('/docs/7.1.1/introduction');
+}
diff --git a/web/app/docs/8.0.0/[slug]/page.tsx b/web/app/docs/8.0.0/[slug]/page.tsx
new file mode 100644
index 00000000..c90b1feb
--- /dev/null
+++ b/web/app/docs/8.0.0/[slug]/page.tsx
@@ -0,0 +1,10 @@
+import { redirect } from 'next/navigation';
+
+type PageProps = {
+ params: Promise<{ slug: string }>;
+};
+
+export default async function V8DocsSlugRedirectPage({ params }: PageProps) {
+ const { slug } = await params;
+ redirect(`/docs/${slug}`);
+}
diff --git a/web/app/docs/8.0.0/page.tsx b/web/app/docs/8.0.0/page.tsx
new file mode 100644
index 00000000..72e63f58
--- /dev/null
+++ b/web/app/docs/8.0.0/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function V8DocsRedirectPage() {
+ redirect('/docs/introduction');
+}
diff --git a/web/app/docs/[slug]/og.png/route.tsx b/web/app/docs/[slug]/og.png/route.tsx
new file mode 100644
index 00000000..14427c35
--- /dev/null
+++ b/web/app/docs/[slug]/og.png/route.tsx
@@ -0,0 +1,50 @@
+import { ImageResponse } from 'next/og';
+import { notFound } from 'next/navigation';
+
+import { getDoc } from '../../../../lib/docs';
+import { getAllDocSlugs } from '../../../../lib/docs-nav';
+import { DefaultVariant, loadBrandFonts, OG_SIZE } from '../../../../lib/og/template';
+
+export const runtime = 'nodejs';
+// Prerender one card per doc at build time. The doc set is fixed per deploy.
+export const dynamic = 'force-static';
+
+type RouteContext = {
+ params: Promise<{ slug: string }>;
+};
+
+/** Prerender a card for every current (v8) doc. */
+export function generateStaticParams() {
+ return getAllDocSlugs().map((slug) => ({ slug }));
+}
+
+/**
+ * Per-doc Open Graph card: the default variant with a "Documentation" eyebrow
+ * plus the doc title and description. Served as a `.png` route handler so the
+ * URL ends in a real image extension. `app/docs/[slug]/page.tsx` points its
+ * `openGraph.images` here.
+ */
+export async function GET(_request: Request, { params }: RouteContext) {
+ const { slug } = await params;
+ const doc = getDoc(slug);
+
+ if (!doc) {
+ notFound();
+ }
+
+ const { fonts, headingFamily, bodyFamily } = await loadBrandFonts();
+
+ return new ImageResponse(
+ ,
+ {
+ ...OG_SIZE,
+ ...(fonts.length > 0 ? { fonts } : {}),
+ }
+ );
+}
diff --git a/web/app/docs/[slug]/page.tsx b/web/app/docs/[slug]/page.tsx
new file mode 100644
index 00000000..a5ee7fc4
--- /dev/null
+++ b/web/app/docs/[slug]/page.tsx
@@ -0,0 +1,163 @@
+import type { Metadata } from 'next';
+import type React from 'react';
+import { notFound, redirect } from 'next/navigation';
+import { evaluate } from '@mdx-js/mdx';
+import { Fragment } from 'react';
+import { jsx, jsxs } from 'react/jsx-runtime';
+import remarkGfm from 'remark-gfm';
+
+import { Card } from '../../../components/docs/Card';
+import { CardGroup } from '../../../components/docs/CardGroup';
+import { BannerLink } from '../../../components/docs/BannerLink';
+import { CodeGroup } from '../../../components/docs/CodeGroup';
+import { DocsPageActions } from '../../../components/docs/DocsPageActions';
+import { HighlightedPre } from '../../../components/docs/HighlightedCode';
+import { LegacySpawnOptionsTable } from '../../../components/docs/LegacySpawnOptionsTable';
+import { Note } from '../../../components/docs/Note';
+import { Warning } from '../../../components/docs/Warning';
+import { TableOfContents } from '../../../components/docs/TableOfContents';
+import styles from '../../../components/docs/docs.module.css';
+import { getDoc } from '../../../lib/docs';
+import { getDocMarkdownUrl } from '../../../lib/docs-markdown';
+import { getAllDocSlugs, getAllLegacyDocSlugs } from '../../../lib/docs-nav';
+import { getDefaultDocsVersionForSlug } from '../../../lib/docs-versions';
+import { ogImage } from '../../../lib/og-meta';
+import { absoluteUrl } from '../../../lib/site';
+
+function slugify(text: string): string {
+ return text
+ .toLowerCase()
+ .replace(/`([^`]+)`/g, '$1')
+ .replace(/[^a-z0-9]+/g, '-')
+ .replace(/^-|-$/g, '');
+}
+
+function HeadingWithId(level: 2 | 3) {
+ return function Heading({ children, ...props }: React.HTMLAttributes) {
+ const text = typeof children === 'string' ? children : String(children);
+ const id = slugify(text);
+ const Tag = `h${level}` as const;
+ return (
+
+ {children}
+
+ );
+ };
+}
+
+const components = {
+ CodeGroup,
+ Card,
+ CardGroup,
+ BannerLink,
+ Note,
+ Warning,
+ SpawnOptionsTable: LegacySpawnOptionsTable,
+ pre: HighlightedPre,
+ h2: HeadingWithId(2),
+ h3: HeadingWithId(3),
+};
+
+type PageProps = {
+ params: Promise<{ slug: string }>;
+};
+
+export async function generateStaticParams() {
+ return [...new Set([...getAllLegacyDocSlugs(), ...getAllDocSlugs()])].map((slug) => ({ slug }));
+}
+
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { slug } = await params;
+
+ // Legacy-only slugs live under /docs/7.1.1; point metadata there.
+ if (getDefaultDocsVersionForSlug(slug) === 'v7.1.1') {
+ const legacyDoc = getDoc(slug, 'v7.1.1');
+ const canonical = absoluteUrl(`/docs/7.1.1/${slug}`);
+ return {
+ title: legacyDoc?.frontmatter.title ?? 'Not Found',
+ description: legacyDoc?.frontmatter.description,
+ alternates: { canonical },
+ };
+ }
+
+ const doc = getDoc(slug, 'v8');
+
+ if (!doc) {
+ return { title: 'Not Found' };
+ }
+
+ return {
+ title: doc.frontmatter.title,
+ description: doc.frontmatter.description,
+ alternates: {
+ canonical: absoluteUrl(`/docs/${slug}`),
+ types: { 'text/markdown': getDocMarkdownUrl(slug) },
+ },
+ openGraph: {
+ title: doc.frontmatter.title,
+ description: doc.frontmatter.description,
+ url: absoluteUrl(`/docs/${slug}`),
+ type: 'article',
+ images: [ogImage(`/docs/${slug}/og.png`, `${doc.frontmatter.title} — Agent Relay docs`)],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: doc.frontmatter.title,
+ description: doc.frontmatter.description,
+ images: [absoluteUrl(`/docs/${slug}/og.png`)],
+ },
+ };
+}
+
+export default async function DocsPage({ params }: PageProps) {
+ const { slug } = await params;
+
+ // Legacy-only slugs are served from the v7.1.1 archive, not the bare path.
+ if (getDefaultDocsVersionForSlug(slug) === 'v7.1.1') {
+ redirect(`/docs/7.1.1/${slug}`);
+ }
+
+ const doc = getDoc(slug, 'v8');
+
+ if (!doc) {
+ notFound();
+ }
+
+ const { default: MDXContent } = await evaluate(doc.content, {
+ Fragment,
+ jsx,
+ jsxs,
+ remarkPlugins: [remarkGfm],
+ } as Parameters[1]);
+
+ const pageUrl = absoluteUrl(`/docs/${slug}`);
+ const markdownPath = `/docs/markdown/${slug}.md`;
+ const markdownUrl = getDocMarkdownUrl(slug);
+
+ return (
+
+
+
+
+
{doc.frontmatter.title}
+
+
+
+ {doc.frontmatter.description && (
+ {doc.frontmatter.description}
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/docs/layout.tsx b/web/app/docs/layout.tsx
new file mode 100644
index 00000000..ea02b517
--- /dev/null
+++ b/web/app/docs/layout.tsx
@@ -0,0 +1,34 @@
+import type { ReactNode } from 'react';
+
+import { DocsLanguageProvider } from '../../components/docs/DocsLanguageContext';
+import { DocsNav } from '../../components/docs/DocsNav';
+import { DocsSearch } from '../../components/docs/DocsSearch';
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import styles from '../../components/docs/docs.module.css';
+import { getSearchIndex } from '../../lib/docs';
+
+const searchIndex = getSearchIndex();
+
+export default function DocsLayout({ children }: { children: ReactNode }) {
+ return (
+
+
+
}
+ actions={
}
+ mobileMenuContent={
}
+ hideMobileDocsLink
+ />
+
+
+
+
+ );
+}
diff --git a/web/app/docs/markdown.md/route.ts b/web/app/docs/markdown.md/route.ts
new file mode 100644
index 00000000..5d6e2308
--- /dev/null
+++ b/web/app/docs/markdown.md/route.ts
@@ -0,0 +1,14 @@
+import { getDocsMarkdownIndex } from '../../../lib/docs-markdown';
+
+// ISR so OpenNext serves this from the incremental cache on Cloudflare Workers
+// (force-static would re-read the filesystem per request, unsupported on Workers).
+export const revalidate = 86400;
+
+export async function GET() {
+ return new Response(getDocsMarkdownIndex(), {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Cache-Control': 'public, max-age=86400',
+ },
+ });
+}
diff --git a/web/app/docs/markdown/[...slug]/route.ts b/web/app/docs/markdown/[...slug]/route.ts
new file mode 100644
index 00000000..65c3cdb4
--- /dev/null
+++ b/web/app/docs/markdown/[...slug]/route.ts
@@ -0,0 +1,44 @@
+import { getDocMarkdown } from '../../../../lib/docs-markdown';
+import { getAllDocSlugs } from '../../../../lib/docs-nav';
+
+// Prerender every doc's `.md` mirror and serve it via ISR from the incremental
+// cache on Cloudflare Workers (the handler reads docs from the filesystem, which
+// only works at build time). dynamicParams=false 404s unknown paths instead of
+// re-rendering them at request time. The next.config rewrite maps
+// `/docs/.md` → `/docs/markdown/.md`, so params carry the `.md`.
+export const revalidate = 86400;
+export const dynamicParams = false;
+
+export function generateStaticParams() {
+ // The rewrite only matches single-segment `.md` paths, so skip nested slugs.
+ return getAllDocSlugs()
+ .filter((slug) => !slug.includes('/'))
+ .map((slug) => ({ slug: [`${slug}.md`] }));
+}
+
+type RouteProps = {
+ params: Promise<{ slug: string[] }>;
+};
+
+export async function GET(_request: Request, { params }: RouteProps) {
+ const { slug: segments } = await params;
+ const rawSlug = segments.at(-1) ?? '';
+ const slug = rawSlug.replace(/\.md$/, '');
+
+ if (segments.length !== 1 || !slug) {
+ return new Response('Not found', { status: 404 });
+ }
+
+ const doc = getDocMarkdown(slug);
+
+ if (!doc) {
+ return new Response('Not found', { status: 404 });
+ }
+
+ return new Response(doc.markdown, {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Cache-Control': 'public, max-age=86400',
+ },
+ });
+}
diff --git a/web/app/docs/markdown/route.ts b/web/app/docs/markdown/route.ts
new file mode 100644
index 00000000..5d6e2308
--- /dev/null
+++ b/web/app/docs/markdown/route.ts
@@ -0,0 +1,14 @@
+import { getDocsMarkdownIndex } from '../../../lib/docs-markdown';
+
+// ISR so OpenNext serves this from the incremental cache on Cloudflare Workers
+// (force-static would re-read the filesystem per request, unsupported on Workers).
+export const revalidate = 86400;
+
+export async function GET() {
+ return new Response(getDocsMarkdownIndex(), {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Cache-Control': 'public, max-age=86400',
+ },
+ });
+}
diff --git a/web/app/docs/page.tsx b/web/app/docs/page.tsx
new file mode 100644
index 00000000..fb299d40
--- /dev/null
+++ b/web/app/docs/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function DocsIndexPage() {
+ redirect('/docs/introduction');
+}
diff --git a/web/app/docs/pre-v8/[slug]/page.tsx b/web/app/docs/pre-v8/[slug]/page.tsx
new file mode 100644
index 00000000..70cb5158
--- /dev/null
+++ b/web/app/docs/pre-v8/[slug]/page.tsx
@@ -0,0 +1,10 @@
+import { redirect } from 'next/navigation';
+
+type PageProps = {
+ params: Promise<{ slug: string }>;
+};
+
+export default async function LegacyDocsSlugRedirectPage({ params }: PageProps) {
+ const { slug } = await params;
+ redirect(`/docs/7.1.1/${slug}`);
+}
diff --git a/web/app/docs/pre-v8/page.tsx b/web/app/docs/pre-v8/page.tsx
new file mode 100644
index 00000000..a601b84d
--- /dev/null
+++ b/web/app/docs/pre-v8/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function LegacyDocsRedirectPage() {
+ redirect('/docs/7.1.1/introduction');
+}
diff --git a/web/app/feed.xml/route.ts b/web/app/feed.xml/route.ts
new file mode 100644
index 00000000..a51c51ea
--- /dev/null
+++ b/web/app/feed.xml/route.ts
@@ -0,0 +1,58 @@
+import { getAllPosts } from '../../lib/blog';
+import { absoluteUrl, SITE_EMAIL, SITE_NAME } from '../../lib/site';
+
+function escapeXml(value: string): string {
+ return value
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+export function GET() {
+ const posts = getAllPosts();
+ const latestDate =
+ posts[0]?.frontmatter.updatedAt ?? posts[0]?.frontmatter.date ?? new Date().toISOString();
+ const feedUrl = absoluteUrl('/feed.xml');
+
+ const items = posts
+ .map((post) => {
+ const postUrl = absoluteUrl(`/blog/${post.slug}`);
+ const publishedAt = new Date(post.frontmatter.date).toUTCString();
+
+ return `
+ -
+
${escapeXml(post.frontmatter.title)}
+ ${postUrl}
+ ${postUrl}
+ ${publishedAt}
+ ${escapeXml(post.frontmatter.description)}
+ ${escapeXml(post.frontmatter.author)}
+ ${post.frontmatter.category ? `${escapeXml(post.frontmatter.category)} ` : ''}
+
+ `;
+ })
+ .join('');
+
+ const xml = `
+
+
+ ${SITE_NAME} Blog
+ ${absoluteUrl('/blog')}
+ Essays, playbooks, and product thinking on multi-agent systems and AI coordination.
+ en-us
+ ${new Date(latestDate).toUTCString()}
+
+ ${escapeXml(SITE_EMAIL)}
+ ${items}
+
+ `;
+
+ return new Response(xml, {
+ headers: {
+ 'Content-Type': 'application/rss+xml; charset=utf-8',
+ 'Cache-Control': 's-maxage=3600, stale-while-revalidate=86400',
+ },
+ });
+}
diff --git a/web/app/file/RelayfileAnimation.tsx b/web/app/file/RelayfileAnimation.tsx
new file mode 100644
index 00000000..a04e06e3
--- /dev/null
+++ b/web/app/file/RelayfileAnimation.tsx
@@ -0,0 +1,912 @@
+'use client';
+
+import { useCallback, useEffect, useRef, useState, type JSX } from 'react';
+
+import s from '../../components/node-relay.module.css';
+
+type ModelProvider = 'claude' | 'gemini' | 'codex' | 'copilot' | 'opencode';
+type FileEventKind = 'read' | 'write' | 'list' | 'watch';
+
+interface AgentNode {
+ id: string;
+ name: string;
+ provider: ModelProvider;
+ model: string;
+ state: 'IDLE' | 'AWAITING TASK' | 'PROCESSING' | 'RELAYING' | 'COMPLETE';
+ statusText: string;
+ glowing: boolean;
+ glowOpacity: number;
+ baseX: number;
+ baseY: number;
+ x: number;
+ y: number;
+ driftPhase: number;
+ driftSpeed: number;
+ driftAmplitudeX: number;
+ driftAmplitudeY: number;
+ active: boolean;
+ opacity: number;
+}
+
+interface Message {
+ from: number;
+ to: number;
+ t: number;
+ speed: number;
+ trail: { x: number; y: number; age: number }[];
+ branches?: number[];
+ isSpawn?: boolean;
+ kind?: FileEventKind;
+}
+
+interface LandingToast {
+ x: number;
+ y: number;
+ text: string;
+ kind: FileEventKind;
+ age: number;
+ maxAge: number;
+ alpha: number;
+}
+
+const FILE_EVENT_KINDS: FileEventKind[] = ['read', 'write', 'list', 'watch'];
+
+const FILE_COLORS: Record = {
+ read: '74, 144, 194',
+ write: '99, 209, 139',
+ list: '193, 103, 75',
+ watch: '254, 188, 46',
+};
+
+const LANDING_TEXTS: Record = {
+ read: ['/src/auth.ts read', '/config.json loaded', '/src/main.ts parsed', '/README.md opened'],
+ write: [
+ '/dist/out.js written',
+ '/src/fix.ts saved',
+ '/tmp/cache.json flushed',
+ '/public/index.css updated',
+ ],
+ list: ['ls /src/ -> 12 files', 'ls /tests/ -> 8 files', 'ls /dist/ -> 4 files', 'ls /app/ -> 9 files'],
+ watch: [
+ 'watch: /src/*.ts changed',
+ 'watch: config updated',
+ 'watch: bundle rebuilt',
+ 'watch: types regenerated',
+ ],
+};
+
+const IDLE_TEXTS: Record = {
+ claude: ['Waiting for file op...', 'Ready'],
+ gemini: ['Waiting for file op...', 'Ready'],
+ codex: ['Waiting for file op...', 'Ready'],
+ copilot: ['Waiting for file op...', 'Ready'],
+ opencode: ['Waiting for file op...', 'Ready'],
+};
+
+const WORKING_TEXTS: Record = {
+ claude: ['Reading /src/auth.ts...', 'Watching /src/*.ts...', 'Indexing...', 'Writing /dist/server.js...'],
+ gemini: ['Reading /config.json...', 'Indexing...', 'Watching /src/*.ts...', 'Writing /dist/client.js...'],
+ codex: ['Writing /dist/out.js...', 'Reading /src/router.ts...', 'Indexing...', 'Watching /src/*.ts...'],
+ copilot: ['Reading /app/page.tsx...', 'Writing /dist/chunk.js...', 'Watching /src/*.ts...', 'Indexing...'],
+ opencode: ['Reading /src/db.ts...', 'Writing /tmp/cache.json...', 'Indexing...', 'Watching /src/*.ts...'],
+};
+
+const RELAYING_TEXTS: Record = {
+ claude: ['Sending file to...', 'Syncing /dist/...', 'Streaming bytes...'],
+ gemini: ['Sending file to...', 'Syncing /dist/...', 'Streaming bytes...'],
+ codex: ['Sending file to...', 'Syncing /dist/...', 'Streaming bytes...'],
+ copilot: ['Sending file to...', 'Syncing /dist/...', 'Streaming bytes...'],
+ opencode: ['Sending file to...', 'Syncing /dist/...', 'Streaming bytes...'],
+};
+
+const NODE_POOL: { name: string; provider: ModelProvider; model: string }[] = [
+ { name: 'FileServer', provider: 'claude', model: 'Opus' },
+ { name: 'Reader', provider: 'gemini', model: '2.5 Pro' },
+ { name: 'Writer', provider: 'codex', model: 'Codex-1' },
+ { name: 'Indexer', provider: 'claude', model: 'Sonnet' },
+ { name: 'Watcher', provider: 'copilot', model: 'GPT-4.1' },
+ { name: 'Syncer', provider: 'opencode', model: 'Gemini' },
+ { name: 'Archiver', provider: 'gemini', model: '2.5 Flash' },
+ { name: 'Validator', provider: 'claude', model: 'Haiku' },
+];
+
+type ScriptEvent =
+ | { tick: number; type: 'spawn'; from: number; to: number }
+ | { tick: number; type: 'message'; from: number; to: number };
+
+const SCRIPT: ScriptEvent[] = [
+ { tick: 3, type: 'spawn', from: 0, to: 1 },
+ { tick: 6, type: 'spawn', from: 0, to: 2 },
+ { tick: 10, type: 'spawn', from: 0, to: 3 },
+ { tick: 14, type: 'message', from: 1, to: 2 },
+ { tick: 17, type: 'message', from: 2, to: 1 },
+ { tick: 21, type: 'spawn', from: 2, to: 4 },
+ { tick: 25, type: 'spawn', from: 2, to: 5 },
+ { tick: 29, type: 'spawn', from: 0, to: 6 },
+ { tick: 33, type: 'message', from: 4, to: 3 },
+ { tick: 35, type: 'message', from: 5, to: 3 },
+ { tick: 38, type: 'message', from: 3, to: 0 },
+ { tick: 42, type: 'spawn', from: 3, to: 7 },
+ { tick: 46, type: 'message', from: 0, to: 6 },
+ { tick: 49, type: 'message', from: 7, to: 2 },
+];
+
+const MAX_NODES = 8;
+const CARD_W = 160;
+const CARD_H = 88;
+
+const NODE_POSITIONS = (() => {
+ const cx = 0.38;
+ const cy = 0.38;
+
+ const positions: { x: number; y: number }[] = [{ x: cx, y: cy }];
+ const rings = [{ count: 7, radius: 0.32 }];
+
+ for (const ring of rings) {
+ for (let i = 0; i < ring.count; i++) {
+ const angle = (i / ring.count) * Math.PI * 2 - Math.PI * 0.1;
+ const jx = Math.sin(positions.length * 7.3) * 0.01;
+ const jy = Math.cos(positions.length * 5.1) * 0.008;
+ positions.push({
+ x: cx + Math.cos(angle) * ring.radius + jx,
+ y: cy + Math.sin(angle) * ring.radius * 0.85 + jy,
+ });
+ }
+ }
+
+ return positions;
+})();
+
+function buildConnections(count: number): [number, number][] {
+ const conns: [number, number][] = [];
+ const threshold = 0.38;
+ for (let i = 0; i < count; i++) {
+ for (let j = i + 1; j < count; j++) {
+ const dx = NODE_POSITIONS[i].x - NODE_POSITIONS[j].x;
+ const dy = NODE_POSITIONS[i].y - NODE_POSITIONS[j].y;
+ if (Math.sqrt(dx * dx + dy * dy) < threshold) {
+ conns.push([i, j]);
+ }
+ }
+ }
+ return conns;
+}
+
+function lerp(a: number, b: number, t: number) {
+ return a + (b - a) * t;
+}
+
+function easeInOut(t: number) {
+ return t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
+}
+
+function pick(arr: T[]): T {
+ return arr[Math.floor(Math.random() * arr.length)];
+}
+
+function getFileColor(kind: FileEventKind, alpha: number) {
+ return `rgba(${FILE_COLORS[kind]}, ${alpha})`;
+}
+
+function isReadyNode(node: AgentNode) {
+ return node.active && node.opacity >= 0.9;
+}
+
+function enqueueMessage(messages: Message[], message: Omit) {
+ messages.push({
+ ...message,
+ kind: message.isSpawn ? undefined : (message.kind ?? pick(FILE_EVENT_KINDS)),
+ t: 0,
+ trail: [],
+ });
+}
+
+function setNodeRelaying(node: AgentNode, statusText: string) {
+ node.state = 'RELAYING';
+ node.statusText = statusText;
+ node.glowing = true;
+}
+
+function spawnLandingToast(landingToasts: LandingToast[], x: number, y: number, kind: FileEventKind) {
+ landingToasts.push({
+ x,
+ y: y - 30,
+ text: pick(LANDING_TEXTS[kind]),
+ kind,
+ age: 0,
+ maxAge: 100,
+ alpha: 0,
+ });
+}
+
+function handleScriptEvents(tick: number, nodes: AgentNode[], messages: Message[]) {
+ for (const event of SCRIPT) {
+ if (event.tick !== tick) {
+ continue;
+ }
+
+ if (event.type === 'spawn') {
+ setNodeRelaying(nodes[event.from], `Spawning ${nodes[event.to].name}...`);
+ enqueueMessage(messages, {
+ from: event.from,
+ to: event.to,
+ speed: 0.008 + Math.random() * 0.004,
+ isSpawn: true,
+ });
+ continue;
+ }
+
+ const sender = nodes[event.from];
+ if (!isReadyNode(sender)) {
+ continue;
+ }
+
+ setNodeRelaying(sender, pick(RELAYING_TEXTS[sender.provider]));
+ enqueueMessage(messages, {
+ from: event.from,
+ to: event.to,
+ speed: 0.007 + Math.random() * 0.005,
+ kind: pick(FILE_EVENT_KINDS),
+ });
+ }
+}
+
+function findReadyIndices(nodes: AgentNode[]) {
+ return nodes.map((node, index) => (isReadyNode(node) ? index : -1)).filter((index) => index >= 0);
+}
+
+function getActiveNeighbors(senderIdx: number, connections: [number, number][], nodes: AgentNode[]) {
+ return connections
+ .filter(([a, b]) => a === senderIdx || b === senderIdx)
+ .map(([a, b]) => (a === senderIdx ? b : a))
+ .filter((index) => isReadyNode(nodes[index]) && index !== senderIdx);
+}
+
+function pickRelayTargets(activeNeighbors: number[]) {
+ if (activeNeighbors.length >= 2 && Math.random() < 0.3) {
+ return [...activeNeighbors].sort(() => Math.random() - 0.5).slice(0, 2);
+ }
+
+ return [pick(activeNeighbors)];
+}
+
+function maybeQueueRandomRelay(
+ tick: number,
+ nodes: AgentNode[],
+ messages: Message[],
+ connections: [number, number][]
+) {
+ const readyIndices = findReadyIndices(nodes);
+ const scriptDone = tick > (SCRIPT.length > 0 ? SCRIPT[SCRIPT.length - 1].tick + 5 : 0);
+ if (!scriptDone || tick % 3 !== 0 || readyIndices.length < 2) {
+ return;
+ }
+
+ const senderIdx = pick(readyIndices);
+ const activeNeighbors = getActiveNeighbors(senderIdx, connections, nodes);
+ if (activeNeighbors.length === 0) {
+ return;
+ }
+
+ const sender = nodes[senderIdx];
+ setNodeRelaying(sender, pick(RELAYING_TEXTS[sender.provider]));
+
+ for (const target of pickRelayTargets(activeNeighbors)) {
+ enqueueMessage(messages, {
+ from: senderIdx,
+ to: target,
+ speed: 0.007 + Math.random() * 0.005,
+ branches: Math.random() < 0.25 && readyIndices.length > 1 ? [pick(readyIndices)] : undefined,
+ kind: pick(FILE_EVENT_KINDS),
+ });
+ }
+}
+
+function cycleNodeStates(nodes: AgentNode[], tick: number) {
+ for (const node of nodes) {
+ if (node.state === 'COMPLETE') {
+ node.state = 'AWAITING TASK';
+ node.statusText = pick(IDLE_TEXTS[node.provider]);
+ }
+
+ if (node.state === 'RELAYING' && tick % 3 === 0) {
+ node.state = 'COMPLETE';
+ node.statusText = 'Done';
+ }
+
+ if (node.state === 'PROCESSING' && Math.random() < 0.15) {
+ node.statusText = pick(WORKING_TEXTS[node.provider]);
+ }
+ }
+}
+
+function updateNodePositions(nodes: AgentNode[], now: number) {
+ for (const node of nodes) {
+ const t = now * node.driftSpeed + node.driftPhase;
+ node.x = node.baseX + Math.sin(t) * node.driftAmplitudeX + Math.cos(t * 0.7) * node.driftAmplitudeX * 0.5;
+ node.y =
+ node.baseY + Math.cos(t * 1.3) * node.driftAmplitudeY + Math.sin(t * 0.5) * node.driftAmplitudeY * 0.4;
+
+ node.opacity = node.active ? Math.min(node.opacity + 0.08, 1) : Math.max(node.opacity - 0.03, 0);
+
+ node.glowOpacity = node.glowing
+ ? Math.min(node.glowOpacity + 0.06, 1)
+ : Math.max(node.glowOpacity - 0.02, 0);
+
+ if (node.glowOpacity > 0.85) {
+ node.glowing = false;
+ }
+ }
+}
+
+type NodeCenter = { cx: number; cy: number; opacity: number };
+
+function buildCenters(nodes: AgentNode[], w: number, h: number): NodeCenter[] {
+ return nodes.map((node) => ({
+ cx: node.x * w + CARD_W / 2,
+ cy: node.y * h + CARD_H / 2,
+ opacity: node.opacity,
+ }));
+}
+
+function drawConnectionLines(
+ ctx: CanvasRenderingContext2D,
+ centers: NodeCenter[],
+ connections: [number, number][]
+) {
+ for (const [i, j] of connections) {
+ const ci = centers[i];
+ const cj = centers[j];
+ if (!ci || !cj) {
+ continue;
+ }
+
+ const lineOpacity = Math.min(ci.opacity, cj.opacity) * 0.1;
+ if (lineOpacity < 0.01) {
+ continue;
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(ci.cx, ci.cy);
+ ctx.lineTo(cj.cx, cj.cy);
+ ctx.strokeStyle = `rgba(45, 79, 62, ${lineOpacity})`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+}
+
+function drawMessageTrail(ctx: CanvasRenderingContext2D, message: Message) {
+ const kind = message.kind ?? 'read';
+
+ for (let trailIndex = message.trail.length - 1; trailIndex >= 0; trailIndex--) {
+ const point = message.trail[trailIndex];
+ point.age++;
+ const trailAlpha = Math.max(0, 0.3 - point.age * 0.003);
+
+ if (trailAlpha <= 0) {
+ message.trail.splice(trailIndex, 1);
+ continue;
+ }
+
+ const trailSize = Math.max(1, 4 - point.age * 0.03);
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, trailSize, 0, Math.PI * 2);
+ ctx.fillStyle = getFileColor(kind, trailAlpha);
+ ctx.fill();
+ }
+}
+
+function drawRoundedRect(
+ ctx: CanvasRenderingContext2D,
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ radius: number
+) {
+ ctx.beginPath();
+ ctx.moveTo(x + radius, y);
+ ctx.lineTo(x + width - radius, y);
+ ctx.arcTo(x + width, y, x + width, y + radius, radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
+ ctx.lineTo(x + radius, y + height);
+ ctx.arcTo(x, y + height, x, y + height - radius, radius);
+ ctx.lineTo(x, y + radius);
+ ctx.arcTo(x, y, x + radius, y, radius);
+ ctx.closePath();
+}
+
+function drawFilePulse(ctx: CanvasRenderingContext2D, x: number, y: number, kind: FileEventKind) {
+ const glow = getFileColor(kind, 1);
+ const grad = ctx.createRadialGradient(x, y, 0, x, y, 32);
+ grad.addColorStop(0, getFileColor(kind, 0.35));
+ grad.addColorStop(0.5, getFileColor(kind, 0.08));
+ grad.addColorStop(1, getFileColor(kind, 0));
+ ctx.beginPath();
+ ctx.arc(x, y, 32, 0, Math.PI * 2);
+ ctx.fillStyle = grad;
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(x, y, 16, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(15, 27, 41, 0.7)';
+ ctx.fill();
+
+ if (kind === 'read') {
+ const docX = x - 12;
+ const docY = y - 10;
+ drawRoundedRect(ctx, docX, docY, 24, 20, 4);
+ ctx.fillStyle = getFileColor(kind, 0.85);
+ ctx.fill();
+ ctx.fillStyle = 'rgba(234, 230, 221, 0.92)';
+ ctx.fillRect(docX + 5, docY + 5, 14, 2);
+ ctx.fillRect(docX + 5, docY + 9, 11, 2);
+ ctx.fillRect(docX + 5, docY + 13, 9, 2);
+ return;
+ }
+
+ if (kind === 'write') {
+ const docX = x - 11;
+ const docY = y - 10;
+ drawRoundedRect(ctx, docX, docY, 20, 22, 4);
+ ctx.fillStyle = getFileColor(kind, 0.85);
+ ctx.fill();
+ ctx.fillStyle = 'rgba(234, 230, 221, 0.92)';
+ ctx.fillRect(docX + 4, docY + 5, 10, 2);
+ ctx.fillRect(docX + 4, docY + 9, 8, 2);
+ ctx.save();
+ ctx.translate(x + 2, y + 5);
+ ctx.rotate(-0.7);
+ ctx.fillStyle = 'rgba(234, 230, 221, 0.96)';
+ ctx.fillRect(-2, -9, 4, 14);
+ ctx.beginPath();
+ ctx.moveTo(-2, -9);
+ ctx.lineTo(2, -9);
+ ctx.lineTo(0, -13);
+ ctx.closePath();
+ ctx.fillStyle = glow;
+ ctx.fill();
+ ctx.restore();
+ return;
+ }
+
+ if (kind === 'list') {
+ const folderX = x - 13;
+ const folderY = y - 8;
+ ctx.beginPath();
+ ctx.moveTo(folderX, folderY + 4);
+ ctx.lineTo(folderX + 7, folderY + 4);
+ ctx.lineTo(folderX + 10, folderY);
+ ctx.lineTo(folderX + 24, folderY);
+ ctx.lineTo(folderX + 26, folderY + 18);
+ ctx.lineTo(folderX, folderY + 18);
+ ctx.closePath();
+ ctx.fillStyle = getFileColor(kind, 0.85);
+ ctx.fill();
+ ctx.fillStyle = 'rgba(234, 230, 221, 0.9)';
+ ctx.fillRect(folderX + 6, folderY + 7, 12, 2);
+ ctx.fillRect(folderX + 6, folderY + 11, 10, 2);
+ return;
+ }
+
+ ctx.beginPath();
+ ctx.ellipse(x, y, 13, 8, 0, 0, Math.PI * 2);
+ ctx.fillStyle = getFileColor(kind, 0.82);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(x, y, 13, 8, 0, 0, Math.PI * 2);
+ ctx.strokeStyle = 'rgba(234, 230, 221, 0.7)';
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(x, y, 4, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(15, 27, 41, 0.85)';
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(x, y, 1.5, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(234, 230, 221, 0.92)';
+ ctx.fill();
+}
+
+function handleMessageArrival(
+ message: Message,
+ nodes: AgentNode[],
+ messages: Message[],
+ landingToasts: LandingToast[],
+ centers: NodeCenter[]
+) {
+ const receiver = nodes[message.to];
+ if (!receiver) {
+ return;
+ }
+
+ if (message.kind && centers[message.to]) {
+ spawnLandingToast(landingToasts, centers[message.to].cx, centers[message.to].cy, message.kind);
+ }
+
+ if (message.isSpawn && !receiver.active) {
+ receiver.active = true;
+ receiver.state = 'AWAITING TASK';
+ receiver.statusText = pick(IDLE_TEXTS[receiver.provider]);
+ receiver.glowing = true;
+ } else if (receiver.active) {
+ receiver.state = 'PROCESSING';
+ receiver.statusText = pick(WORKING_TEXTS[receiver.provider]);
+ receiver.glowing = true;
+ }
+
+ if (!message.branches || !receiver.active) {
+ return;
+ }
+
+ const branchTargets = message.branches.filter(
+ (branchTarget) =>
+ branchTarget !== message.to && branchTarget !== message.from && nodes[branchTarget]?.active
+ );
+
+ if (branchTargets.length === 0) {
+ return;
+ }
+
+ for (const branchTarget of branchTargets) {
+ enqueueMessage(messages, {
+ from: message.to,
+ to: branchTarget,
+ speed: 0.007 + Math.random() * 0.005,
+ kind: pick(FILE_EVENT_KINDS),
+ });
+ }
+
+ receiver.state = 'RELAYING';
+ receiver.statusText = pick(RELAYING_TEXTS[receiver.provider]);
+}
+
+function drawMessages(
+ ctx: CanvasRenderingContext2D,
+ nodes: AgentNode[],
+ messages: Message[],
+ centers: NodeCenter[],
+ landingToasts: LandingToast[]
+) {
+ for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex--) {
+ const message = messages[messageIndex];
+ message.t += message.speed;
+
+ const from = centers[message.from];
+ const to = centers[message.to];
+ if (!from || !to) {
+ messages.splice(messageIndex, 1);
+ continue;
+ }
+
+ const progress = easeInOut(Math.min(message.t, 1));
+ const px = lerp(from.cx, to.cx, progress);
+ const py = lerp(from.cy, to.cy, progress);
+
+ message.trail.push({ x: px, y: py, age: 0 });
+ drawMessageTrail(ctx, message);
+
+ if (message.t <= 1) {
+ if (message.kind) {
+ drawFilePulse(ctx, px, py, message.kind);
+ }
+ continue;
+ }
+
+ handleMessageArrival(message, nodes, messages, landingToasts, centers);
+ messages.splice(messageIndex, 1);
+ }
+}
+
+function drawGlowRings(ctx: CanvasRenderingContext2D, nodes: AgentNode[], centers: NodeCenter[]) {
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+ if (node.glowOpacity <= 0.01 || node.opacity <= 0.1) {
+ continue;
+ }
+
+ const center = centers[i];
+ const radius = 56;
+ const alpha = node.glowOpacity * node.opacity * 0.07;
+ const grad = ctx.createRadialGradient(center.cx, center.cy, radius * 0.6, center.cx, center.cy, radius);
+ grad.addColorStop(0, `rgba(45, 79, 62, ${alpha})`);
+ grad.addColorStop(1, 'rgba(45, 79, 62, 0)');
+ ctx.beginPath();
+ ctx.arc(center.cx, center.cy, radius, 0, Math.PI * 2);
+ ctx.fillStyle = grad;
+ ctx.fill();
+ }
+}
+
+function drawLandingToasts(ctx: CanvasRenderingContext2D, landingToasts: LandingToast[]) {
+ for (let i = landingToasts.length - 1; i >= 0; i--) {
+ const toast = landingToasts[i];
+ toast.age++;
+
+ if (toast.age < 12) {
+ toast.alpha = toast.age / 12;
+ } else if (toast.age > toast.maxAge - 18) {
+ toast.alpha = (toast.maxAge - toast.age) / 18;
+ } else {
+ toast.alpha = 1;
+ }
+
+ toast.y -= 0.3;
+
+ if (toast.age >= toast.maxAge) {
+ landingToasts.splice(i, 1);
+ continue;
+ }
+
+ if (toast.alpha <= 0.01) {
+ continue;
+ }
+
+ ctx.save();
+ ctx.globalAlpha = toast.alpha * 0.88;
+ ctx.font = '600 11px "JetBrains Mono", monospace';
+
+ const textWidth = ctx.measureText(toast.text).width;
+ const pillWidth = textWidth + 20;
+ const pillHeight = 24;
+ const pillX = toast.x - pillWidth / 2;
+ const pillY = toast.y - pillHeight / 2;
+ const pillRadius = pillHeight / 2;
+
+ drawRoundedRect(ctx, pillX, pillY, pillWidth, pillHeight, pillRadius);
+ ctx.fillStyle = 'rgba(15, 27, 41, 0.9)';
+ ctx.fill();
+ ctx.strokeStyle = getFileColor(toast.kind, 0.35);
+ ctx.lineWidth = 1;
+ ctx.stroke();
+
+ ctx.fillStyle = getFileColor(toast.kind, 0.95);
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.fillText(toast.text, toast.x, toast.y);
+ ctx.restore();
+ }
+}
+
+function ClaudeLogo() {
+ return (
+
+
+
+ );
+}
+
+function CodexLogo() {
+ return (
+
+
+
+
+
+ );
+}
+
+function CopilotLogo() {
+ return (
+
+
+
+ );
+}
+
+function OpenCodeLogo() {
+ return (
+
+
+
+
+
+ );
+}
+
+function GeminiLogo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const LOGO_MAP: Record JSX.Element> = {
+ claude: ClaudeLogo,
+ codex: CodexLogo,
+ copilot: CopilotLogo,
+ opencode: OpenCodeLogo,
+ gemini: GeminiLogo,
+};
+
+export function RelayfileAnimation() {
+ const containerRef = useRef(null);
+ const canvasRef = useRef(null);
+ const nodesRef = useRef([]);
+ const messagesRef = useRef([]);
+ const landingToastsRef = useRef([]);
+ const connectionsRef = useRef<[number, number][]>([]);
+ const rafRef = useRef(0);
+ const tickRef = useRef(0);
+ const timeRef = useRef(0);
+ const [renderNodes, setRenderNodes] = useState([]);
+
+ const initNodes = useCallback(() => {
+ const created: AgentNode[] = NODE_POOL.map((def, i) => ({
+ id: `node-${i}`,
+ name: def.name,
+ provider: def.provider,
+ model: def.model,
+ state: i === 0 ? 'PROCESSING' : 'IDLE',
+ statusText: i === 0 ? pick(WORKING_TEXTS[def.provider]) : '',
+ glowing: i === 0,
+ glowOpacity: 0,
+ baseX: NODE_POSITIONS[i].x,
+ baseY: NODE_POSITIONS[i].y,
+ x: NODE_POSITIONS[i].x,
+ y: NODE_POSITIONS[i].y,
+ driftPhase: Math.random() * Math.PI * 2,
+ driftSpeed: 0.0003 + Math.random() * 0.0004,
+ driftAmplitudeX: 0.008 + Math.random() * 0.01,
+ driftAmplitudeY: 0.006 + Math.random() * 0.008,
+ active: i === 0,
+ opacity: i === 0 ? 1 : 0,
+ }));
+ nodesRef.current = created;
+ messagesRef.current = [];
+ landingToastsRef.current = [];
+ connectionsRef.current = buildConnections(MAX_NODES);
+ setRenderNodes([...created]);
+ }, []);
+
+ useEffect(() => {
+ initNodes();
+ }, [initNodes]);
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ const tick = tickRef.current++;
+ const ns = nodesRef.current;
+ const ms = messagesRef.current;
+ handleScriptEvents(tick, ns, ms);
+ maybeQueueRandomRelay(tick, ns, ms, connectionsRef.current);
+ cycleNodeStates(ns, tick);
+ setRenderNodes([...ns]);
+ }, 250);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ const container = containerRef.current;
+ if (!canvas || !container) {
+ return;
+ }
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) {
+ return;
+ }
+
+ const resize = () => {
+ const rect = container.getBoundingClientRect();
+ const dpr = window.devicePixelRatio || 1;
+ canvas.width = rect.width * dpr;
+ canvas.height = rect.height * dpr;
+ canvas.style.width = `${rect.width}px`;
+ canvas.style.height = `${rect.height}px`;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ };
+
+ resize();
+ window.addEventListener('resize', resize);
+
+ const draw = () => {
+ const rect = container.getBoundingClientRect();
+ const w = rect.width;
+ const h = rect.height;
+ const ns = nodesRef.current;
+ const ms = messagesRef.current;
+ const landingToasts = landingToastsRef.current;
+ const now = timeRef.current++;
+
+ ctx.clearRect(0, 0, w, h);
+ updateNodePositions(ns, now);
+ const centers = buildCenters(ns, w, h);
+ drawConnectionLines(ctx, centers, connectionsRef.current);
+ drawMessages(ctx, ns, ms, centers, landingToasts);
+ drawGlowRings(ctx, ns, centers);
+ drawLandingToasts(ctx, landingToasts);
+
+ rafRef.current = requestAnimationFrame(draw);
+ };
+
+ rafRef.current = requestAnimationFrame(draw);
+
+ return () => {
+ window.removeEventListener('resize', resize);
+ cancelAnimationFrame(rafRef.current);
+ };
+ }, []);
+
+ return (
+
+
+ {renderNodes.map((node) => {
+ const Logo = LOGO_MAP[node.provider];
+ const isBusy = node.state === 'PROCESSING' || node.state === 'RELAYING';
+
+ return (
+
0
+ ? `0 0 ${18 * node.glowOpacity}px rgba(45, 79, 62, ${0.15 * node.glowOpacity}), 0 2px 10px rgba(0,0,0,0.08)`
+ : '0 2px 8px rgba(0,0,0,0.06)',
+ pointerEvents: node.opacity < 0.1 ? 'none' : undefined,
+ }}
+ >
+
+
+
+ {node.name}
+
+
{node.model}
+
+
+ {isBusy && }
+ {node.statusText}
+
+
+ );
+ })}
+
+ );
+}
diff --git a/web/app/file/RelayfileContent.tsx b/web/app/file/RelayfileContent.tsx
new file mode 100644
index 00000000..724c3a44
--- /dev/null
+++ b/web/app/file/RelayfileContent.tsx
@@ -0,0 +1,610 @@
+'use client';
+
+import Link from 'next/link';
+import { useMemo, useState, type ReactNode } from 'react';
+
+import { FadeIn } from '../../components/FadeIn';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+
+import s from './relayfile.module.css';
+
+const featureCards = [
+ {
+ title: 'Provider files',
+ desc: 'Mount Linear issues, GitHub PRs, Notion pages, Slack channels, and SaaS records as ordinary paths.',
+ },
+ {
+ title: 'File writeback',
+ desc: 'Create, patch, and delete provider records by saving files. Adapters validate changes and queue writeback.',
+ },
+ {
+ title: 'Realtime sync',
+ desc: 'Every writer publishes a new revision so other agents see fresh state on their next read.',
+ },
+ {
+ title: 'Scoped mounts',
+ desc: 'Give each agent path-level read, write, watch, or admin permissions instead of broad provider credentials.',
+ },
+ {
+ title: 'Deterministic digests',
+ desc: 'Precompute high-value rollups like yesterday.md so agents start with the answer-shaped file.',
+ },
+ {
+ title: 'Native tools',
+ desc: 'Agents use cat, grep, find, jq, rg, and editors against a real local mount or the HTTP API.',
+ },
+];
+
+const integrationBadges = [
+ 'Linear',
+ 'GitHub',
+ 'Notion',
+ 'Slack',
+ 'HubSpot',
+ 'Salesforce',
+ 'Google Drive',
+ 'Gmail',
+ 'Composio',
+ 'Pipedream',
+ 'Nango',
+ 'Custom APIs',
+];
+
+const sdkTabs = {
+ mount: {
+ label: 'Mount SDK',
+ language: 'typescript',
+ code: `import { RelayfileSetup } from '@relayfile/sdk';
+
+const setup = new RelayfileSetup({
+ accessToken: process.env.RELAYFILE_ACCESS_TOKEN!,
+});
+
+const handle = await setup.ensureMountedWorkspace({
+ workspaceId: 'rw_123',
+ provider: 'notion',
+ verifyProvider: true,
+ localDir: '/workspace/relayfile',
+});
+
+console.log(handle.env().RELAYFILE_LOCAL_DIR);`,
+ bullets: [
+ 'Attach a hosted workspace inside Daytona, E2B, CI, or your own sandbox.',
+ 'Gate startup on provider readiness before the agent begins work.',
+ 'The agent receives a normal directory plus environment variables.',
+ ],
+ },
+ vercel: {
+ label: 'Vercel AI SDK',
+ language: 'typescript',
+ code: `import { generateText, tool } from 'ai';
+import { z } from 'zod';
+import { RelayFileClient } from '@relayfile/sdk';
+
+const files = new RelayFileClient({ token: process.env.RELAYFILE_TOKEN! });
+
+const { text } = await generateText({
+ model: process.env.AI_MODEL!,
+ prompt: 'What changed yesterday across GitHub, Linear, and Notion?',
+ tools: {
+ readRelayfile: tool({
+ description: 'Read a file from the Relayfile workspace',
+ inputSchema: z.object({ path: z.string() }),
+ execute: ({ path }) => files.readFile('rw_123', path),
+ }),
+ },
+});`,
+ bullets: [
+ 'Expose one Relayfile read tool instead of one tool per provider.',
+ 'Let the model inspect digests first, then open raw provider files as needed.',
+ 'Works with generateText, streamText, and AI Gateway model strings.',
+ ],
+ },
+ anthropic: {
+ label: 'Claude Agent SDK',
+ language: 'typescript',
+ code: `import { createSdkMcpServer, query, tool } from '@anthropic-ai/claude-agent-sdk';
+import { z } from 'zod';
+import { RelayFileClient } from '@relayfile/sdk';
+
+const files = new RelayFileClient({ token: process.env.RELAYFILE_TOKEN! });
+
+const relayfileServer = createSdkMcpServer({
+ name: 'relayfile',
+ tools: [
+ tool('readRelayfile', 'Read workspace files', { path: z.string() }, async ({ path }) => ({
+ content: [{ type: 'text', text: JSON.stringify(await files.readFile('rw_123', path)) }],
+ })),
+ ],
+});
+
+for await (const message of query({
+ prompt: 'Summarize yesterday.md, then draft Linear follow-ups.',
+ options: { mcpServers: { relayfile: relayfileServer } },
+})) {
+ console.log(message);
+}`,
+ bullets: [
+ 'Keep Claude in its normal agent loop while Relayfile owns provider context.',
+ 'Use path-scoped tokens for the files the Claude worker may read or write.',
+ 'Pair with a mounted directory when the agent should use bash directly.',
+ ],
+ },
+ openai: {
+ label: 'OpenAI Agents',
+ language: 'typescript',
+ code: `import { Agent, run, tool } from '@openai/agents';
+import { z } from 'zod';
+import { RelayFileClient } from '@relayfile/sdk';
+
+const files = new RelayFileClient({ token: process.env.RELAYFILE_TOKEN! });
+
+const agent = new Agent({
+ name: 'workspace-reviewer',
+ instructions: 'Use Relayfile paths before asking for provider-specific tools.',
+ tools: [
+ tool({
+ name: 'read_relayfile',
+ parameters: z.object({ path: z.string() }),
+ execute: ({ path }) => files.readFile('rw_123', path),
+ }),
+ ],
+});
+
+await run(agent, 'Review /digests/yesterday.md and file Linear follow-ups.');`,
+ bullets: [
+ 'Use Relayfile as the agent state and integration layer for SDK-owned runs.',
+ 'Keep orchestration, approvals, and tracing in the framework you already use.',
+ 'Add write tools only for scoped paths that the agent is allowed to change.',
+ ],
+ },
+ curl: {
+ label: 'cURL',
+ language: 'curl',
+ code: `curl "https://api.agentrelay.com/relayfile/v1/workspaces/rw_123/fs/tree?path=/" \\
+ -H "authorization: Bearer $RELAYFILE_TOKEN"
+
+curl "https://api.agentrelay.com/relayfile/v1/workspaces/rw_123/fs/file?path=/digests/yesterday.md" \\
+ -H "authorization: Bearer $RELAYFILE_TOKEN"
+
+curl -X PUT "https://api.agentrelay.com/relayfile/v1/workspaces/rw_123/fs/file?path=/linear/issues/AGE-12.json" \\
+ -H "authorization: Bearer $RELAYFILE_TOKEN" \\
+ -H "content-type: application/json" \\
+ -d '{"contentType":"application/json","content":"{\\"state\\":\\"In Review\\"}"}'`,
+ bullets: [
+ 'Debug the exact HTTP surface behind the SDK and mount daemon.',
+ 'Use standard shell tools in CI or one-off operational scripts.',
+ 'Query the tree, read files, and submit provider writeback from one API.',
+ ],
+ },
+} as const;
+
+const whyCards = [
+ {
+ title: 'No schema tax',
+ desc: 'Agents open the files they need instead of loading a large typed tool surface for every connected provider.',
+ },
+ {
+ title: 'Useful writes',
+ desc: 'Provider updates are file operations with validation, retries, and audit trails handled by the integration layer.',
+ },
+ {
+ title: 'Multi-agent by default',
+ desc: 'Reviewer, implementer, and orchestrator agents can all mount the same workspace and react to the same revisions.',
+ },
+];
+
+const steps = [
+ {
+ step: '01',
+ title: 'Connect a provider',
+ code: `npx relayfile setup \\
+ --provider notion \\
+ --workspace research-room \\
+ --local-dir ./relayfile-mount`,
+ },
+ {
+ step: '02',
+ title: 'Read the tree',
+ code: `ls ./relayfile-mount
+cat ./relayfile-mount/LAYOUT.md
+cat ./relayfile-mount/digests/yesterday.md`,
+ },
+ {
+ step: '03',
+ title: 'Write back',
+ code: `cat > ./relayfile-mount/linear/issues/drafts/follow-up.json <<'JSON'
+{
+ "title": "Follow up from digest",
+ "state": "Todo"
+}
+JSON`,
+ },
+];
+
+type Lang = 'typescript' | 'python' | 'curl';
+
+function highlightNonString(text: string, keywords: RegExp, types: RegExp, methods: RegExp) {
+ return text
+ .replace(types, `$1 `)
+ .replace(keywords, `$& `)
+ .replace(methods, `.$1 (`);
+}
+
+function highlight(code: string, lang: Lang) {
+ const keywords =
+ lang === 'python'
+ ? /\b(from|import|await|for|in)\b/g
+ : lang === 'curl'
+ ? /\b(curl)\b/g
+ : /\b(import|from|const|await|new)\b/g;
+ const types = /\b(RelayfileSetup|RelayFileClient|Agent|Relayfile)\b/g;
+ const methods = /\.(\w+)\(/g;
+
+ const escaped = code.replace(/&/g, '&').replace(//g, '>');
+
+ const parts: string[] = [];
+ let lastIndex = 0;
+ const stringMatches = [...escaped.matchAll(/(["'`])(?:(?!\1).)*\1/g)];
+
+ if (stringMatches.length === 0) {
+ return highlightNonString(escaped, keywords, types, methods);
+ }
+
+ for (const match of stringMatches) {
+ const before = escaped.slice(lastIndex, match.index);
+ parts.push(highlightNonString(before, keywords, types, methods));
+ parts.push(`${match[0]} `);
+ lastIndex = match.index! + match[0].length;
+ }
+
+ parts.push(highlightNonString(escaped.slice(lastIndex), keywords, types, methods));
+ return parts.join('');
+}
+
+function RelayfileAnimation() {
+ return (
+
+
+
+
+
+
+
+
+
+
relayfile workspace
+
+
+
+
+ workspace
+ shared volume
+
+
+ /plans
+ watching
+
+
+ brief.md
+ updated 2s ago
+
+
+ tasks.json
+ locked by Builder
+
+
+ deploy.log
+ streaming
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ WATCH
+ /plans
+
+
+ WRITE
+ /docs/spec.md
+
+
+ LOCK
+ /tasks.json
+
+
+
+
+
+ );
+}
+
+export function RelayfileContent({ navActions }: { navActions?: ReactNode }) {
+ const [activeTab, setActiveTab] = useState('mount');
+ const activeSdk = sdkTabs[activeTab];
+ const highlightedSdk = useMemo(() => highlight(activeSdk.code, activeSdk.language), [activeSdk]);
+ const highlightedSteps = useMemo(() => steps.map((step) => highlight(step.code, 'curl')), []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INTEGRATION FILESYSTEM FOR AGENTS
+
+
+
+ Relayfile
+
+ gives agents files
+
+
+
+ Mount Linear, GitHub, Notion, Slack, HubSpot, Salesforce, and your own systems as a realtime
+ filesystem. Agents inspect provider data with bash, write records by saving files, and
+ coordinate through scoped mounts instead of bespoke tool glue.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The file interface for integration work
+
+ Relayfile turns the systems where work happens into paths an agent can list, read, grep, edit, and
+ watch.
+
+
+
+
+ {featureCards.map((feature, index) => (
+
+
+
{String(index + 1).padStart(2, '0')}
+
{feature.title}
+
{feature.desc}
+
+
+ ))}
+
+
+
+
+
+
+ Mount the systems agents actually use
+
+ Provider auth, pagination, sync, and writeback stay behind the mount. The agent sees one
+ workspace tree with layouts, indexes, digests, and provider records side by side.
+
+
+
+ {integrationBadges.map((integration) => (
+
+ {integration}
+
+ ))}
+
+
+
+
+
+
+
+ SDK
+ Use the same files from code, HTTP, or a mount
+
+ Vercel AI SDK, Claude Agent SDK, OpenAI Agents SDK, scripts, and terminal-native agents can all
+ read the same workspace tree and write through the same contract.
+
+
+
+ {(Object.keys(sdkTabs) as Array).map((tab) => (
+ setActiveTab(tab)}
+ >
+ {sdkTabs[tab].label}
+
+ ))}
+
+
+
+ {activeSdk.bullets.map((bullet) => (
+
+
+ {bullet}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
{activeSdk.label}
+
+
+
+
+
+
+
+
+
+
+
+
+ Why Relayfile
+
+ Purpose-built for agents that need to work across real SaaS systems.
+
+
+
+
+ {whyCards.map((card, index) => (
+
+
+
+ {String(index + 1).padStart(2, '0')}
+
+
{card.title}
+
{card.desc}
+
+
+ ))}
+
+
+
+
+
+
+
+ Get useful in three moves
+
+ Connect a provider, read the generated workspace, then write back through the same file
+ interface. The first useful workflow is a directory listing, not a framework migration.
+
+
+
+
+ {steps.map((step, index) => (
+
+
+
+ {step.step}
+
{step.title}
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
Powered by Agent Relay
+
+ Relayfile extends the Agent Relay platform with shared storage for agents that communicate,
+ coordinate, and act on the same files.
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/file/page.tsx b/web/app/file/page.tsx
new file mode 100644
index 00000000..5f4a8014
--- /dev/null
+++ b/web/app/file/page.tsx
@@ -0,0 +1,15 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+
+import { RelayfileContent } from './RelayfileContent';
+
+export const metadata: Metadata = {
+ title: 'Relayfile | Integration filesystem for AI agents',
+ description:
+ 'Mount SaaS integrations as files so agents can read, write, watch, and coordinate through one realtime workspace.',
+};
+
+export default function FilePage() {
+ return } />;
+}
diff --git a/web/app/file/relayfile.module.css b/web/app/file/relayfile.module.css
new file mode 100644
index 00000000..90e83a1f
--- /dev/null
+++ b/web/app/file/relayfile.module.css
@@ -0,0 +1,1105 @@
+/* Relayfile page */
+.page {
+ min-height: 100vh;
+ background:
+ radial-gradient(
+ ellipse 36% 28% at 10% 8%,
+ color-mix(in srgb, var(--primary) 16%, white 22%),
+ transparent 72%
+ ),
+ radial-gradient(
+ ellipse 34% 26% at 90% 10%,
+ color-mix(in srgb, var(--primary) 14%, white 20%),
+ transparent 70%
+ ),
+ var(--landing-hero-bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+.heroSection {
+ position: relative;
+ overflow: hidden;
+ margin-top: 0;
+ padding-top: 60px;
+}
+
+:global(html[data-theme='dark']) .page {
+ background:
+ radial-gradient(circle at 14% 0%, color-mix(in srgb, var(--primary) 18%, transparent), transparent 36%),
+ radial-gradient(circle at 78% 14%, color-mix(in srgb, var(--primary) 14%, transparent), transparent 30%),
+ radial-gradient(circle at 50% 22%, color-mix(in srgb, var(--primary) 8%, transparent), transparent 44%),
+ var(--landing-hero-bg);
+}
+
+.heroSection > * {
+ position: relative;
+ z-index: 2;
+}
+
+.heroBgSvg {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 0;
+ pointer-events: none;
+ opacity: 0.96;
+ filter: saturate(1.08) contrast(1.03);
+}
+
+:global(html[data-theme='dark']) .heroBgSvg {
+ opacity: 0.88;
+ filter: none;
+}
+
+.hero {
+ display: grid;
+ grid-template-columns: 1fr 1.2fr;
+ align-items: start;
+ gap: 32px;
+ max-width: 1280px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 44px 40px 48px;
+}
+
+.heroLeft {
+ display: flex;
+ flex-direction: column;
+ gap: 28px;
+ padding-top: 56px;
+}
+
+.heroRight {
+ position: relative;
+ min-height: 580px;
+}
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--line);
+ padding: 6px 16px;
+ border-radius: 100px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ color: var(--primary);
+ width: fit-content;
+}
+
+.badgeDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 6px color-mix(in srgb, var(--primary) 50%, transparent);
+ animation: pulse 2s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.45;
+ }
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(3rem, 6vw, 5rem);
+ font-weight: 500;
+ line-height: 1.05;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0;
+}
+
+:global(html[data-theme='dark']) .headline {
+ opacity: 1;
+}
+
+.subtitle {
+ font-size: 1.1rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 50ch;
+ font-weight: 400;
+ margin: 0;
+}
+
+.ctas {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding-top: 4px;
+}
+
+.ctaPrimary {
+ composes: btn btn-primary from global;
+}
+
+.ctaSecondary {
+ composes: btn btn-secondary from global;
+}
+
+.animationShell {
+ position: relative;
+ height: 100%;
+ min-height: 580px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.animationAura {
+ position: absolute;
+ inset: 12% 10% 10%;
+ border-radius: 36px;
+ background:
+ radial-gradient(circle at 30% 28%, color-mix(in srgb, var(--primary) 26%, transparent), transparent 34%),
+ radial-gradient(
+ circle at 74% 26%,
+ color-mix(in srgb, var(--secondary-400) 22%, transparent),
+ transparent 30%
+ ),
+ radial-gradient(circle at 50% 74%, color-mix(in srgb, var(--primary) 14%, white 8%), transparent 38%);
+ filter: blur(22px);
+ opacity: 0.9;
+}
+
+.animationBoard {
+ position: relative;
+ z-index: 1;
+ width: min(100%, 680px);
+ min-height: 500px;
+ border-radius: 28px;
+ overflow: hidden;
+ border: 1px solid color-mix(in srgb, var(--primary) 16%, transparent);
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--surface-strong) 92%, transparent) 0%,
+ color-mix(in srgb, var(--surface) 96%, transparent) 100%
+ );
+ box-shadow:
+ 0 24px 80px rgba(0, 0, 0, 0.14),
+ inset 0 1px 0 rgba(255, 255, 255, 0.22);
+ backdrop-filter: blur(16px);
+}
+
+.boardHeader,
+.previewTitleBar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 14px 18px;
+ background: color-mix(in srgb, var(--surface) 78%, transparent);
+ border-bottom: 1px solid var(--line);
+}
+
+.previewDots {
+ display: flex;
+ gap: 6px;
+}
+
+.previewDots span {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+}
+
+.boardTitle,
+.previewTitleText {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ color: var(--fg-faint);
+}
+
+.boardCanvas {
+ position: relative;
+ min-height: 438px;
+ padding: 24px;
+ overflow: hidden;
+ background:
+ linear-gradient(to right, color-mix(in srgb, var(--line) 55%, transparent) 1px, transparent 1px),
+ linear-gradient(to bottom, color-mix(in srgb, var(--line) 45%, transparent) 1px, transparent 1px),
+ linear-gradient(180deg, transparent 0%, color-mix(in srgb, var(--primary) 3%, transparent) 100%);
+ background-size:
+ 90px 90px,
+ 90px 90px,
+ auto;
+}
+
+.folderCard,
+.fileCard {
+ position: absolute;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ padding: 14px 16px;
+ border-radius: 16px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ box-shadow: 0 16px 40px color-mix(in srgb, black 10%, transparent);
+}
+
+.folderCard {
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--secondary-50) 90%, white 10%) 0%,
+ color-mix(in srgb, var(--secondary-100) 94%, white 6%) 100%
+ );
+}
+
+:global(html[data-theme='dark']) .folderCard {
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--secondary-900) 34%, var(--surface-strong) 66%) 0%,
+ color-mix(in srgb, var(--secondary-950) 26%, var(--surface) 74%) 100%
+ );
+}
+
+.folderCardMain {
+ top: 88px;
+ left: 248px;
+ min-width: 146px;
+ animation: floatPrimary 7s ease-in-out infinite;
+}
+
+.folderCardTop {
+ top: 26px;
+ left: 84px;
+ min-width: 132px;
+ animation: floatSoft 8s ease-in-out infinite;
+}
+
+.fileCardLeft {
+ top: 194px;
+ left: 68px;
+ min-width: 138px;
+ animation: floatSoft 7.5s ease-in-out infinite;
+}
+
+.fileCardRight {
+ top: 192px;
+ right: 74px;
+ min-width: 148px;
+ animation: floatSoft 8.5s ease-in-out infinite reverse;
+}
+
+.fileCardBottom {
+ bottom: 62px;
+ left: 240px;
+ min-width: 158px;
+ animation: floatPrimary 9s ease-in-out infinite reverse;
+}
+
+.folderLabel,
+.fileName {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--fg);
+}
+
+.folderMeta,
+.fileMeta {
+ font-size: 0.78rem;
+ color: var(--fg-muted);
+}
+
+.boardLinks {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ stroke: color-mix(in srgb, var(--primary) 36%, transparent);
+ stroke-width: 2;
+ stroke-linecap: round;
+ stroke-dasharray: 8 10;
+ opacity: 0.9;
+}
+
+.pulse {
+ position: absolute;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 35%, transparent);
+ animation: ping 2.6s ease-out infinite;
+}
+
+.pulseOne {
+ top: 134px;
+ left: 312px;
+}
+
+.pulseTwo {
+ top: 222px;
+ left: 204px;
+ animation-delay: 0.75s;
+}
+
+.pulseThree {
+ top: 220px;
+ right: 206px;
+ animation-delay: 1.2s;
+}
+
+.activityRail {
+ position: absolute;
+ right: 20px;
+ bottom: 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ min-width: 180px;
+ padding: 14px;
+ border-radius: 16px;
+ border: 1px solid var(--line);
+ background: color-mix(in srgb, var(--surface-strong) 88%, transparent);
+ backdrop-filter: blur(10px);
+}
+
+.activityRow {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+}
+
+.activityType {
+ color: var(--primary);
+ font-weight: 600;
+}
+
+.activityPath {
+ color: var(--fg-muted);
+}
+
+@keyframes ping {
+ 0% {
+ transform: scale(1);
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 35%, transparent);
+ }
+ 70% {
+ transform: scale(1.06);
+ box-shadow: 0 0 0 14px color-mix(in srgb, var(--primary) 0%, transparent);
+ }
+ 100% {
+ transform: scale(1);
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 0%, transparent);
+ }
+}
+
+@keyframes floatPrimary {
+ 0%,
+ 100% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(-10px);
+ }
+}
+
+@keyframes floatSoft {
+ 0%,
+ 100% {
+ transform: translateY(0px) translateX(0px);
+ }
+ 50% {
+ transform: translateY(-8px) translateX(3px);
+ }
+}
+
+.featuresWrapper {
+ background: var(--bg);
+ position: relative;
+ z-index: 3;
+}
+
+.featuresHeader {
+ text-align: center;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 72px 40px 40px;
+}
+
+.featuresTitle,
+.byohTitle,
+.sdkTitle,
+.deployTitle,
+.openclawTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.5vw, 2.6rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0 0 14px;
+}
+
+:global(html[data-theme='dark']) .featuresTitle,
+:global(html[data-theme='dark']) .byohTitle,
+:global(html[data-theme='dark']) .sdkTitle,
+:global(html[data-theme='dark']) .deployTitle,
+:global(html[data-theme='dark']) .openclawTitle {
+ opacity: 1;
+}
+
+.featuresSubtitle,
+.byohSubtitle,
+.sdkSubtitle,
+.deploySubtitle,
+.openclawSubtitle {
+ font-size: 1.04rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.featuresSection {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 20px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 0 40px 88px;
+}
+
+.featureCol {
+ display: flex;
+}
+
+.featureTile {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ min-height: 100%;
+ padding: 22px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--surface-strong) 96%, transparent) 0%,
+ color-mix(in srgb, var(--surface) 98%, transparent) 100%
+ );
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 4%, transparent);
+}
+
+.featureChip {
+ width: fit-content;
+ padding: 6px 10px;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
+ color: var(--primary);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ font-weight: 700;
+}
+
+.featureTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.08rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0;
+}
+
+.featureDesc {
+ font-size: 0.92rem;
+ line-height: 1.62;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.byohWrapper {
+ background: var(--showcase-bg);
+ position: relative;
+ overflow: hidden;
+}
+
+.byohWrapper::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 40% 50% at 20% 30%, rgba(255, 255, 255, 0.4) 0%, transparent 70%),
+ radial-gradient(ellipse 35% 40% at 75% 65%, rgba(255, 255, 255, 0.25) 0%, transparent 70%),
+ radial-gradient(ellipse 50% 30% at 50% 10%, rgba(255, 255, 255, 0.3) 0%, transparent 60%);
+ pointer-events: none;
+}
+
+.byohWrapper::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 400'%3E%3Cpath d='M-20,70 Q220,10 520,96 T1020,62 T1220,82' fill='none' stroke='rgba(255,255,255,0.22)' stroke-width='4'/%3E%3Cpath d='M-20,150 Q250,72 560,156 T1060,124 T1220,146' fill='none' stroke='rgba(255,255,255,0.16)' stroke-width='3'/%3E%3Cpath d='M-20,246 Q200,182 500,254 T1000,226 T1220,246' fill='none' stroke='rgba(255,255,255,0.12)' stroke-width='2.5'/%3E%3C/svg%3E");
+ background-size: cover;
+ background-position: center;
+ pointer-events: none;
+}
+
+.byohSection {
+ position: relative;
+ z-index: 1;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px 96px;
+ text-align: center;
+}
+
+.byohText {
+ margin-bottom: 42px;
+}
+
+.byohSubtitle {
+ max-width: 56ch;
+ margin: 0 auto;
+}
+
+.byohLogos {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 16px;
+}
+
+.toolBadge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 120px;
+ padding: 14px 18px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg);
+ font-size: 0.88rem;
+ font-weight: 600;
+ box-shadow: 0 4px 20px color-mix(in srgb, var(--primary) 6%, transparent);
+}
+
+.sdkWrapper,
+.deployWrapper,
+.openclawWrapper {
+ background: var(--section-bg);
+}
+
+.sdkSection {
+ display: grid;
+ grid-template-columns: 1fr 1.1fr;
+ align-items: start;
+ gap: 44px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px;
+}
+
+.sdkText {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.openclawBadge {
+ display: inline-flex;
+ align-items: center;
+ width: fit-content;
+ padding: 4px 12px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ border: 1px solid color-mix(in srgb, var(--primary) 12%, transparent);
+ border-radius: 100px;
+ font-size: 0.7rem;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.tabRow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.tabButton {
+ padding: 10px 16px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg-muted);
+ font: inherit;
+ font-size: 0.88rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition:
+ transform 0.15s,
+ border-color 0.2s,
+ color 0.2s,
+ background 0.2s;
+}
+
+.tabButton:hover {
+ transform: translateY(-1px);
+ border-color: var(--card-hover-border);
+}
+
+.tabButtonActive {
+ background: color-mix(in srgb, var(--primary) 12%, transparent);
+ border-color: color-mix(in srgb, var(--primary) 28%, transparent);
+ color: var(--primary);
+}
+
+.sdkBullets {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.sdkBullet {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ font-size: 0.95rem;
+ line-height: 1.6;
+ color: var(--fg-muted);
+}
+
+.sdkBulletDot {
+ width: 8px;
+ height: 8px;
+ margin-top: 0.45rem;
+ flex-shrink: 0;
+ border-radius: 50%;
+ background: var(--primary);
+}
+
+.sdkCode {
+ min-width: 0;
+}
+
+.codePanel {
+ overflow: hidden;
+ border-radius: 18px;
+ border: 1px solid var(--terminal-line);
+ background: var(--terminal-bg);
+ box-shadow: 0 18px 48px color-mix(in srgb, black 18%, transparent);
+}
+
+.sdkCodeBlock {
+ margin: 0;
+ padding: 22px;
+ overflow-x: auto;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.84rem;
+ line-height: 1.68;
+ color: var(--terminal-fg);
+}
+
+.codeKeyword {
+ color: var(--terminal-keyword);
+}
+.codeString {
+ color: var(--terminal-string);
+}
+.codeType {
+ color: var(--terminal-type);
+}
+.codeMethod {
+ color: var(--terminal-method);
+}
+
+.deploySection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 20px 40px 92px;
+ text-align: center;
+}
+
+.deploySubtitle {
+ margin-bottom: 40px;
+}
+
+.deployCards {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 24px;
+}
+
+.deployCard {
+ padding: 30px 26px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ text-align: left;
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.2s;
+}
+
+.deployCard:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 8%, transparent);
+ transform: translateY(-2px);
+}
+
+.deployIcon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 44px;
+ height: 44px;
+ padding: 0 12px;
+ border-radius: 12px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ color: var(--primary);
+ margin-bottom: 16px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ font-weight: 700;
+}
+
+.deployCardTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.2rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--fg);
+ margin: 0 0 10px;
+}
+
+.deployCardText {
+ font-size: 0.94rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.openclawSection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px 96px;
+}
+
+.openclawText {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ max-width: 720px;
+ margin-bottom: 34px;
+}
+
+.stepsGrid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 24px;
+}
+
+.stepCard {
+ height: 100%;
+ border-radius: 18px;
+ overflow: hidden;
+ border: 1px solid var(--line);
+ background: var(--surface-strong);
+ box-shadow: 0 10px 32px color-mix(in srgb, var(--primary) 5%, transparent);
+}
+
+.stepHeader {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 18px 20px;
+ border-bottom: 1px solid var(--line);
+ background: color-mix(in srgb, var(--surface) 72%, transparent);
+}
+
+.stepNumber {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
+ color: var(--primary);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ font-weight: 700;
+}
+
+.stepTitle {
+ margin: 0;
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.06rem;
+ font-weight: 600;
+ color: var(--fg);
+}
+
+.stepCode {
+ margin: 0;
+ padding: 20px;
+ min-height: 226px;
+ overflow-x: auto;
+ background: var(--terminal-bg);
+ color: var(--terminal-fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ line-height: 1.66;
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+}
+
+.poweredWrapper {
+ background: var(--bg);
+ padding: 0 40px 96px;
+}
+
+.poweredCard {
+ max-width: 820px;
+ margin: 0 auto;
+ padding: 28px 32px;
+ text-align: center;
+ border-radius: 22px;
+ border: 1px solid var(--line);
+ background:
+ radial-gradient(circle at top, color-mix(in srgb, var(--primary) 10%, transparent), transparent 58%),
+ var(--card-bg);
+ box-shadow: 0 14px 40px color-mix(in srgb, var(--primary) 6%, transparent);
+}
+
+.poweredEyebrow {
+ display: inline-block;
+ margin-bottom: 10px;
+ font-size: 0.75rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.poweredText {
+ margin: 0;
+ font-size: 1rem;
+ line-height: 1.7;
+ color: var(--fg-muted);
+}
+
+@media (max-width: 1100px) {
+ .featuresSection {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 960px) {
+ .hero {
+ grid-template-columns: 1fr;
+ padding: 52px 32px 52px;
+ gap: 24px;
+ }
+
+ .heroRight,
+ .animationShell {
+ min-height: 420px;
+ }
+
+ .animationBoard {
+ min-height: 420px;
+ }
+
+ .boardCanvas {
+ min-height: 356px;
+ }
+
+ .folderCardMain {
+ left: 50%;
+ transform: translateX(-50%);
+ animation: none;
+ }
+
+ .folderCardTop {
+ left: 28px;
+ }
+
+ .fileCardLeft {
+ left: 24px;
+ top: 198px;
+ }
+
+ .fileCardRight {
+ right: 24px;
+ top: 198px;
+ }
+
+ .fileCardBottom {
+ left: 50%;
+ bottom: 72px;
+ transform: translateX(-50%);
+ animation: none;
+ }
+
+ .activityRail {
+ position: static;
+ margin-top: 260px;
+ }
+
+ .featuresSection {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ padding: 0 24px 72px;
+ }
+
+ .featuresHeader,
+ .byohSection,
+ .sdkSection,
+ .deploySection,
+ .openclawSection,
+ .poweredWrapper {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+
+ .sdkSection {
+ grid-template-columns: 1fr;
+ }
+
+ .deployCards,
+ .stepsGrid {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 600px) {
+ .hero {
+ padding: 64px 20px 56px;
+ }
+
+ .headline {
+ font-size: clamp(2.4rem, 11vw, 4.5rem);
+ line-height: 1.08;
+ }
+
+ .subtitle {
+ font-size: 1.04rem;
+ }
+
+ .ctas {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .ctaPrimary,
+ .ctaSecondary {
+ width: 100%;
+ justify-content: center;
+ }
+
+ .heroRight,
+ .animationShell {
+ min-height: 360px;
+ }
+
+ .animationBoard {
+ min-height: 380px;
+ }
+
+ .boardCanvas {
+ padding: 18px;
+ }
+
+ .folderCard,
+ .fileCard {
+ min-width: 0;
+ padding: 12px 14px;
+ }
+
+ .folderCardMain {
+ top: 76px;
+ left: 50%;
+ width: calc(100% - 120px);
+ }
+
+ .folderCardTop,
+ .fileCardLeft,
+ .fileCardRight,
+ .fileCardBottom {
+ position: static;
+ width: 100%;
+ margin-top: 12px;
+ transform: none;
+ animation: none;
+ }
+
+ .boardLinks,
+ .pulse {
+ display: none;
+ }
+
+ .activityRail {
+ margin-top: 16px;
+ min-width: 0;
+ }
+
+ .featuresSection {
+ grid-template-columns: 1fr;
+ }
+
+ .featuresHeader,
+ .byohSection,
+ .sdkSection,
+ .deploySection,
+ .openclawSection,
+ .poweredWrapper {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+
+ .featuresSection {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+
+ .sdkCodeBlock {
+ font-size: 0.74rem;
+ padding: 16px;
+ }
+
+ .stepCode {
+ font-size: 0.72rem;
+ padding: 16px;
+ min-height: auto;
+ }
+
+ .toolBadge {
+ min-width: 0;
+ padding: 10px 14px;
+ font-size: 0.82rem;
+ }
+
+ .deployCards {
+ grid-template-columns: 1fr;
+ }
+
+ .stepsGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .poweredCard {
+ padding: 24px 20px;
+ }
+}
diff --git a/web/app/globals.css b/web/app/globals.css
new file mode 100644
index 00000000..5411fa81
--- /dev/null
+++ b/web/app/globals.css
@@ -0,0 +1,191 @@
+@import 'tailwindcss';
+@import '../public/brand.css';
+
+html {
+ /* Strong custom easing curves — the built-in CSS easings are too weak to
+ feel intentional. Shared across interactive elements site-wide. */
+ --ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
+ --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+
+ background: var(--bg);
+ color-scheme: dark;
+ transition: background 0.3s ease;
+}
+
+html[data-theme='dark'] {
+ color-scheme: dark;
+}
+
+body {
+ min-height: 100vh;
+ background: var(--bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+a {
+ color: inherit;
+}
+
+/* ── Buttons ──
+ Canonical button design, shared across the site. Landing-page CSS
+ modules pull these in with `composes: btn btn-primary from global;`
+ so the primary button looks identical everywhere. */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ font-size: 0.92rem;
+ font-weight: 600;
+ letter-spacing: 0.01em;
+ padding: 12px 28px;
+ border-radius: 100px;
+ text-decoration: none;
+ cursor: pointer;
+ transition:
+ background 0.2s,
+ border-color 0.2s,
+ color 0.2s,
+ transform 0.15s var(--ease-out-quint),
+ box-shadow 0.2s;
+}
+
+.btn-primary {
+ background: var(--primary-600);
+ color: var(--primary-fg);
+ border: 1.5px solid var(--primary-800);
+}
+
+.btn-primary:hover {
+ background: var(--primary-500);
+ border-color: var(--primary-800);
+}
+
+.btn-secondary {
+ background: var(--secondary-surface);
+ color: var(--fg);
+ border: 1.5px solid var(--line);
+}
+
+.btn-secondary:hover {
+ border-color: var(--primary);
+ color: var(--primary);
+}
+
+/* Lift on hover only where a real pointer exists — touch devices fire :hover
+ on tap, which makes the lift flash on press. */
+@media (hover: hover) and (pointer: fine) {
+ .btn:hover {
+ transform: translateY(-1px);
+ }
+}
+
+/* Press feedback: every pressable element should confirm it heard the click.
+ Placed after :hover so the press scale wins while the button is held. */
+.btn:active {
+ transform: scale(0.97);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .btn:hover,
+ .btn:active {
+ transform: none;
+ }
+}
+
+code,
+pre {
+ font-family: var(--font-geist-mono), monospace;
+}
+
+/* ── Syntax highlighting ── */
+.syn-keyword {
+ color: var(--primary);
+}
+.syn-string {
+ color: var(--syntax-string);
+}
+.syn-type {
+ color: var(--primary-500);
+}
+.syn-method {
+ color: var(--syntax-method);
+}
+.syn-comment {
+ color: var(--syntax-comment);
+ font-style: italic;
+}
+.syn-number {
+ color: var(--primary-hover);
+}
+
+html[data-theme='dark'] .shiki,
+html[data-theme='dark'] .shiki span {
+ color: var(--shiki-dark) !important;
+}
+
+html[data-theme='dark'] .shiki {
+ background-color: var(--code-bg) !important;
+}
+
+html[data-theme='dark'] .shiki span {
+ background-color: transparent !important;
+}
+
+.skill-page {
+ max-width: 1040px;
+ margin: 0 auto;
+ padding: 40px 16px 56px;
+ min-height: 100vh;
+ color: var(--fg);
+}
+
+.skill-page__header {
+ margin-bottom: 20px;
+ padding: 24px;
+ border: 1px solid var(--line);
+ border-radius: 18px;
+ background: var(--bg-elevated);
+ backdrop-filter: blur(10px);
+}
+
+.skill-page h1 {
+ font-size: clamp(1.8rem, 4vw, 2.4rem);
+ margin: 0 0 12px;
+ letter-spacing: -0.04em;
+}
+
+.skill-page__eyebrow {
+ margin: 0 0 8px;
+ font-size: 0.78rem;
+ font-weight: 700;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.skill-page__lead {
+ margin: 0;
+ max-width: 70ch;
+ line-height: 1.7;
+ color: var(--fg-muted);
+}
+
+.skill-page pre {
+ margin: 0;
+ white-space: pre-wrap;
+ word-break: break-word;
+ line-height: 1.55;
+ border: 1px solid var(--line);
+ border-radius: 18px;
+ background: var(--surface);
+ color: var(--fg-muted);
+ padding: 24px;
+ box-shadow: var(--shadow-strong);
+}
diff --git a/web/app/landing.module.css b/web/app/landing.module.css
new file mode 100644
index 00000000..6f81f0f1
--- /dev/null
+++ b/web/app/landing.module.css
@@ -0,0 +1,4441 @@
+/* Landing page */
+.page {
+ --agent-tools-band-bg:
+ radial-gradient(circle at 18% 24%, rgba(116, 184, 226, 0.16), transparent 32%),
+ linear-gradient(180deg, #10273a 0%, #17364c 52%, #0d2638 100%);
+ min-height: 100vh;
+ background: #152e44;
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+/* ---- HERO ---- */
+
+.heroSection {
+ position: relative;
+ background: var(--agent-tools-band-bg);
+ transition: background 0.3s ease;
+ overflow: hidden;
+ margin-top: 0px;
+ padding-top: 60px;
+}
+
+:global(html[data-theme='dark']) .page {
+ background: #152e44;
+}
+
+:global(html[data-theme='dark']) .heroSection {
+ background: var(--agent-tools-band-bg);
+}
+
+.heroSection > * {
+ position: relative;
+ z-index: 2;
+}
+
+/* Hero communication graph and contour background */
+.heroBgSvg {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 0;
+ pointer-events: none;
+ opacity: 0.96;
+ filter: saturate(1.1) contrast(1.04);
+}
+
+:global(html[data-theme='dark']) .heroBgSvg {
+ opacity: 0.88;
+ filter: none;
+}
+
+.hero {
+ display: grid;
+ grid-template-columns: 1fr 1.2fr;
+ align-items: start;
+ gap: 32px;
+ max-width: 1280px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 44px 40px 48px;
+ min-height: auto;
+}
+
+.heroLeft {
+ display: flex;
+ flex-direction: column;
+ gap: 28px;
+ padding-top: 56px;
+}
+
+.heroRight {
+ position: relative;
+ align-self: stretch;
+ min-height: 580px;
+}
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--line);
+ padding: 6px 16px;
+ border-radius: 100px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ color: var(--primary);
+ width: fit-content;
+}
+
+.badgeDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 6px color-mix(in srgb, var(--primary) 50%, transparent);
+ animation: pulse 2s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.4;
+ }
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(3.2rem, 7vw, 5.5rem);
+ font-weight: 500;
+ line-height: 1.05;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ opacity: 0.85;
+}
+
+:global(html[data-theme='dark']) .headline {
+ opacity: 1;
+}
+
+.subtitle {
+ font-size: 1.1rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 48ch;
+ font-weight: 400;
+}
+
+.ctas {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding-top: 4px;
+}
+
+.installCommand {
+ width: 100%;
+ min-height: 64px;
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ padding: 0.72rem 0.9rem;
+ border: 1px solid rgba(156, 207, 255, 0.26);
+ border-radius: 0.75rem;
+ background:
+ linear-gradient(180deg, rgba(156, 207, 255, 0.16), rgba(156, 207, 255, 0.055)), rgba(7, 17, 31, 0.46);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.08),
+ inset 0 -1px 0 rgba(156, 207, 255, 0.08),
+ 0 18px 34px rgba(3, 10, 20, 0.16);
+ -webkit-backdrop-filter: blur(18px) saturate(145%);
+ backdrop-filter: blur(18px) saturate(145%);
+ color: var(--fg);
+ font: inherit;
+ text-align: left;
+ cursor: pointer;
+ transition:
+ background 0.2s,
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.15s var(--ease-out-quint);
+}
+
+@media (hover: hover) and (pointer: fine) {
+ .installCommand:hover {
+ border-color: rgba(156, 207, 255, 0.42);
+ background:
+ linear-gradient(180deg, rgba(156, 207, 255, 0.21), rgba(156, 207, 255, 0.075)), rgba(8, 24, 42, 0.5);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
+ inset 0 -1px 0 rgba(156, 207, 255, 0.1),
+ 0 20px 40px rgba(3, 10, 20, 0.2);
+ transform: translateY(-1px);
+ }
+}
+
+.installCommand:active {
+ transform: scale(0.99);
+}
+
+.installCommand:focus-visible {
+ outline: 2px solid color-mix(in srgb, var(--primary) 55%, white 20%);
+ outline-offset: 3px;
+}
+
+.installPrompt {
+ color: var(--fg-faint);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.9rem;
+ flex-shrink: 0;
+}
+
+.installCommand code {
+ min-width: 0;
+ flex: 1;
+ overflow: hidden;
+ color: var(--fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.86rem;
+ line-height: 1.4;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.installCopy {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+ color: var(--fg-muted);
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ flex-shrink: 0;
+}
+
+.installCopy svg {
+ width: 0.95rem;
+ height: 0.95rem;
+}
+
+.ctaPrimary {
+ composes: btn btn-primary from global;
+}
+
+.ctaSecondary {
+ composes: btn btn-secondary from global;
+}
+
+.installSection {
+ display: flex;
+ justify-content: center;
+ padding: 0 0 82px;
+ background: transparent;
+ border: 0;
+ position: relative;
+ z-index: 3;
+}
+
+.installInner {
+ position: relative;
+ width: 100%;
+ max-width: 800px;
+ display: grid;
+ gap: 30px;
+ padding: 40px 40px 0;
+}
+
+.installInner::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 40px;
+ right: 40px;
+ height: 1px;
+ background: linear-gradient(
+ 90deg,
+ transparent,
+ color-mix(in srgb, var(--primary) 38%, transparent),
+ transparent
+ );
+}
+
+.installInner::after {
+ content: '';
+ position: absolute;
+ top: -48px;
+ left: 50%;
+ width: min(560px, calc(100% - 80px));
+ height: 96px;
+ pointer-events: none;
+ transform: translateX(-50%);
+ background: radial-gradient(
+ ellipse at center,
+ color-mix(in srgb, var(--primary) 13%, transparent),
+ transparent 68%
+ );
+ opacity: 0.72;
+}
+
+.installInner > * {
+ position: relative;
+ z-index: 1;
+}
+
+.waitlistSection {
+ --card-bg: #0d1724;
+ --line: rgba(116, 184, 226, 0.22);
+ position: relative;
+ z-index: 3;
+ overflow: hidden;
+ padding: 106px 40px 112px;
+ background: #08111a;
+ border-top: 1px solid rgba(116, 184, 226, 0.08);
+ border-bottom: 1px solid rgba(116, 184, 226, 0.08);
+}
+
+.waitlistSection::before {
+ content: none;
+}
+
+.waitlistInner {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 38px;
+ max-width: 760px;
+ margin: 0 auto;
+ text-align: center;
+}
+
+.waitlistCopy {
+ display: grid;
+ gap: 18px;
+}
+
+.waitlistTitle {
+ margin: 0;
+ color: rgba(237, 244, 251, 0.9);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2rem, 3vw, 2.6rem);
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ line-height: 1.1;
+}
+
+.waitlistSubtitle {
+ max-width: 64ch;
+ margin: 0 auto;
+ color: rgba(168, 184, 200, 0.92);
+ font-size: 1.05rem;
+ line-height: 1.65;
+}
+
+.waitlistFormPanel {
+ min-width: 0;
+}
+
+.waitlistFormPanel form {
+ max-width: 620px;
+}
+
+.installHeader {
+ display: block;
+}
+
+.installHeaderText {
+ width: 100%;
+ max-width: 720px;
+ margin: 0 auto;
+ display: grid;
+ gap: 8px;
+}
+
+.installTitleRow {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 24px;
+ min-height: 42px;
+}
+
+.installTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.3rem, 2vw, 1.7rem);
+ font-weight: 600;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ margin: 0;
+}
+
+.installSubtitle {
+ max-width: 60ch;
+ margin: 0;
+ color: var(--fg-muted);
+ font-size: 0.92rem;
+ line-height: 1.6;
+}
+
+.installAgentLogos {
+ position: relative;
+ display: flex;
+ flex: 0 0 auto;
+ flex-wrap: nowrap;
+ justify-content: flex-end;
+ gap: 0;
+ width: 260px;
+ height: 42px;
+ padding-right: 0;
+}
+
+.installAgentLogo {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 42px;
+ height: 42px;
+ margin-right: -14px;
+ color: var(--fg);
+ filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.28));
+ transition:
+ color 0.2s,
+ filter 0.2s,
+ margin-right 0.2s,
+ transform 0.2s;
+}
+
+.installAgentLogos:hover .installAgentLogo {
+ margin-right: 10px;
+}
+
+.installAgentTooltip {
+ position: absolute;
+ top: auto;
+ bottom: calc(100% + 10px);
+ right: 0;
+ width: max-content;
+ max-width: 290px;
+ padding: 0.58rem 0.72rem;
+ border: 1px solid color-mix(in srgb, var(--primary) 22%, var(--line));
+ border-radius: 8px;
+ background: color-mix(in srgb, var(--bg-elevated) 94%, var(--primary) 6%);
+ box-shadow: 0 14px 32px rgba(0, 0, 0, 0.28);
+ color: var(--fg);
+ font-size: 0.78rem;
+ font-weight: 600;
+ line-height: 1.35;
+ opacity: 0;
+ pointer-events: none;
+ transform: translateY(4px);
+ transition:
+ opacity 0.16s,
+ transform 0.16s;
+ z-index: 4;
+}
+
+.installAgentLogos:hover .installAgentTooltip {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.installAgentLogo:hover {
+ filter: drop-shadow(0 12px 18px rgba(0, 0, 0, 0.34));
+ transform: translateY(-3px);
+}
+
+.installAgentLogoIcon {
+ width: 2rem;
+ height: 2rem;
+ color: currentColor;
+ flex-shrink: 0;
+}
+
+.installActions {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 12px;
+ align-items: stretch;
+ width: 100%;
+ max-width: 720px;
+ margin: 0 auto;
+}
+
+.skillCallout {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 18px;
+ width: 100%;
+ max-width: 720px;
+ margin: -4px auto 0;
+ padding: 14px 16px;
+ border: 1px solid rgba(156, 207, 255, 0.18);
+ border-radius: 8px;
+ background: rgba(7, 17, 31, 0.36);
+ color: var(--fg-muted);
+}
+
+.skillCalloutText {
+ display: grid;
+ gap: 4px;
+ min-width: 0;
+ font-size: 0.88rem;
+ line-height: 1.5;
+}
+
+.skillCalloutText strong {
+ color: var(--fg);
+ font-size: 0.82rem;
+ font-weight: 800;
+ letter-spacing: 0.04em;
+ line-height: 1;
+ text-transform: uppercase;
+}
+
+.skillCalloutLink {
+ flex-shrink: 0;
+ color: rgba(180, 220, 255, 0.9);
+ font-size: 0.82rem;
+ font-weight: 800;
+ text-decoration: none;
+}
+
+.skillCalloutLink:hover {
+ color: var(--fg);
+}
+
+.agentPromptCopy {
+ position: relative;
+ min-height: 64px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 14px;
+ padding: 0.72rem 0.9rem;
+ border: 1px solid rgba(156, 207, 255, 0.14);
+ border-radius: 0.75rem;
+ background:
+ linear-gradient(180deg, rgba(156, 207, 255, 0.055), rgba(156, 207, 255, 0.012)), rgba(5, 12, 22, 0.5);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.035),
+ inset 0 -1px 0 rgba(156, 207, 255, 0.035),
+ 0 10px 24px rgba(3, 10, 20, 0.1);
+ -webkit-backdrop-filter: blur(18px) saturate(145%);
+ backdrop-filter: blur(18px) saturate(145%);
+ color: color-mix(in srgb, var(--fg) 76%, transparent);
+ font: inherit;
+ text-align: left;
+ cursor: pointer;
+ transition:
+ background 0.2s,
+ border-color 0.2s,
+ box-shadow 0.2s,
+ color 0.2s,
+ transform 0.15s var(--ease-out-quint);
+}
+
+@media (hover: hover) and (pointer: fine) {
+ .agentPromptCopy:hover {
+ border-color: rgba(156, 207, 255, 0.22);
+ background:
+ linear-gradient(180deg, rgba(156, 207, 255, 0.08), rgba(156, 207, 255, 0.024)), rgba(7, 17, 31, 0.5);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.05),
+ inset 0 -1px 0 rgba(156, 207, 255, 0.05),
+ 0 12px 28px rgba(3, 10, 20, 0.12);
+ transform: translateY(-1px);
+ }
+}
+
+.agentPromptCopy:active {
+ transform: scale(0.99);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .installCommand:hover,
+ .installCommand:active,
+ .agentPromptCopy:hover,
+ .agentPromptCopy:active {
+ transform: none;
+ }
+}
+
+.agentPromptCopy:focus-within {
+ outline: 2px solid color-mix(in srgb, var(--primary) 55%, white 20%);
+ outline-offset: 3px;
+}
+
+.agentPromptText {
+ position: relative;
+ min-width: 0;
+ flex: 1 1 auto;
+ display: grid;
+ gap: 4px;
+ padding-left: 14px;
+ transition: margin-left 0.18s ease;
+}
+
+.agentPromptText::before {
+ position: absolute;
+ top: 2px;
+ bottom: 2px;
+ left: 0;
+ width: 3px;
+ border-radius: 999px;
+ background: rgba(116, 184, 226, 0.48);
+ content: '';
+}
+
+.agentPromptPreview {
+ display: block;
+ max-width: none;
+ overflow: hidden;
+ color: rgba(196, 211, 226, 0.72);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ line-height: 1.45;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.agentPromptPanel {
+ position: absolute;
+ right: 0;
+ bottom: calc(100% + 8px);
+ width: min(560px, calc(100vw - 48px));
+ max-height: min(440px, calc(100vh - 160px));
+ overflow: auto;
+ padding: 0;
+ border: 1px solid color-mix(in srgb, var(--primary) 24%, var(--line));
+ border-radius: 8px;
+ background: color-mix(in srgb, var(--bg-elevated) 96%, var(--primary) 4%);
+ box-shadow: 0 18px 44px rgba(0, 0, 0, 0.34);
+ color: var(--fg);
+ z-index: 8;
+}
+
+.agentPromptPanelTitle {
+ position: sticky;
+ top: 0;
+ display: block;
+ padding: 0.72rem 0.85rem;
+ border-bottom: 1px solid var(--line);
+ background: color-mix(in srgb, var(--bg-elevated) 96%, var(--primary) 4%);
+ color: var(--primary);
+ font-size: 0.76rem;
+ font-weight: 800;
+ letter-spacing: 0.04em;
+ line-height: 1;
+ text-transform: uppercase;
+}
+
+.agentPromptPanelBody {
+ display: block;
+ padding: 0.85rem;
+ color: var(--fg-muted);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.78rem;
+ line-height: 1.5;
+ white-space: pre-wrap;
+}
+
+.agentPromptActions {
+ display: inline-flex;
+ align-items: center;
+ flex-shrink: 0;
+}
+
+.agentPromptShow,
+.agentPromptCta {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+ padding: 0;
+ border: none;
+ background: transparent;
+ font: inherit;
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ cursor: pointer;
+}
+
+.agentPromptShow {
+ position: absolute;
+ top: 50%;
+ left: 0.9rem;
+ color: var(--fg-muted);
+ opacity: 0;
+ pointer-events: none;
+ white-space: nowrap;
+ transform: translate(4px, -50%);
+ transition:
+ color 0.18s,
+ opacity 0.18s,
+ transform 0.18s;
+}
+
+.agentPromptCopy:hover .agentPromptText,
+.agentPromptCopy:focus .agentPromptText,
+.agentPromptCopy:focus-within .agentPromptText,
+.agentPromptCopy[data-prompt-open='true'] .agentPromptText {
+ margin-left: 7.9rem;
+}
+
+.agentPromptCopy:hover .agentPromptShow,
+.agentPromptCopy:focus .agentPromptShow,
+.agentPromptCopy:focus-within .agentPromptShow,
+.agentPromptCopy[data-prompt-open='true'] .agentPromptShow {
+ opacity: 1;
+ pointer-events: auto;
+ transform: translate(0, -50%);
+}
+
+.agentPromptShow:hover {
+ color: var(--fg);
+}
+
+.agentPromptCta {
+ color: rgba(156, 207, 255, 0.62);
+ pointer-events: none;
+ transition: color 0.18s ease;
+}
+
+.agentPromptCopy:hover .agentPromptCta,
+.agentPromptCopy:focus .agentPromptCta,
+.agentPromptCopy:focus-within .agentPromptCta {
+ color: rgba(180, 220, 255, 0.82);
+}
+
+.agentPromptShow svg,
+.agentPromptCta svg {
+ width: 0.95rem;
+ height: 0.95rem;
+}
+
+/* ---- FEATURES SECTION ---- */
+
+.featuresWrapper {
+ background: #0b1c2a;
+ transition: background 0.3s ease;
+ position: relative;
+ z-index: 3;
+}
+
+.featuresSection {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 0;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 0 40px 96px;
+}
+
+.featureCol {
+ position: relative;
+ display: grid;
+ grid-template-columns: minmax(0, 1.05fr) minmax(320px, 0.95fr);
+ align-items: center;
+ gap: clamp(40px, 6vw, 84px);
+ min-height: 430px;
+ padding: 72px 0;
+ isolation: isolate;
+}
+
+.featureCol::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 50%;
+ width: 100vw;
+ transform: translateX(-50%);
+ background: var(--feature-band-bg, #0b1c2a);
+ border-top: 1px solid color-mix(in srgb, white 5%, transparent);
+ z-index: 0;
+}
+
+.featureCol > * {
+ position: relative;
+ z-index: 1;
+}
+
+.messagingFeature {
+ order: 1;
+ --feature-band-bg: #0b1c2a;
+}
+
+.deliveryFeature {
+ order: 3;
+ --feature-band-bg: #0b1c2a;
+}
+
+.deliveryFeature .featurePreview {
+ order: 1;
+}
+
+.deliveryFeature .featureCopy {
+ order: 2;
+}
+
+.deliveryFeature::after {
+ content: none;
+}
+
+.commandsFeature {
+ order: 5;
+ --feature-band-bg: #0b1c2a;
+ padding-top: 36px;
+}
+
+.commandsFeature::before {
+ border-top: 0;
+}
+
+.commandsFeature .featurePreview {
+ order: 2;
+}
+
+.commandsFeature .featureCopy {
+ order: 1;
+ max-width: 620px;
+}
+
+/* The "works with every agent" band takes the slot between the messaging
+ feature (order 1) and the delivery feature (order 3), carrying its own band
+ borders. */
+.howItWorks {
+ order: 2;
+}
+
+.featureSeparator.a2aSeparator {
+ order: 6;
+}
+
+.a2aFeature {
+ order: 7;
+ --feature-band-bg: #0b1c2a;
+ padding-top: 36px;
+}
+
+.a2aFeature::before {
+ border-top: 0;
+}
+
+.a2aFeature .featurePreview {
+ order: 1;
+}
+
+.a2aFeature .featureCopy {
+ order: 2;
+ max-width: 620px;
+}
+
+.featureSeparator {
+ position: relative;
+ order: 4;
+ grid-column: 1 / -1;
+ width: 100vw;
+ height: 70px;
+ margin: -56px calc(50% - 50vw) -72px;
+ isolation: isolate;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+.featureSeparator::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 128vw;
+ height: 1px;
+ transform: translate(-50%, -50%) rotate(2deg);
+ background: linear-gradient(
+ 90deg,
+ transparent 0%,
+ rgba(116, 184, 226, 0.05) 18%,
+ rgba(116, 184, 226, 0.2) 50%,
+ rgba(116, 184, 226, 0.05) 82%,
+ transparent 100%
+ );
+ z-index: 0;
+}
+
+.featureSeparatorWaves {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ z-index: 1;
+ width: 128vw;
+ height: 78px;
+ color: #74b8e2;
+ opacity: 0.4;
+ overflow: visible;
+ transform: translate(-50%, -50%) rotate(2deg);
+ transform-origin: center;
+}
+
+.featureSeparatorWaves path {
+ stroke: currentColor;
+ stroke-width: 1.2;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+}
+
+.featureSeparatorWaves path:nth-child(2) {
+ opacity: 0.5;
+}
+
+/* A2A separator: a livelier sibling of the plain feature wave. A gradient
+ strand (blue → pear-green → blue) with a soft glow, an extra ghost strand,
+ an opposite tilt, and a gentle horizontal drift. */
+.a2aSeparatorWaves {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ z-index: 1;
+ width: 132vw;
+ height: 96px;
+ overflow: visible;
+ opacity: 0.5;
+ transform: translate(-50%, -50%) rotate(-2.4deg);
+ transform-origin: center;
+ filter: drop-shadow(0 0 4px rgba(116, 184, 226, 0.15));
+}
+
+.a2aSeparatorWaves path {
+ stroke: url(#a2aWaveStroke);
+ stroke-linecap: round;
+ fill: none;
+ vector-effect: non-scaling-stroke;
+}
+
+.a2aWaveLead {
+ stroke-width: 1.2;
+ opacity: 0.6;
+}
+
+.a2aWaveTrail {
+ stroke-width: 1;
+ opacity: 0.32;
+}
+
+.a2aWaveGhost {
+ stroke-width: 0.9;
+ opacity: 0.18;
+}
+
+.a2aWaveFlow {
+ animation: a2aWaveDrift 9s ease-in-out infinite alternate;
+ will-change: transform;
+}
+
+@keyframes a2aWaveDrift {
+ from {
+ transform: translateX(-14px);
+ }
+ to {
+ transform: translateX(14px);
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .a2aWaveFlow {
+ animation: none;
+ }
+}
+
+.messagingFeature .featurePreview {
+ order: 2;
+}
+
+.messagingFeature .featureCopy {
+ order: 1;
+}
+
+.featurePreview {
+ position: relative;
+ border-radius: 14px;
+ overflow: hidden;
+ margin-bottom: 0;
+ height: clamp(340px, 36vw, 440px);
+ border: 1px solid var(--line);
+ background: var(--surface);
+}
+
+.featureCopy {
+ display: grid;
+ gap: 16px;
+ max-width: 460px;
+}
+
+.messagingFeature .featureCopy,
+.deliveryFeature .featureCopy {
+ max-width: 620px;
+}
+
+.capabilityBand {
+ position: relative;
+ order: 3;
+ grid-column: 1 / -1;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 24px;
+ margin-top: 28px;
+ padding: 64px 0 76px;
+ isolation: isolate;
+}
+
+.capabilityBand::before {
+ content: none;
+}
+
+.capabilityBand > * {
+ position: relative;
+ z-index: 1;
+}
+
+.capabilityDivider {
+ position: relative;
+ grid-column: 1 / -1;
+ width: 100vw;
+ height: 70px;
+ margin: -104px calc(50% - 50vw) 44px;
+ isolation: isolate;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+.capabilityDivider::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 128vw;
+ height: 1px;
+ transform: translate(-50%, -50%) rotate(-2deg);
+ background: linear-gradient(
+ 90deg,
+ transparent 0%,
+ rgba(116, 184, 226, 0.05) 18%,
+ rgba(116, 184, 226, 0.2) 50%,
+ rgba(116, 184, 226, 0.05) 82%,
+ transparent 100%
+ );
+ z-index: 0;
+}
+
+.capabilityDividerWaves {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ z-index: 1;
+ width: 128vw;
+ height: 92px;
+ color: #74b8e2;
+ opacity: 0.42;
+ overflow: visible;
+ transform: translate(-50%, -50%) rotate(-2deg);
+ transform-origin: center;
+}
+
+.capabilityDividerWaves path {
+ stroke: currentColor;
+ stroke-width: 1.4;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+}
+
+.capabilityDividerWaves path:nth-child(2) {
+ opacity: 0.42;
+}
+
+.capabilityDividerWaves path:nth-child(3) {
+ opacity: 0.28;
+}
+
+.capabilityHeader {
+ display: grid;
+ grid-column: 1 / -1;
+ gap: 12px;
+ max-width: 720px;
+ margin: 0 auto 40px;
+ text-align: center;
+}
+
+.capabilityHeader h3 {
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2rem, 3.4vw, 3rem);
+ font-weight: 650;
+ line-height: 1.08;
+ letter-spacing: 0;
+}
+
+.capabilityHeader p {
+ margin: 0;
+ color: color-mix(in srgb, var(--fg-muted) 92%, white 8%);
+ font-size: 1.04rem;
+ line-height: 1.65;
+}
+
+.capabilityItem {
+ display: grid;
+ align-content: start;
+ gap: 18px;
+ min-width: 0;
+}
+
+.capabilityPreview {
+ height: 260px;
+}
+
+.realtimeCapabilityPreview,
+.searchCapabilityPreview {
+ background: #07111f;
+}
+
+.realtimeCapabilityPreview .previewAccentGemini,
+.searchCapabilityPreview .previewAccentSearch {
+ display: none;
+}
+
+.realtimeCapabilityPreview .realtimePreview {
+ border: 0;
+ border-radius: 0;
+ background: transparent;
+}
+
+.realtimeCapabilityPreview .realtimeEvent,
+.searchCapabilityPreview .searchBar,
+.searchCapabilityPreview .searchResults > div {
+ border: 1px solid rgba(156, 207, 255, 0.13);
+ background: #0e1729;
+ box-shadow: 0 10px 24px rgba(2, 7, 18, 0.22);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+}
+
+.searchCapabilityPreview .searchPreview {
+ align-content: start;
+ gap: 0;
+ overflow: hidden;
+ padding: 14px;
+ border: 1px solid rgba(156, 207, 255, 0.15);
+ border-radius: 14px;
+ background: #0e1729;
+ box-shadow: 0 18px 38px rgba(2, 7, 18, 0.3);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+}
+
+.searchCapabilityPreview .searchBar {
+ border: 0;
+ border-bottom: 1px solid rgba(156, 207, 255, 0.12);
+ border-radius: 0;
+ background: transparent;
+ box-shadow: none;
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+}
+
+.searchCapabilityPreview .searchResults > div {
+ border: 0;
+ border-radius: 0;
+ background: transparent;
+ box-shadow: none;
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+}
+
+.realtimeCapabilityPreview .realtimeEvent code,
+.realtimeCapabilityPreview .realtimeEventAgent {
+ color: rgba(255, 255, 255, 0.94);
+}
+
+.realtimeCapabilityPreview .realtimeEvent span {
+ color: rgba(226, 232, 240, 0.72);
+}
+
+.realtimeCapabilityPreview .realtimeHarnessLogo,
+.realtimeCapabilityPreview .realtimeEvent strong {
+ color: rgba(226, 232, 240, 0.58);
+}
+
+.webhookCapabilityPreview {
+ background: #07111e;
+}
+
+.webhookCapabilityPreview .webhookPreview {
+ padding: 0;
+ justify-content: stretch;
+}
+
+.webhookCapabilityPreview .webhookCodeTitle {
+ border-top: 0;
+ border-right: 0;
+ border-left: 0;
+ border-radius: 0;
+ border-bottom-color: rgba(156, 207, 255, 0.2);
+ background: linear-gradient(180deg, rgba(74, 144, 194, 0.34), rgba(22, 47, 72, 0.56));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16);
+ color: rgba(248, 250, 252, 0.82);
+ backdrop-filter: blur(16px) saturate(1.35);
+ -webkit-backdrop-filter: blur(16px) saturate(1.35);
+}
+
+.webhookCapabilityPreview .webhookCodeSnippet {
+ flex: 1;
+ min-height: 0;
+ border-right: 0;
+ border-bottom: 0;
+ border-left: 0;
+ border-radius: 0;
+ box-shadow: none;
+}
+
+.capabilityPreview .searchPreview {
+ inset: 18px;
+}
+
+.capabilityPreview .searchBar {
+ min-height: 44px;
+ padding: 0 4px 12px;
+}
+
+.capabilityPreview .searchResults {
+ gap: 0;
+}
+
+.capabilityPreview .searchResults > div {
+ min-height: 50px;
+ padding: 12px 4px;
+}
+
+.capabilityPreview .searchResults > div + div {
+ border-top: 1px solid rgba(156, 207, 255, 0.1);
+}
+
+.capabilityCopy {
+ display: grid;
+ gap: 10px;
+}
+
+.capabilityCopy h3 {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.55rem;
+ font-weight: 650;
+ line-height: 1.15;
+ letter-spacing: 0;
+ color: var(--fg);
+ margin: 0;
+}
+
+.capabilityCopy p {
+ margin: 0;
+ color: color-mix(in srgb, var(--fg-muted) 92%, white 8%);
+ font-size: 0.98rem;
+ line-height: 1.65;
+}
+
+.featureTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 3rem;
+ font-weight: 600;
+ line-height: 1.05;
+ letter-spacing: 0;
+ color: var(--fg);
+ margin: 0;
+}
+
+.titleUnderlineWord {
+ position: relative;
+ display: inline-block;
+}
+
+.titleUnderline {
+ position: absolute;
+ left: 0;
+ top: 100%;
+ width: 100%;
+ height: 0.34em;
+ margin-top: -0.12em;
+ color: #74b8e2;
+ opacity: 0.6;
+ overflow: visible;
+ transform: rotate(-3deg);
+ transform-origin: left center;
+}
+
+.titleUnderline path {
+ fill: none;
+ stroke: currentColor;
+ stroke-width: 1.4;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+}
+
+.titleUnderline path:nth-child(2) {
+ opacity: 0.55;
+}
+
+.actionToolBadges {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ margin-top: 2px;
+}
+
+.actionToolBadge {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+ color: rgba(196, 211, 226, 0.74);
+ cursor: default;
+ transition:
+ color 0.18s ease,
+ transform 0.18s ease;
+}
+
+.actionToolBadge:hover,
+.actionToolBadge:focus-visible {
+ color: #ffffff;
+ transform: translateY(-1px);
+}
+
+.actionToolBadge:focus-visible {
+ outline: 1px solid color-mix(in srgb, var(--primary) 48%, white 14%);
+ outline-offset: 4px;
+}
+
+.actionToolBadge svg {
+ flex: none;
+}
+
+.actionToolTooltip {
+ position: absolute;
+ bottom: calc(100% + 12px);
+ left: 0;
+ z-index: 8;
+ width: min(330px, calc(100vw - 48px));
+ padding: 12px 13px;
+ border: 1px solid rgba(156, 207, 255, 0.2);
+ border-radius: 10px;
+ background: rgba(7, 17, 31, 0.94);
+ box-shadow: 0 18px 42px rgba(3, 10, 20, 0.36);
+ color: rgba(214, 228, 241, 0.9);
+ font-size: 0.78rem;
+ font-weight: 500;
+ line-height: 1.45;
+ opacity: 0;
+ pointer-events: none;
+ transform: translateY(5px);
+ transition:
+ opacity 0.16s ease,
+ transform 0.16s ease;
+}
+
+.actionToolTooltip strong {
+ display: block;
+ margin-bottom: 4px;
+ color: #ffffff;
+ font-size: 0.72rem;
+ font-weight: 800;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+}
+
+.actionToolBadge:hover .actionToolTooltip,
+.actionToolBadge:focus-visible .actionToolTooltip {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.featureDesc {
+ font-size: 1.05rem;
+ line-height: 1.7;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.featureList {
+ display: grid;
+ gap: 12px;
+ margin: 0;
+ padding: 0;
+ color: var(--fg-muted);
+ font-size: 1.05rem;
+ line-height: 1.55;
+ list-style: none;
+}
+
+.featureList li {
+ position: relative;
+ padding-left: 22px;
+}
+
+.featureList li::before {
+ content: '';
+ position: absolute;
+ top: 0.68em;
+ left: 0;
+ width: 7px;
+ height: 7px;
+ border-radius: 999px;
+ background: #58a6ff;
+ box-shadow: 0 0 12px rgba(88, 166, 255, 0.26);
+}
+
+/* Preview: Accent background */
+.previewAccent {
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(135deg, #c1674b 0%, #b45542 50%, #cd866a 100%);
+ z-index: 0;
+}
+
+/* Swooping lines overlay */
+.previewAccent::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background:
+ /* Swoop 1 — large arc top-left to bottom-right */
+ radial-gradient(
+ ellipse 120% 80% at -20% 10%,
+ rgba(255, 255, 255, 0.3) 0%,
+ rgba(255, 255, 255, 0.3) 1px,
+ transparent 2px
+ ),
+ radial-gradient(
+ ellipse 120% 80% at -15% 15%,
+ rgba(255, 255, 255, 0.2) 0%,
+ rgba(255, 255, 255, 0.2) 1px,
+ transparent 2px
+ ),
+ /* Swoop 2 — mid arc */
+ radial-gradient(
+ ellipse 100% 60% at 0% 50%,
+ rgba(255, 255, 255, 0.25) 0%,
+ rgba(255, 255, 255, 0.25) 1px,
+ transparent 2px
+ ),
+ radial-gradient(
+ ellipse 100% 60% at 5% 55%,
+ rgba(255, 255, 255, 0.15) 0%,
+ rgba(255, 255, 255, 0.15) 1px,
+ transparent 2px
+ ),
+ /* Broad wash */
+ radial-gradient(ellipse 80% 100% at -10% 40%, rgba(255, 255, 255, 0.08) 0%, transparent 60%);
+}
+
+.previewAccent::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ overflow: hidden;
+ /* SVG swooping curves as inline background */
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 320'%3E%3Cpath d='M-20,80 Q100,20 250,120 T500,90' fill='none' stroke='rgba(255,255,255,0.3)' stroke-width='2'/%3E%3Cpath d='M-20,130 Q120,60 280,160 T520,130' fill='none' stroke='rgba(255,255,255,0.2)' stroke-width='1.5'/%3E%3Cpath d='M-20,180 Q80,120 200,200 T480,170' fill='none' stroke='rgba(255,255,255,0.15)' stroke-width='1.5'/%3E%3Cpath d='M-20,240 Q100,180 260,260 T500,230' fill='none' stroke='rgba(255,255,255,0.12)' stroke-width='1'/%3E%3Cpath d='M-20,280 Q140,230 300,290 T520,270' fill='none' stroke='rgba(255,255,255,0.08)' stroke-width='1'/%3E%3C/svg%3E");
+ background-size: cover;
+ background-position: center;
+}
+
+/* Preview: Terminal */
+.previewTerminal {
+ position: relative;
+ z-index: 1;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ background: var(--terminal-bg);
+ border-radius: 10px 10px 0 0;
+ margin: 14px 0 0 14px;
+ overflow: hidden;
+ box-shadow: -4px -4px 24px color-mix(in srgb, black 20%, transparent);
+}
+
+.previewTitleBar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 14px;
+ background: var(--terminal-panel);
+ border-bottom: 1px solid var(--terminal-line);
+}
+
+.previewDots {
+ display: flex;
+ gap: 6px;
+}
+
+.previewDots span {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+}
+
+.previewTitleText {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.68rem;
+ color: var(--terminal-muted);
+}
+
+.previewCode {
+ flex: 1;
+ margin: 0;
+ padding: 16px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ line-height: 1.65;
+ color: var(--terminal-fg);
+ background: transparent;
+ border: none;
+ overflow: hidden;
+ white-space: pre;
+}
+
+/* Terminal syntax colors */
+.codeKeyword {
+ color: var(--terminal-keyword);
+}
+.codeString {
+ color: var(--terminal-string);
+}
+.codeType {
+ color: var(--terminal-type);
+}
+.codeMethod {
+ color: var(--primary);
+}
+.codeComment {
+ color: var(--terminal-muted);
+ font-style: italic;
+}
+.codeCursor {
+ color: var(--terminal-keyword);
+ animation: blink 1.2s step-end infinite;
+}
+
+@keyframes blink {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0;
+ }
+}
+
+/* Preview: Chat */
+.previewChat {
+ position: absolute;
+ top: 14px;
+ right: 0;
+ bottom: 0;
+ left: 34px;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ padding: 12px;
+ gap: 8px;
+ border: 1px solid var(--line);
+ border-radius: 12px 0 12px 0;
+ background: color-mix(in srgb, var(--surface) 94%, white 6%);
+ box-shadow: -10px 10px 28px color-mix(in srgb, var(--fg) 14%, transparent);
+ overflow: hidden;
+}
+
+.chatChannelHeader {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ min-height: 34px;
+ padding: 0 10px;
+ border: 1px solid var(--line);
+ border-radius: 10px;
+ background: color-mix(in srgb, var(--card-bg) 76%, white 5%);
+ flex-shrink: 0;
+}
+
+.chatChannelHash {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 18px;
+ height: 18px;
+ border-radius: 6px;
+ background: color-mix(in srgb, #58a6ff 22%, transparent);
+ color: #58a6ff;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ font-weight: 900;
+}
+
+.chatChannelHeader strong {
+ overflow: hidden;
+ color: var(--fg);
+ font-size: 0.76rem;
+ font-weight: 800;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.chatChannelMeta {
+ margin-left: auto;
+ color: var(--fg-faint);
+ font-size: 0.62rem;
+ font-weight: 700;
+ white-space: nowrap;
+}
+
+.chatStream {
+ display: flex;
+ flex: 1;
+ min-height: 0;
+ flex-direction: column;
+ gap: 7px;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ overscroll-behavior: auto;
+ scrollbar-width: none;
+}
+
+.chatStream::-webkit-scrollbar {
+ display: none;
+}
+
+.chatMsg {
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ padding: 8px 10px;
+ border-radius: 10px;
+ background: var(--card-bg);
+ border: 1px solid var(--line);
+ flex-shrink: 0;
+}
+
+.chatMsgPlanner .chatName,
+.chatMsgBuilder .chatName,
+.chatMsgReviewer .chatName {
+ color: color-mix(in srgb, var(--fg) 82%, white 18%);
+}
+
+.chatNameRow {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.chatIcon {
+ width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+}
+
+.chatIconPlanner {
+ color: #c1674b;
+}
+
+.chatIconBuilder {
+ color: #a8b0bd;
+}
+
+.chatIconReviewer {
+ color: #8fb7d4;
+}
+
+.chatName {
+ font-size: 0.68rem;
+ font-weight: 700;
+ color: color-mix(in srgb, var(--fg) 82%, white 18%);
+}
+
+.chatText {
+ font-size: 0.7rem;
+ line-height: 1.42;
+ color: var(--fg-muted);
+}
+
+.chatMention {
+ color: #58a6ff;
+ font-weight: 700;
+}
+
+.chatReactions {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 5px;
+ margin-top: 5px;
+}
+
+.chatReaction {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ height: 22px;
+ min-width: 30px;
+ border-radius: 999px;
+ font-size: 0.72rem;
+ line-height: 1;
+ transform-origin: left center;
+ animation: chatReactionPop 220ms ease-out both;
+}
+
+.chatReaction {
+ gap: 5px;
+ padding: 0 8px;
+ border: 1px solid color-mix(in srgb, #58a6ff 58%, transparent);
+ background: color-mix(in srgb, #0ea5e9 30%, var(--card-bg) 70%);
+ color: var(--fg);
+}
+
+.chatReaction strong {
+ font-size: 0.68rem;
+ font-weight: 800;
+ color: color-mix(in srgb, #bfdbfe 82%, white 18%);
+}
+
+@keyframes chatReactionPop {
+ from {
+ opacity: 0;
+ transform: translateY(-2px) scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+.chatReply {
+ position: relative;
+ margin-left: 24px;
+}
+
+.chatReply::before {
+ content: '';
+ position: absolute;
+ top: -4px;
+ bottom: 4px;
+ left: -13px;
+ width: 1px;
+ background: color-mix(in srgb, #58a6ff 48%, transparent);
+}
+
+.chatInput {
+ margin-top: auto;
+ padding: 8px 12px;
+ border-radius: 10px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ font-size: 0.75rem;
+ color: var(--fg-faint);
+}
+
+.chatInput span::after {
+ content: '';
+ display: inline-block;
+ width: 5px;
+ height: 1em;
+ margin-left: 4px;
+ transform: translateY(2px);
+ background: color-mix(in srgb, var(--primary) 70%, white 30%);
+ animation: blink 1.2s step-end infinite;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .chatReaction,
+ .chatInput span::after {
+ animation: none;
+ }
+}
+
+/* Preview: Gemini accent (reverse L — flush bottom-left) */
+.previewAccentGemini {
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(315deg, #4285f4 0%, #34a853 35%, #fbbc05 65%, #ea4335 100%);
+ z-index: 0;
+}
+
+.previewAccentGemini::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 40% 50% at 80% 70%, rgba(255, 255, 255, 0.15) 0%, transparent 70%),
+ radial-gradient(ellipse 35% 40% at 25% 35%, rgba(255, 255, 255, 0.1) 0%, transparent 70%),
+ radial-gradient(ellipse 50% 30% at 50% 90%, rgba(255, 255, 255, 0.12) 0%, transparent 60%);
+}
+
+.previewAccentGemini::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 320'%3E%3Cpath d='M420,80 Q300,20 150,120 T-100,90' fill='none' stroke='rgba(255,255,255,0.25)' stroke-width='2'/%3E%3Cpath d='M420,140 Q280,70 120,170 T-80,140' fill='none' stroke='rgba(255,255,255,0.18)' stroke-width='1.5'/%3E%3Cpath d='M420,200 Q320,140 200,220 T-80,190' fill='none' stroke='rgba(255,255,255,0.12)' stroke-width='1.5'/%3E%3Cpath d='M420,260 Q300,200 140,280 T-100,250' fill='none' stroke='rgba(255,255,255,0.08)' stroke-width='1'/%3E%3C/svg%3E");
+ background-size: cover;
+}
+
+.previewAccentBlue {
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(135deg, #7eb8da 0%, #4a90c2 48%, #2d6a9c 100%);
+ z-index: 0;
+}
+
+.previewAccentBlue::before {
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 50% 70% at 18% 26%, rgba(255, 255, 255, 0.16) 0%, transparent 70%),
+ radial-gradient(ellipse 42% 50% at 82% 76%, rgba(255, 255, 255, 0.12) 0%, transparent 66%);
+ content: '';
+}
+
+.previewAccentBlue::after {
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 320'%3E%3Cpath d='M-20,70 Q110,20 250,110 T500,80' fill='none' stroke='rgba(255,255,255,0.24)' stroke-width='2'/%3E%3Cpath d='M-20,130 Q120,70 280,160 T520,130' fill='none' stroke='rgba(255,255,255,0.16)' stroke-width='1.5'/%3E%3Cpath d='M-20,210 Q80,160 220,235 T500,210' fill='none' stroke='rgba(255,255,255,0.12)' stroke-width='1.25'/%3E%3C/svg%3E");
+ background-size: cover;
+ content: '';
+}
+
+/* Preview: Dashboard */
+.previewDashboard {
+ position: absolute;
+ inset: 0 34px 14px 0;
+ width: auto;
+ height: auto;
+ z-index: 1;
+ display: block;
+ padding: 0;
+ background: #07111f;
+ border: 1px solid color-mix(in srgb, #9ccfff 15%, transparent);
+ border-radius: 0 0 12px 0;
+ box-shadow: 10px 10px 28px color-mix(in srgb, #020712 42%, transparent);
+ transform: none;
+ overflow: hidden;
+}
+
+.previewDashboard::before {
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(circle at 72% 18%, rgba(74, 144, 194, 0.16), transparent 34%),
+ linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 42%);
+ content: '';
+ pointer-events: none;
+}
+
+.previewAccentWebhook,
+.previewAccentSearch,
+.previewAccentCommand {
+ position: absolute;
+ inset: 0;
+ z-index: 0;
+}
+
+.previewAccentWebhook {
+ background: linear-gradient(45deg, #182235 0%, #24364f 48%, #4a90c2 100%);
+}
+
+.previewAccentSearch {
+ background: #232d3a;
+}
+
+.previewAccentCommand {
+ background: linear-gradient(135deg, #c1674b 0%, #b45542 50%, #cd866a 100%);
+}
+
+.previewAccentWebhook::before,
+.previewAccentCommand::before {
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 40% 50% at 80% 70%, rgba(255, 255, 255, 0.15) 0%, transparent 70%),
+ radial-gradient(ellipse 35% 40% at 25% 35%, rgba(255, 255, 255, 0.1) 0%, transparent 70%),
+ radial-gradient(ellipse 50% 30% at 50% 90%, rgba(255, 255, 255, 0.12) 0%, transparent 60%);
+ content: '';
+}
+
+.previewAccentCommand::after {
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 320'%3E%3Cpath d='M-20,80 Q100,20 250,120 T500,90' fill='none' stroke='rgba(255,255,255,0.22)' stroke-width='2'/%3E%3Cpath d='M-20,140 Q120,70 280,170 T520,140' fill='none' stroke='rgba(255,255,255,0.15)' stroke-width='1.5'/%3E%3Cpath d='M-20,210 Q80,150 220,235 T500,210' fill='none' stroke='rgba(255,255,255,0.1)' stroke-width='1.25'/%3E%3C/svg%3E");
+ background-size: cover;
+ content: '';
+}
+
+.previewAccentWebhook::after {
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 320'%3E%3Cpath d='M-40,245 C50,205 78,122 160,126 C256,131 262,64 440,84' fill='none' stroke='rgba(255,255,255,0.22)' stroke-width='2'/%3E%3Cpath d='M-35,196 C68,180 94,90 186,98 C272,106 304,178 438,145' fill='none' stroke='rgba(255,255,255,0.15)' stroke-width='1.5'/%3E%3Cpath d='M-30,92 C64,130 116,38 198,52 C282,66 310,250 438,216' fill='none' stroke='rgba(255,255,255,0.11)' stroke-width='1.25'/%3E%3Cpath d='M62,340 C112,260 92,174 172,164 C254,154 286,304 370,260' fill='none' stroke='rgba(255,255,255,0.08)' stroke-width='1'/%3E%3C/svg%3E");
+ background-size: cover;
+ content: '';
+}
+
+.searchPreview,
+.commandsPreview {
+ position: absolute;
+ inset: 22px;
+ z-index: 1;
+ display: grid;
+ align-content: center;
+ gap: 12px;
+ color: #f7fafc;
+}
+
+.searchBar,
+.searchResults > div {
+ border: 1px solid rgba(255, 255, 255, 0.14);
+ background: rgba(7, 17, 31, 0.72);
+ box-shadow: 0 14px 32px rgba(2, 7, 18, 0.24);
+ backdrop-filter: blur(12px);
+}
+
+.webhookPreview {
+ position: absolute;
+ inset: 0;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ padding: 20px 0 20px 20px;
+ color: #f7fafc;
+}
+
+.webhookCodeTitle {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ width: 100%;
+ padding: 11px 16px;
+ border: 1px solid rgba(255, 255, 255, 0.14);
+ border-right: 0;
+ border-radius: 10px 0 0 0;
+ background: #111a24;
+ color: rgba(226, 232, 240, 0.72);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.68rem;
+ line-height: 1;
+}
+
+.webhookCodeDots {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ flex: 0 0 auto;
+}
+
+.webhookCodeDots span {
+ width: 9px;
+ height: 9px;
+ border-radius: 50%;
+}
+
+.webhookCodeDots span:nth-child(1) {
+ background: #ff5f57;
+}
+
+.webhookCodeDots span:nth-child(2) {
+ background: #febc2e;
+}
+
+.webhookCodeDots span:nth-child(3) {
+ background: #28c840;
+}
+
+.webhookCodeSnippet {
+ width: 100%;
+ min-height: 174px;
+ margin: 0;
+ padding: 18px 16px 20px;
+ border: 1px solid rgba(255, 255, 255, 0.14);
+ border-top: 0;
+ border-right: 0;
+ border-radius: 0 0 0 10px;
+ background: #07111e;
+ box-shadow: -10px 12px 30px rgba(2, 7, 18, 0.28);
+ color: rgba(248, 250, 252, 0.9);
+ overflow: hidden;
+}
+
+.webhookCodeSnippet code {
+ overflow: hidden;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.7rem;
+ font-weight: 700;
+ line-height: 1.7;
+ text-overflow: ellipsis;
+ white-space: pre-wrap;
+}
+
+.commandPrompt code {
+ overflow: hidden;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ font-weight: 700;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.searchPreview {
+ align-content: start;
+ padding-top: 14px;
+}
+
+.searchBar {
+ display: grid;
+ grid-template-columns: 20px minmax(0, 1fr);
+ align-items: center;
+ gap: 10px;
+ min-height: 48px;
+ padding: 0 14px;
+ border-radius: 999px;
+}
+
+.searchBar svg {
+ width: 18px;
+ height: 18px;
+ color: rgba(248, 250, 252, 0.78);
+}
+
+.searchBar span {
+ overflow: hidden;
+ color: rgba(255, 255, 255, 0.94);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.78rem;
+ font-weight: 700;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.searchQuery {
+ display: inline-flex;
+ align-items: center;
+ min-width: 0;
+}
+
+.searchCursor {
+ display: inline-block;
+ width: 2px;
+ height: 1.05em;
+ margin-left: 2px;
+ border-radius: 999px;
+ background: rgba(156, 207, 255, 0.9);
+ animation: searchCursorBlink 920ms steps(2, start) infinite;
+}
+
+.searchResults {
+ display: grid;
+ gap: 9px;
+}
+
+.searchResults > div,
+.commandMenu > div {
+ display: grid;
+ gap: 4px;
+ min-height: 50px;
+ padding: 10px 12px;
+ border-radius: 10px;
+}
+
+.searchResults strong,
+.commandMenu strong {
+ overflow: hidden;
+ color: #ffffff;
+ font-size: 0.76rem;
+ line-height: 1.1;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.searchResults span,
+.commandMenu span {
+ overflow: hidden;
+ color: rgba(240, 246, 252, 0.68);
+ font-size: 0.68rem;
+ line-height: 1.3;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.searchResults strong {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 3px;
+}
+
+.searchResults strong span {
+ color: #ffffff;
+ font-size: inherit;
+}
+
+.searchResults time {
+ color: rgba(190, 206, 224, 0.48);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.6rem;
+ font-weight: 700;
+}
+
+.searchResults mark {
+ padding: 0 3px;
+ border-radius: 4px;
+ background: rgba(116, 184, 226, 0.18);
+ color: rgba(225, 242, 255, 0.96);
+}
+
+.searchResultVisible {
+ opacity: 1;
+ transform: translateY(0);
+ transition:
+ opacity 260ms ease,
+ transform 260ms ease;
+}
+
+.searchResultHidden {
+ opacity: 0;
+ transform: translateY(8px);
+ transition:
+ opacity 180ms ease,
+ transform 180ms ease;
+}
+
+@keyframes searchCursorBlink {
+ 0%,
+ 45% {
+ opacity: 1;
+ }
+ 46%,
+ 100% {
+ opacity: 0;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .searchCursor {
+ animation: none;
+ }
+
+ .searchResultVisible,
+ .searchResultHidden {
+ transform: none;
+ transition: none;
+ }
+}
+
+.actionsHeader {
+ display: grid;
+ gap: 14px;
+ max-width: 760px;
+}
+
+.actionsHeader .featureTitle {
+ margin-bottom: 0;
+}
+
+.actionsHeader .featureDesc {
+ max-width: 68ch;
+}
+
+.actionsGrid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 24px;
+}
+
+.actionCard {
+ display: grid;
+ align-content: start;
+ gap: 18px;
+ min-width: 0;
+}
+
+.actionCardVisual {
+ display: grid;
+ min-height: 330px;
+ overflow: hidden;
+ border: 1px solid rgba(156, 207, 255, 0.16);
+ border-radius: 14px;
+ background: radial-gradient(circle at 18% 16%, rgba(116, 184, 226, 0.14), transparent 32%), #07111f;
+ box-shadow: 0 18px 38px rgba(2, 7, 18, 0.3);
+}
+
+.toolSurface {
+ display: grid;
+ align-content: center;
+ gap: 12px;
+ min-width: 0;
+ padding: 24px;
+}
+
+.toolSurfaceTitle {
+ display: flex;
+ gap: 8px;
+}
+
+.toolSurfaceTitle span {
+ display: inline-flex;
+ align-items: center;
+ min-height: 24px;
+ padding: 0 10px;
+ border: 1px solid rgba(156, 207, 255, 0.16);
+ border-radius: 999px;
+ background: rgba(156, 207, 255, 0.08);
+ color: rgba(225, 242, 255, 0.82);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.62rem;
+ font-weight: 800;
+ letter-spacing: 0.04em;
+}
+
+.toolCommand,
+.toolList > div {
+ min-width: 0;
+ border: 1px solid rgba(156, 207, 255, 0.12);
+ border-radius: 10px;
+ background: #0e1729;
+}
+
+.toolCommand {
+ display: grid;
+ grid-template-columns: 22px minmax(0, 1fr);
+ align-items: center;
+ min-height: 44px;
+ gap: 8px;
+ padding: 0 12px;
+}
+
+.toolCommand span {
+ color: rgba(116, 184, 226, 0.84);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ font-weight: 900;
+}
+
+.toolCommand code {
+ overflow: hidden;
+ color: rgba(248, 250, 252, 0.9);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ font-weight: 750;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.toolList {
+ display: grid;
+ gap: 8px;
+ margin-top: 2px;
+}
+
+.toolList > div {
+ display: grid;
+ gap: 4px;
+ min-height: 52px;
+ padding: 10px 12px;
+}
+
+.toolList strong {
+ overflow: hidden;
+ color: #ffffff;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ line-height: 1.1;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.toolList span {
+ overflow: hidden;
+ color: rgba(240, 246, 252, 0.62);
+ font-size: 0.68rem;
+ line-height: 1.3;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.editorWindow {
+ align-self: center;
+ justify-self: stretch;
+ min-width: 0;
+ margin: 22px;
+ overflow: hidden;
+ border: 1px solid rgba(156, 207, 255, 0.16);
+ border-radius: 12px;
+ background: #07111e;
+}
+
+.editorTitlebar {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ min-height: 38px;
+ padding: 0 14px;
+ border-bottom: 1px solid rgba(156, 207, 255, 0.14);
+ background: #111a24;
+}
+
+.editorTitlebar span {
+ width: 9px;
+ height: 9px;
+ border-radius: 50%;
+}
+
+.editorTitlebar span:nth-child(1) {
+ background: #ff5f57;
+}
+
+.editorTitlebar span:nth-child(2) {
+ background: #febc2e;
+}
+
+.editorTitlebar span:nth-child(3) {
+ background: #28c840;
+}
+
+.editorTitlebar strong {
+ margin-left: 6px;
+ color: rgba(226, 232, 240, 0.64);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.66rem;
+ font-weight: 750;
+}
+
+.editorCode {
+ margin: 0;
+ padding: 18px 16px 20px;
+ color: rgba(248, 250, 252, 0.9);
+ overflow: hidden;
+}
+
+.editorCode code {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ font-weight: 720;
+ line-height: 1.75;
+ white-space: pre-wrap;
+}
+
+.codeFunction {
+ color: #9ccfff;
+}
+
+.codeVariable {
+ color: #f2c28d;
+}
+
+.codeMuted {
+ color: rgba(226, 232, 240, 0.74);
+}
+
+.editorCode .codeKeyword {
+ color: #ff7b72;
+}
+
+.editorCode .codeString {
+ color: #f8d4af;
+}
+
+.commandsEditorPreview {
+ border: 0;
+ border-radius: 14px;
+ background: #07111f;
+ box-shadow: none;
+}
+
+.commandsEditorPreview .editorWindow {
+ position: absolute;
+ top: 0;
+ right: auto;
+ bottom: auto;
+ left: 0;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ border: 1px solid rgba(156, 207, 255, 0.16);
+ border-radius: 14px;
+ box-sizing: border-box;
+ align-self: stretch;
+ justify-self: stretch;
+}
+
+.commandsEditorPreview .editorTitlebar {
+ min-height: 46px;
+}
+
+.commandsEditorPreview .editorCode {
+ display: flex;
+ flex: 1;
+ align-items: center;
+ padding: clamp(22px, 4vw, 36px);
+}
+
+.commandsEditorPreview .editorCode code {
+ font-size: clamp(0.68rem, 1.1vw, 0.88rem);
+ line-height: 1.9;
+}
+
+.actionCardCopy {
+ display: grid;
+ gap: 8px;
+}
+
+.actionCardCopy h4 {
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.5rem;
+ font-weight: 650;
+ line-height: 1.15;
+ letter-spacing: 0;
+}
+
+.actionCardCopy p {
+ margin: 0;
+ color: color-mix(in srgb, var(--fg-muted) 92%, white 8%);
+ font-size: 0.98rem;
+ line-height: 1.65;
+}
+
+.commandsPreview {
+ align-content: center;
+ display: flex;
+ inset: 0;
+}
+
+.commandsCard {
+ display: grid;
+ align-content: center;
+ flex: 1;
+ gap: 10px;
+ width: 100%;
+ height: 100%;
+ padding: 24px;
+ border: 0;
+ border-radius: 0;
+ background: #07111f;
+ box-shadow: none;
+}
+
+.commandPrompt {
+ display: grid;
+ grid-template-columns: 30px minmax(0, 1fr);
+ align-items: center;
+ min-height: 52px;
+ padding: 0 14px;
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ border-radius: 12px;
+ background: rgba(255, 255, 255, 0.055);
+}
+
+.commandPrompt span {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+ border-radius: 8px;
+ background: rgba(248, 250, 252, 0.1);
+ color: rgba(248, 250, 252, 0.8);
+ font-family: var(--font-geist-mono), monospace;
+ font-weight: 900;
+}
+
+.commandMenu {
+ display: grid;
+ gap: 9px;
+}
+
+.commandMenu > div {
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ background: rgba(255, 255, 255, 0.045);
+}
+
+.durableTimelinePreview {
+ position: relative;
+ display: grid;
+ align-content: end;
+ height: 100%;
+ gap: 6px;
+ overflow: hidden;
+ padding: 0 18px 14px 42px;
+ mask-image: none;
+ -webkit-mask-image: none;
+}
+
+.durableTimelineLine {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 25px;
+ width: 1px;
+ background: linear-gradient(
+ 180deg,
+ rgba(126, 184, 218, 0.28),
+ rgba(126, 184, 218, 0.36) 10%,
+ rgba(126, 184, 218, 0.2) 55%,
+ rgba(126, 184, 218, 0.24) 82%,
+ rgba(126, 184, 218, 0.18)
+ );
+}
+
+.durableEvent,
+.durableAgentCard,
+.durableDelivered {
+ position: relative;
+ display: grid;
+ align-items: center;
+ height: 36px;
+ min-height: 36px;
+ min-width: 0;
+ border: 1px solid rgba(156, 207, 255, 0.13);
+ border-radius: 8px;
+ background: rgba(15, 27, 45, 0.72);
+ box-shadow: 0 10px 24px rgba(2, 7, 18, 0.22);
+ backdrop-filter: blur(10px);
+}
+
+.durableTimelineRow {
+ animation: durableTimelineRowIn 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+.durableEvent {
+ grid-template-columns: minmax(0, 1fr) auto;
+ padding: 0 10px 0 12px;
+}
+
+.durableEventDot {
+ position: absolute;
+ top: 50%;
+ left: -21px;
+ z-index: 1;
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: #8b95a5;
+ transform: translateY(-50%);
+}
+
+.durableEvent code,
+.durableEvent time,
+.durableDelivered strong,
+.durableDelivered time {
+ overflow: hidden;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.66rem;
+ font-weight: 700;
+ line-height: 1;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.durableEvent code {
+ color: rgba(245, 248, 255, 0.92);
+}
+
+.durableEvent time {
+ color: rgba(190, 206, 224, 0.56);
+}
+
+.durableAgentCard {
+ grid-template-columns: 15px minmax(0, 1fr) auto;
+ gap: 8px;
+ padding: 0 10px;
+}
+
+.durableAgentIcon {
+ width: 14px;
+ height: 14px;
+ color: rgba(245, 248, 255, 0.68);
+}
+
+.durableAgentCard strong {
+ overflow: hidden;
+ color: rgba(245, 248, 255, 0.96);
+ font-size: 0.72rem;
+ font-weight: 700;
+ line-height: 1;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.durableStatus {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ color: rgba(190, 206, 224, 0.7);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.58rem;
+ font-weight: 700;
+ line-height: 1;
+}
+
+.durableStatus span {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #8b95a5;
+}
+
+.durableAgentOffline {
+ border-color: rgba(139, 149, 165, 0.22);
+ background: rgba(19, 30, 49, 0.76);
+}
+
+.durableAgentOnline {
+ border-color: rgba(156, 207, 255, 0.13);
+ background: rgba(15, 27, 45, 0.72);
+}
+
+.durableAgentOnline .durableStatus {
+ color: rgba(190, 206, 224, 0.7);
+}
+
+.durableAgentOnline .durableStatus span {
+ background: #28c840;
+}
+
+.durableDelivered {
+ grid-template-columns: minmax(0, 1fr) auto;
+ padding: 0 10px 0 12px;
+ border-color: rgba(156, 207, 255, 0.13);
+ background: rgba(15, 27, 45, 0.72);
+ animation: durableDeliveredIn 560ms cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+.durableDelivered span {
+ position: absolute;
+ top: 50%;
+ left: -25px;
+ display: grid;
+ place-items: center;
+ width: 16px;
+ height: 16px;
+ color: #28c840;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.9rem;
+ font-weight: 800;
+ line-height: 1;
+ transform: translateY(-50%);
+}
+
+.durableDelivered strong {
+ color: rgba(245, 248, 255, 0.92);
+}
+
+.durableDelivered time {
+ color: rgba(190, 206, 224, 0.56);
+}
+
+.durableDeliveredOne {
+ animation-delay: 120ms;
+}
+
+.durableDeliveredTwo {
+ animation-delay: 250ms;
+}
+
+.durableDeliveredThree {
+ animation-delay: 380ms;
+}
+
+@keyframes durableDeliveredIn {
+ 0% {
+ opacity: 0;
+ transform: translateY(-4px) scale(0.98);
+ }
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+@keyframes durableTimelineRowIn {
+ 0% {
+ opacity: 0;
+ transform: translateY(8px) scale(0.98);
+ }
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .durableTimelineRow,
+ .durableDelivered {
+ animation: none;
+ }
+}
+
+.deliveryTableHead {
+ display: grid;
+ grid-template-columns: 27px minmax(68px, 1fr) repeat(4, minmax(34px, 0.48fr));
+ align-items: center;
+ column-gap: 8px;
+ padding: 0 0 7px;
+ border-bottom: 1px solid var(--line);
+ color: var(--fg-faint);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.56rem;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+}
+
+.deliveryTableHead span {
+ overflow: hidden;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.deliveryTableHead span:nth-child(2) {
+ text-align: left;
+}
+
+.deliveryTableBody {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ justify-content: flex-end;
+ min-height: 0;
+ overflow: hidden;
+}
+
+.dashRow {
+ display: grid;
+ flex-shrink: 0;
+ grid-template-columns: 7px 12px minmax(68px, 1fr) repeat(4, minmax(34px, 0.48fr));
+ align-items: center;
+ column-gap: 8px;
+ min-height: 45px;
+ padding: 8px 0;
+ border-bottom: 1px solid var(--line);
+}
+
+.dashRow:last-child {
+ border-bottom: 0;
+}
+
+.dashDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+.dashIcon {
+ width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+ color: var(--fg-muted);
+}
+
+.dashAgent {
+ font-size: 0.78rem;
+ font-weight: 600;
+ color: var(--fg);
+}
+
+.dashAgentGroup {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ min-width: 0;
+ flex: 1;
+}
+
+.dashAgentGroup .dashAgent {
+ overflow: hidden;
+ flex: 0 1 auto;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.deliveryStats {
+ display: contents;
+}
+
+.deliveryStats span {
+ overflow: hidden;
+ color: var(--fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.66rem;
+ font-weight: 700;
+ line-height: 1;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.deliveryStats strong {
+ font: inherit;
+}
+
+.deliveryTableHead,
+.deliveryTableBody {
+ display: none;
+}
+
+/* Preview: Real-time events */
+.realtimePreview {
+ position: absolute;
+ inset: 0;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ padding: 18px;
+ border: 1px solid color-mix(in srgb, white 22%, transparent);
+ border-radius: 10px;
+ background: color-mix(in srgb, var(--surface) 92%, white 8%);
+ box-shadow: none;
+ overflow: hidden;
+}
+
+.realtimeFeed {
+ position: relative;
+ height: 100%;
+ overflow: hidden;
+ display: grid;
+ mask-image: linear-gradient(180deg, transparent 0%, black 12%, black 100%);
+ -webkit-mask-image: linear-gradient(180deg, transparent 0%, black 12%, black 100%);
+}
+
+.realtimeFeedList {
+ display: grid;
+ gap: 8px;
+ min-height: 100%;
+ align-content: end;
+}
+
+.realtimeEvent {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 9px;
+ height: 54px;
+ padding: 9px 10px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--card-bg);
+ box-sizing: border-box;
+ transform-origin: bottom center;
+ animation: realtimeEventPop 360ms cubic-bezier(0.16, 1, 0.3, 1);
+}
+
+.realtimeEventDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 999px;
+ background: #28c840;
+ box-shadow: 0 0 8px rgba(40, 200, 64, 0.45);
+}
+
+.realtimeEventDot-live {
+ background: #28c840;
+ box-shadow: 0 0 6px rgba(40, 200, 64, 0.28);
+}
+
+.realtimeEventDot-idle {
+ background: #9ca3af;
+ box-shadow: 0 0 5px rgba(156, 163, 175, 0.18);
+}
+
+.realtimeEventDot-disconnected {
+ background: #ff5f57;
+ box-shadow: 0 0 6px rgba(255, 95, 87, 0.26);
+}
+
+.realtimeEvent div {
+ min-width: 0;
+ display: grid;
+ gap: 2px;
+}
+
+.realtimeEvent code {
+ overflow: hidden;
+ color: var(--fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.68rem;
+ font-weight: 700;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.realtimeEvent span {
+ overflow: hidden;
+ color: var(--fg-muted);
+ font-size: 0.68rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.realtimeEventMeta {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ min-width: 0;
+}
+
+.realtimeHarnessLogo {
+ width: 11px;
+ height: 11px;
+ color: var(--fg-faint);
+ flex-shrink: 0;
+}
+
+.realtimeEventAgent {
+ color: var(--fg);
+ font-weight: 700;
+}
+
+.realtimeEvent strong {
+ color: var(--fg-faint);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.66rem;
+ font-weight: 700;
+}
+
+@keyframes realtimeEventPop {
+ 0% {
+ opacity: 0;
+ transform: translateY(12px) scale(0.97);
+ }
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .realtimeEvent {
+ animation: none;
+ }
+}
+
+/* ---- SELF-MANAGING AGENTS SECTION ---- */
+
+.selfManageWrapper {
+ background: var(--section-bg);
+ border-top: 1px solid var(--line);
+ transition: background 0.3s ease;
+}
+
+.selfManageSection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px 96px;
+}
+
+.selfManageHeader {
+ max-width: 760px;
+ margin: 0 auto 48px;
+ text-align: center;
+}
+
+.selfManageTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.5vw, 2.6rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0 0 14px;
+}
+
+.selfManageSubtitle {
+ font-size: 1.05rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0 auto;
+}
+
+.selfManageGrid {
+ display: grid;
+ grid-template-columns: minmax(240px, 0.7fr) minmax(0, 1.55fr);
+ align-items: stretch;
+ gap: 28px;
+}
+
+.selfManageCard {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ min-height: 360px;
+ padding: 34px;
+ border: 1px solid var(--line);
+ border-radius: 14px;
+ background: var(--card-bg);
+}
+
+.selfManageCardTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.3rem, 2vw, 1.8rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ margin: 0 0 12px;
+}
+
+.selfManageCardText {
+ color: var(--fg-muted);
+ font-size: 0.98rem;
+ line-height: 1.65;
+ margin: 0;
+}
+
+.selfManageCodeCard {
+ position: relative;
+ min-height: 430px;
+ border: 1px solid var(--line);
+ border-radius: 14px;
+ background: var(--surface);
+ overflow: hidden;
+}
+
+.selfManageTerminal {
+ position: absolute;
+ inset: 18px 0 0 18px;
+ height: auto;
+ margin: 0;
+ border-radius: 10px 0 0 0;
+ box-shadow: -6px -6px 28px color-mix(in srgb, black 22%, transparent);
+}
+
+.selfManagePreviewCode {
+ padding: 18px 20px;
+ font-size: 0.9rem;
+ line-height: 1.65;
+ overflow-wrap: anywhere;
+ white-space: pre-wrap;
+}
+
+@media (max-width: 960px) {
+ .featuresSection {
+ grid-template-columns: 1fr;
+ padding: 0 24px 72px;
+ gap: 0;
+ }
+
+ .featureCol {
+ grid-template-columns: 1fr;
+ gap: 24px;
+ min-height: auto;
+ padding: 56px 0;
+ }
+
+ .featureCol .featurePreview {
+ order: 1;
+ }
+
+ .featureCol .featureCopy {
+ order: 2;
+ }
+
+ .messagingFeature .featureCopy {
+ order: 1;
+ }
+
+ .messagingFeature .featurePreview {
+ order: 2;
+ }
+
+ .deliveryFeature .featureCopy {
+ order: 2;
+ }
+
+ .deliveryFeature .featurePreview {
+ order: 1;
+ }
+
+ .commandsFeature .featureCopy {
+ order: 1;
+ }
+
+ .commandsFeature .featurePreview {
+ order: 2;
+ }
+
+ .featureSeparator {
+ height: 56px;
+ margin: -34px calc(50% - 50vw) -50px;
+ }
+
+ .featureSeparatorWaves {
+ width: 130vw;
+ height: 78px;
+ }
+
+ .featureCopy {
+ max-width: none;
+ }
+
+ .featurePreview {
+ height: 260px;
+ }
+
+ .capabilityBand {
+ grid-template-columns: 1fr;
+ gap: 32px;
+ padding: 56px 0;
+ }
+
+ .capabilityPreview {
+ height: 260px;
+ }
+
+ .capabilityCopy h3 {
+ font-size: 1.35rem;
+ }
+
+ .capabilityCopy p {
+ font-size: 0.95rem;
+ }
+
+ .actionsGrid {
+ grid-template-columns: 1fr;
+ gap: 32px;
+ }
+
+ .actionCardVisual {
+ min-height: 300px;
+ }
+
+ .toolSurface {
+ padding: 20px;
+ }
+
+ .editorWindow {
+ margin: 18px;
+ }
+
+ .editorCode {
+ padding: 16px 14px 18px;
+ }
+
+ .editorCode code {
+ font-size: 0.66rem;
+ line-height: 1.7;
+ }
+
+ .actionCardCopy h4 {
+ font-size: 1.35rem;
+ }
+
+ .actionCardCopy p {
+ font-size: 0.95rem;
+ }
+
+ .featureTitle {
+ font-size: 2.25rem;
+ }
+
+ .featureDesc {
+ font-size: 0.98rem;
+ }
+
+ .featureList {
+ font-size: 0.98rem;
+ gap: 10px;
+ }
+
+ .realtimePreview {
+ inset: 0;
+ gap: 12px;
+ }
+
+ .previewChat {
+ top: 12px;
+ left: 20px;
+ padding: 10px;
+ gap: 6px;
+ }
+
+ .chatChannelHeader {
+ min-height: 30px;
+ padding: 0 8px;
+ }
+
+ .chatChannelHeader strong {
+ font-size: 0.68rem;
+ }
+
+ .chatChannelMeta {
+ font-size: 0.56rem;
+ }
+
+ .chatStream {
+ gap: 6px;
+ }
+
+ .chatMsg {
+ padding: 7px 9px;
+ }
+
+ .chatText {
+ font-size: 0.62rem;
+ line-height: 1.35;
+ }
+
+ .chatReply {
+ margin-left: 16px;
+ }
+
+ .chatReply::before {
+ left: -10px;
+ }
+
+ .previewDashboard {
+ inset: 0 24px 12px 0;
+ }
+
+ .searchPreview,
+ .commandsPreview {
+ inset: 16px;
+ gap: 9px;
+ }
+
+ .webhookPreview {
+ padding: 16px 0 16px 16px;
+ }
+
+ .commandsPreview {
+ inset: 0;
+ }
+
+ .commandsCard {
+ padding: 20px;
+ }
+
+ .webhookCodeTitle {
+ padding: 10px 14px;
+ }
+
+ .webhookCodeSnippet {
+ min-height: 168px;
+ padding: 15px 14px 16px;
+ }
+
+ .webhookCodeSnippet code {
+ font-size: 0.66rem;
+ line-height: 1.65;
+ }
+
+ .searchPreview {
+ padding-top: 0;
+ }
+
+ .searchResults,
+ .commandMenu {
+ gap: 7px;
+ }
+
+ .searchResults > div,
+ .commandMenu > div {
+ min-height: 43px;
+ padding: 8px 10px;
+ }
+
+ .commandPrompt {
+ min-height: 46px;
+ }
+
+ .selfManageSection {
+ padding: 72px 24px 80px;
+ }
+
+ .selfManageGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .selfManageCard {
+ min-height: auto;
+ }
+
+ .selfManageCodeCard {
+ min-height: 380px;
+ }
+}
+
+/* ---- SDK SECTION ---- */
+
+.sdkWrapper {
+ background: var(--section-bg);
+ transition: background 0.3s ease;
+}
+
+.sdkSection {
+ display: grid;
+ grid-template-columns: 1.6fr 1fr;
+ align-items: center;
+ gap: 60px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 72px 40px 80px;
+}
+
+.sdkText {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.sdkTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.5vw, 2.6rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0;
+}
+
+.sdkSubtitle {
+ font-size: 1.05rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 38ch;
+ margin: 0;
+}
+
+.sdkLink {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 0.92rem;
+ font-weight: 600;
+ color: var(--primary);
+ text-decoration: none;
+ transition: gap 0.2s ease;
+}
+
+.sdkLink:hover {
+ gap: 10px;
+}
+
+.sdkCode {
+ min-width: 0;
+}
+
+/* ---- BYOH SECTION ---- */
+
+.byohWrapper {
+ background: var(--showcase-bg);
+ position: relative;
+ overflow: hidden;
+ transition: background 0.3s ease;
+}
+
+.byohWrapper::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 40% 50% at 20% 30%, rgba(255, 255, 255, 0.4) 0%, transparent 70%),
+ radial-gradient(ellipse 35% 40% at 75% 65%, rgba(255, 255, 255, 0.25) 0%, transparent 70%),
+ radial-gradient(ellipse 50% 30% at 50% 10%, rgba(255, 255, 255, 0.3) 0%, transparent 60%),
+ radial-gradient(ellipse 30% 45% at 85% 20%, rgba(255, 255, 255, 0.2) 0%, transparent 65%),
+ radial-gradient(ellipse 45% 35% at 10% 80%, rgba(255, 255, 255, 0.2) 0%, transparent 60%);
+ pointer-events: none;
+}
+
+.byohWrapper::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 400'%3E%3Cpath d='M-20,60 Q200,0 500,90 T1000,50 T1220,70' fill='none' stroke='rgba(255,255,255,0.35)' stroke-width='4'/%3E%3Cpath d='M-20,130 Q250,50 550,140 T1050,100 T1220,130' fill='none' stroke='rgba(255,255,255,0.25)' stroke-width='3.5'/%3E%3Cpath d='M-20,200 Q180,130 480,210 T980,170 T1220,200' fill='none' stroke='rgba(255,255,255,0.2)' stroke-width='3'/%3E%3Cpath d='M-20,270 Q220,200 520,280 T1020,240 T1220,270' fill='none' stroke='rgba(255,255,255,0.15)' stroke-width='2.5'/%3E%3Cpath d='M-20,340 Q280,280 580,350 T1080,310 T1220,340' fill='none' stroke='rgba(255,255,255,0.1)' stroke-width='2'/%3E%3C/svg%3E");
+ background-size: cover;
+ background-position: center;
+ pointer-events: none;
+}
+
+.byohWrapper > * {
+ position: relative;
+ z-index: 1;
+}
+
+.featureByoh {
+ order: 2;
+ justify-self: stretch;
+ width: auto;
+ margin-inline: calc(50% - 50vw);
+ background: var(--agent-tools-band-bg);
+ border-top: 1px solid color-mix(in srgb, white 5%, transparent);
+ border-bottom: 1px solid color-mix(in srgb, white 5%, transparent);
+}
+
+.featureByoh::before,
+.featureByoh::after {
+ background: none;
+ opacity: 0;
+}
+
+.byohSection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 80px 40px 100px;
+ text-align: center;
+}
+
+.byohText {
+ margin-bottom: 48px;
+}
+
+.byohTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 3rem;
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0 0 16px;
+}
+
+.byohSubtitle {
+ font-size: 1.05rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 52ch;
+ margin: 0 auto;
+}
+
+.byohLogos {
+ display: flex;
+ justify-content: center;
+ gap: 24px;
+ flex-wrap: wrap;
+}
+
+.logoCard {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ padding: 24px 28px;
+ border-radius: 12px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.2s;
+ min-width: 100px;
+}
+
+.logoCard:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 4px 20px color-mix(in srgb, var(--primary) 8%, transparent);
+ transform: translateY(-2px);
+}
+
+.byohLogo {
+ width: 36px;
+ height: 36px;
+ color: var(--fg-muted);
+}
+
+.logoLabel {
+ font-size: 0.78rem;
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ color: var(--fg-muted);
+}
+
+.byohFootnote {
+ width: 100%;
+ text-align: center;
+ font-size: 0.82rem;
+ color: var(--fg-faint);
+ margin: 16px 0 0;
+}
+
+:global(html[data-theme='dark']) .byohWrapper {
+ background:
+ radial-gradient(
+ circle at 50% -10%,
+ color-mix(in srgb, var(--primary) 12%, transparent) 0%,
+ transparent 42%
+ ),
+ linear-gradient(180deg, #09131d 0%, #0d1824 52%, #09131d 100%);
+ border-top: 1px solid color-mix(in srgb, var(--primary) 14%, transparent);
+ border-bottom: 1px solid color-mix(in srgb, var(--primary) 10%, transparent);
+}
+
+:global(html[data-theme='dark']) .byohWrapper::before {
+ background:
+ radial-gradient(
+ circle at 18% 28%,
+ color-mix(in srgb, var(--primary) 20%, transparent) 0%,
+ transparent 24%
+ ),
+ radial-gradient(
+ circle at 82% 18%,
+ color-mix(in srgb, var(--secondary-400) 18%, transparent) 0%,
+ transparent 20%
+ ),
+ radial-gradient(
+ circle at 52% 100%,
+ color-mix(in srgb, var(--primary) 14%, transparent) 0%,
+ transparent 32%
+ );
+ opacity: 0.9;
+}
+
+:global(html[data-theme='dark']) .byohWrapper::after {
+ background-image:
+ linear-gradient(to right, color-mix(in srgb, var(--primary) 9%, transparent) 1px, transparent 1px),
+ linear-gradient(to bottom, color-mix(in srgb, var(--primary) 7%, transparent) 1px, transparent 1px);
+ background-size: 144px 144px;
+ background-position: center;
+ opacity: 0.28;
+ mask-image: linear-gradient(180deg, transparent 0%, black 14%, black 86%, transparent 100%);
+ -webkit-mask-image: linear-gradient(180deg, transparent 0%, black 14%, black 86%, transparent 100%);
+}
+
+:global(html[data-theme='dark']) .featureByoh {
+ background: var(--agent-tools-band-bg);
+ border-top: 1px solid color-mix(in srgb, white 5%, transparent);
+ border-bottom: 1px solid color-mix(in srgb, white 5%, transparent);
+}
+
+:global(html[data-theme='dark']) .featureByoh::before,
+:global(html[data-theme='dark']) .featureByoh::after {
+ background: none;
+ opacity: 0;
+}
+
+:global(html[data-theme='dark']) .byohText {
+ margin-bottom: 40px;
+}
+
+:global(html[data-theme='dark']) .byohTitle {
+ opacity: 1;
+}
+
+:global(html[data-theme='dark']) .byohSubtitle {
+ color: color-mix(in srgb, var(--fg-muted) 92%, white 8%);
+}
+
+:global(html[data-theme='dark']) .byohLogos {
+ gap: 18px;
+}
+
+:global(html[data-theme='dark']) .logoCard {
+ min-width: 116px;
+ padding: 22px 24px;
+ background: color-mix(in srgb, var(--surface-strong) 82%, transparent);
+ border: 1px solid color-mix(in srgb, var(--primary) 14%, transparent);
+ box-shadow:
+ 0 18px 48px rgba(0, 0, 0, 0.22),
+ inset 0 1px 0 rgba(255, 255, 255, 0.03);
+ backdrop-filter: blur(18px);
+}
+
+:global(html[data-theme='dark']) .logoCard:hover {
+ border-color: color-mix(in srgb, var(--primary) 30%, transparent);
+ box-shadow:
+ 0 22px 56px rgba(0, 0, 0, 0.28),
+ 0 0 0 1px color-mix(in srgb, var(--primary) 18%, transparent);
+ transform: translateY(-4px);
+}
+
+:global(html[data-theme='dark']) .byohLogo {
+ color: color-mix(in srgb, var(--fg) 90%, var(--primary) 10%);
+}
+
+:global(html[data-theme='dark']) .logoLabel {
+ color: var(--fg);
+}
+
+:global(html[data-theme='dark']) .byohFootnote {
+ color: var(--fg-muted);
+}
+
+/* ---- LOCAL / CLOUD SECTION ---- */
+
+.deployWrapper {
+ background: var(--agent-tools-band-bg);
+ border-top: 1px solid color-mix(in srgb, white 5%, transparent);
+ border-bottom: 1px solid color-mix(in srgb, white 5%, transparent);
+ transition: background 0.3s ease;
+}
+
+.deploySection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 80px 40px 34px;
+ text-align: center;
+}
+
+.deployTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.5vw, 2.6rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0 0 16px;
+}
+
+.deploySubtitle {
+ font-size: 1.05rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 50ch;
+ margin: 0 auto 48px;
+}
+
+.deployCards {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 24px;
+ max-width: 720px;
+ margin: 0 auto;
+}
+
+.deployCard {
+ display: block;
+ padding: 32px 28px;
+ border-radius: 14px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: inherit;
+ text-align: left;
+ text-decoration: none;
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.2s;
+}
+
+.deployCard:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 4px 20px color-mix(in srgb, var(--primary) 8%, transparent);
+ transform: translateY(-2px);
+}
+
+.deployCard:focus-visible {
+ outline: 2px solid color-mix(in srgb, var(--primary) 55%, white 20%);
+ outline-offset: 4px;
+}
+
+.deployIcon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 44px;
+ height: 44px;
+ border-radius: 10px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ color: var(--primary);
+ margin-bottom: 16px;
+}
+
+.deployCardTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.2rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--fg);
+ margin: 0 0 8px;
+}
+
+.deployCardText {
+ font-size: 0.9rem;
+ line-height: 1.6;
+ color: var(--fg-muted);
+ margin: 0 0 16px;
+}
+
+.deployCardCode {
+ display: inline-block;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ padding: 6px 12px;
+ border-radius: 6px;
+ background: var(--code-bg);
+ color: var(--code-fg);
+}
+
+/* ---- RESPONSIVE ---- */
+
+@media (max-width: 960px) {
+ .hero {
+ grid-template-columns: 1fr;
+ padding: 64px 32px 52px;
+ gap: 24px;
+ margin-top: -80px;
+ min-height: auto;
+ }
+
+ .heroLeft {
+ gap: 22px;
+ padding-top: 0;
+ }
+
+ .heroRight {
+ min-height: 320px;
+ }
+
+ .headline {
+ font-size: clamp(2.85rem, 10.5vw, 4.35rem);
+ }
+
+ .sdkSection {
+ grid-template-columns: 1fr;
+ padding: 60px 24px 80px;
+ gap: 36px;
+ }
+
+ .byohSection {
+ padding: 60px 24px 80px;
+ }
+
+ .deploySection {
+ padding: 60px 24px 36px;
+ }
+
+ .deployCards {
+ grid-template-columns: 1fr;
+ max-width: 400px;
+ }
+
+ .byohLogos {
+ gap: 16px;
+ }
+
+ .logoCard {
+ padding: 18px 20px;
+ min-width: 80px;
+ }
+
+ .installSection {
+ padding: 0 0 68px;
+ }
+
+ .installInner {
+ padding: 36px 32px 0;
+ }
+
+ .installInner::before {
+ left: 32px;
+ right: 32px;
+ }
+
+ .installInner::after {
+ width: min(460px, calc(100% - 64px));
+ }
+
+ .waitlistSection {
+ padding: 78px 24px 86px;
+ }
+
+ .waitlistInner {
+ gap: 30px;
+ max-width: 620px;
+ }
+
+ .installHeader {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 18px;
+ }
+
+ .installAgentLogos {
+ flex-wrap: wrap;
+ justify-content: flex-start;
+ max-width: none;
+ padding-right: 0;
+ gap: 8px;
+ }
+
+ .installAgentLogo,
+ .installAgentLogos:hover .installAgentLogo {
+ margin-right: 0;
+ }
+
+ .installActions {
+ grid-template-columns: 1fr;
+ }
+
+ .skillCallout {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .installCommand,
+ .agentPromptCopy {
+ width: 100%;
+ max-width: none;
+ }
+}
+
+@media (max-width: 600px) {
+ .hero {
+ padding: 76px 28px 56px;
+ gap: 28px;
+ }
+
+ .heroLeft {
+ gap: 24px;
+ }
+
+ .headline {
+ font-size: clamp(3.05rem, 11.5vw, 4.5rem);
+ line-height: 1.08;
+ }
+
+ .ctas {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .ctaPrimary,
+ .ctaSecondary {
+ justify-content: center;
+ padding: 12px 20px;
+ font-size: 0.92rem;
+ }
+
+ .subtitle {
+ font-size: 1.15rem;
+ line-height: 1.62;
+ }
+
+ .heroRight {
+ display: none;
+ }
+
+ .installSection {
+ padding: 0 0 64px;
+ }
+
+ .installInner {
+ padding: 34px 28px 0;
+ }
+
+ .installInner::before {
+ left: 28px;
+ right: 28px;
+ }
+
+ .installInner::after {
+ width: min(360px, calc(100% - 56px));
+ }
+
+ .waitlistSection {
+ padding: 68px 28px 76px;
+ }
+
+ .installTitleRow {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .installAgentLogos {
+ width: 100%;
+ }
+
+ .installAgentLogo {
+ width: 38px;
+ height: 38px;
+ }
+
+ .installAgentLogoIcon {
+ width: 1.7rem;
+ height: 1.7rem;
+ }
+
+ /* Keep the prompt box as a single row so Copy stays inline, matching the
+ install command above it. The hover-reveal "Show prompt" toggle isn't
+ reachable on touch, so it stays out of the flow on small screens. */
+ .agentPromptActions {
+ flex-shrink: 0;
+ }
+
+ .agentPromptText {
+ padding-right: 8px;
+ }
+
+ .skillCallout {
+ padding: 14px;
+ }
+
+ .selfManageCard {
+ padding: 28px;
+ }
+
+ .selfManageCodeCard {
+ min-height: 360px;
+ }
+
+ .selfManageTerminal {
+ inset: 16px 0 0 16px;
+ }
+
+ .selfManagePreviewCode {
+ padding: 16px;
+ font-size: 0.72rem;
+ line-height: 1.55;
+ }
+}
+
+/* ---- WORKS WITH EVERY AGENT (pyramid diagram) ---- */
+
+/* Same band fill + separation as the surrounding feature bands. */
+.howItWorks {
+ position: relative;
+ grid-column: 1 / -1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 88px 0 92px;
+ isolation: isolate;
+}
+
+.howItWorks::before {
+ content: '';
+ position: absolute;
+ inset: 0 calc(50% - 50vw);
+ background: var(--agent-tools-band-bg);
+ border-top: 1px solid color-mix(in srgb, white 5%, transparent);
+ border-bottom: 1px solid color-mix(in srgb, white 5%, transparent);
+ z-index: 0;
+}
+
+.howItWorks > * {
+ position: relative;
+ z-index: 1;
+}
+
+.howHeader {
+ max-width: 660px;
+ margin: 0 0 48px;
+ text-align: center;
+}
+
+.howTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2.1rem, 4vw, 2.85rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.12;
+ color: var(--fg);
+ margin: 0 0 16px;
+}
+
+.howSubtitle {
+ font-size: 1.05rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0 auto;
+ max-width: 56ch;
+}
+
+/* pyramid: relay (top) -> connector pipes -> two agent groups (bottom) */
+.howStage {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ max-width: 680px;
+ margin: 0 auto;
+}
+
+/* apex zone: relay box (top) over the connector; wires + dots span both so a
+ message can travel up behind the relay box and pop back out */
+.howApex {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+}
+
+/* --- relay hub at the apex (sits above the dots so they hide behind it) --- */
+.howCore {
+ position: relative;
+ z-index: 2;
+ display: flex;
+ justify-content: center;
+}
+
+.howCoreNode {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 14px;
+ height: 72px;
+ padding: 0 28px;
+ border-radius: 18px;
+ border: 1px solid color-mix(in srgb, var(--primary) 42%, transparent);
+ background:
+ radial-gradient(circle at 50% 0%, color-mix(in srgb, var(--primary) 22%, transparent), transparent 72%),
+ #0e2031;
+ box-shadow:
+ 0 0 0 6px color-mix(in srgb, var(--primary) 8%, transparent),
+ 0 16px 44px color-mix(in srgb, #04101c 52%, transparent);
+}
+
+.howCoreNode::before {
+ content: '';
+ position: absolute;
+ inset: -22px;
+ z-index: -1;
+ border-radius: 50%;
+ background: radial-gradient(circle, color-mix(in srgb, var(--primary) 16%, transparent), transparent 68%);
+ pointer-events: none;
+}
+
+.howCoreMark {
+ width: 40px;
+ height: auto;
+}
+
+.howCoreWordmark {
+ width: 138px;
+ height: auto;
+}
+
+/* --- connector spacer: fixed height so pipes + dots align at any width.
+ Combined with the 72px relay box, the apex zone is a known height. --- */
+.howLink {
+ width: 100%;
+ height: 72px;
+}
+
+.howWires {
+ position: absolute;
+ inset: 0;
+ z-index: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.howWire {
+ fill: none;
+ stroke: color-mix(in srgb, var(--primary) 38%, transparent);
+ stroke-width: 1.5;
+ stroke-linejoin: round;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+}
+
+.howMsgs {
+ position: absolute;
+ inset: 0;
+ z-index: 1;
+ pointer-events: none;
+ /* Size container so the dots can be positioned with container-query units
+ (cqw/cqh) driven through GPU-composited transforms instead of animating
+ left/top. The box is sized by `inset: 0` (not its contents), so size
+ containment is safe here. */
+ container-type: size;
+}
+
+.howMsg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 10px;
+ height: 10px;
+ margin: -5px 0 0 -5px;
+ border-radius: 50%;
+ background: var(--c);
+ box-shadow:
+ 0 0 0 1px color-mix(in srgb, var(--c) 35%, transparent),
+ 0 0 14px 2px var(--c);
+ opacity: 0;
+ /* Position is driven by the individual `translate` property and the size is
+ driven by the individual `scale` property; both composite on the GPU and
+ interpolate independently, mirroring the old left/top vs transform split. */
+ will-change: translate, scale, opacity;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .howMsg {
+ animation: none;
+ }
+}
+
+/* Three messages, each its own colour, routed up to the relay and back down. */
+.howMsg1 {
+ --c: #74b8e2; /* blue — CLI -> custom */
+ animation: howMsg_m1 4.6s linear 0s infinite;
+}
+.howMsg2 {
+ --c: #d97757; /* Claude orange — custom -> CLI */
+ animation: howMsg_m2 5s linear 1.8s infinite;
+}
+.howMsg3 {
+ --c: #a6d388; /* green — CLI -> CLI */
+ animation: howMsg_m3 4.4s linear 3.6s infinite;
+}
+
+@keyframes howMsg_m1 {
+ 0% {
+ translate: 25cqw 100cqh;
+ opacity: 0;
+ scale: 1;
+ }
+ 4% {
+ opacity: 1;
+ }
+ 9% {
+ translate: 25cqw 75cqh;
+ }
+ 22% {
+ translate: 50cqw 75cqh;
+ animation-timing-function: cubic-bezier(0.6, 0, 0.9, 0.2);
+ }
+ 37% {
+ translate: 50cqw 50cqh;
+ opacity: 1;
+ scale: 0.5;
+ }
+ 43% {
+ translate: 50cqw 40cqh;
+ opacity: 0;
+ scale: 0.25;
+ }
+ 49% {
+ translate: 50cqw 40cqh;
+ opacity: 0;
+ scale: 0.25;
+ }
+ 50% {
+ translate: 50cqw 50cqh;
+ opacity: 1;
+ scale: 1.55;
+ animation-timing-function: cubic-bezier(0.2, 0.8, 0.3, 1);
+ }
+ 56% {
+ translate: 50cqw 57cqh;
+ scale: 1;
+ }
+ 66% {
+ translate: 50cqw 75cqh;
+ }
+ 82% {
+ translate: 75cqw 75cqh;
+ }
+ 86% {
+ opacity: 1;
+ }
+ 91% {
+ translate: 75cqw 100cqh;
+ opacity: 0;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes howMsg_m2 {
+ 0% {
+ translate: 75cqw 100cqh;
+ opacity: 0;
+ scale: 1;
+ }
+ 4% {
+ opacity: 1;
+ }
+ 9% {
+ translate: 75cqw 75cqh;
+ }
+ 22% {
+ translate: 50cqw 75cqh;
+ animation-timing-function: cubic-bezier(0.6, 0, 0.9, 0.2);
+ }
+ 37% {
+ translate: 50cqw 50cqh;
+ opacity: 1;
+ scale: 0.5;
+ }
+ 43% {
+ translate: 50cqw 40cqh;
+ opacity: 0;
+ scale: 0.25;
+ }
+ 49% {
+ translate: 50cqw 40cqh;
+ opacity: 0;
+ scale: 0.25;
+ }
+ 50% {
+ translate: 50cqw 50cqh;
+ opacity: 1;
+ scale: 1.55;
+ animation-timing-function: cubic-bezier(0.2, 0.8, 0.3, 1);
+ }
+ 56% {
+ translate: 50cqw 57cqh;
+ scale: 1;
+ }
+ 66% {
+ translate: 50cqw 75cqh;
+ }
+ 82% {
+ translate: 25cqw 75cqh;
+ }
+ 86% {
+ opacity: 1;
+ }
+ 91% {
+ translate: 25cqw 100cqh;
+ opacity: 0;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes howMsg_m3 {
+ 0% {
+ translate: 25cqw 100cqh;
+ opacity: 0;
+ scale: 1;
+ }
+ 4% {
+ opacity: 1;
+ }
+ 9% {
+ translate: 25cqw 75cqh;
+ }
+ 22% {
+ translate: 50cqw 75cqh;
+ animation-timing-function: cubic-bezier(0.6, 0, 0.9, 0.2);
+ }
+ 37% {
+ translate: 50cqw 50cqh;
+ opacity: 1;
+ scale: 0.5;
+ }
+ 43% {
+ translate: 50cqw 40cqh;
+ opacity: 0;
+ scale: 0.25;
+ }
+ 49% {
+ translate: 50cqw 40cqh;
+ opacity: 0;
+ scale: 0.25;
+ }
+ 50% {
+ translate: 50cqw 50cqh;
+ opacity: 1;
+ scale: 1.55;
+ animation-timing-function: cubic-bezier(0.2, 0.8, 0.3, 1);
+ }
+ 56% {
+ translate: 50cqw 57cqh;
+ scale: 1;
+ }
+ 66% {
+ translate: 50cqw 75cqh;
+ }
+ 82% {
+ translate: 25cqw 75cqh;
+ }
+ 86% {
+ opacity: 1;
+ }
+ 91% {
+ translate: 25cqw 100cqh;
+ opacity: 0;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+
+/* --- agent groups at the base --- */
+.howRow {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: clamp(16px, 4vw, 40px);
+ width: 100%;
+}
+
+.howCol {
+ display: flex;
+ justify-content: center;
+}
+
+.howGroup {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ padding: 22px 18px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: color-mix(in srgb, var(--card-bg) 92%, #07111c 8%);
+ box-shadow: 0 14px 38px color-mix(in srgb, #04101c 36%, transparent);
+}
+
+.howGroupLabel {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.05rem;
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ color: var(--fg);
+ text-align: center;
+}
+
+.howGroupLogos {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+.howChip {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 44px;
+ height: 44px;
+ border-radius: 12px;
+ border: 1px solid var(--line);
+ background: color-mix(in srgb, var(--card-bg) 55%, #060f1a 45%);
+ /* Mono brand marks (OpenCode, Cursor, GitHub, Hermes) inherit this. */
+ color: #e6edf5;
+ transition:
+ transform 0.2s,
+ border-color 0.2s;
+}
+
+.howChip:hover {
+ transform: translateY(-2px);
+ border-color: var(--card-hover-border);
+}
+
+.howChipSvg {
+ width: 24px;
+ height: 24px;
+}
+
+.howGroupCaption {
+ margin: 0;
+ font-size: 0.8rem;
+ line-height: 1.45;
+ color: var(--fg-faint);
+ text-align: center;
+}
+
+@media (max-width: 600px) {
+ .howItWorks {
+ padding: 64px 0 72px;
+ }
+
+ .howHeader {
+ margin-bottom: 38px;
+ }
+
+ .howCoreWordmark {
+ width: 120px;
+ }
+
+ /* Keep the relay box and connector equal so the apex coordinate math
+ (relay bottom 50%, bus 75%) holds at this breakpoint too. */
+ .howCoreNode {
+ height: 60px;
+ }
+
+ .howLink {
+ height: 60px;
+ }
+
+ .howRow {
+ gap: 12px;
+ }
+
+ .howGroup {
+ padding: 16px 8px;
+ gap: 10px;
+ }
+
+ /* Cap the logo row at three chips wide so the five icons wrap evenly to a
+ 3-over-2 grid (the bottom two stay centred). */
+ .howGroupLogos {
+ gap: 8px;
+ max-width: 140px;
+ margin: 0 auto;
+ }
+
+ .howChip {
+ width: 41px;
+ height: 41px;
+ }
+
+ .howChipSvg {
+ width: 24px;
+ height: 24px;
+ }
+
+ .howGroupLabel {
+ font-size: 0.95rem;
+ }
+
+ .howGroupCaption {
+ font-size: 0.74rem;
+ }
+}
+
+/* Reduced motion: park the three messages on the pipes instead of animating. */
+@media (prefers-reduced-motion: reduce) {
+ .howMsg {
+ animation: none;
+ opacity: 1;
+ transform: none;
+ }
+ .howMsg1 {
+ left: 25%;
+ top: 88%;
+ }
+ .howMsg2 {
+ left: 50%;
+ top: 62%;
+ }
+ .howMsg3 {
+ left: 75%;
+ top: 88%;
+ }
+}
diff --git a/web/app/layout.tsx b/web/app/layout.tsx
new file mode 100644
index 00000000..1d8b2776
--- /dev/null
+++ b/web/app/layout.tsx
@@ -0,0 +1,91 @@
+import type { Metadata } from 'next';
+import { PostHogProvider } from '@posthog/next';
+import { Geist_Mono, Inter, Sora } from 'next/font/google';
+import type { ReactNode } from 'react';
+
+import { defaultOgImage } from '../lib/og-meta';
+import { POSTHOG_HOST, SITE_URL } from '../lib/site';
+import { WebsitePostHogPageView } from './PostHogPageView';
+import './globals.css';
+
+const inter = Inter({
+ variable: '--font-geist-sans',
+ subsets: ['latin'],
+});
+
+const sora = Sora({
+ variable: '--font-heading',
+ subsets: ['latin'],
+});
+
+const geistMono = Geist_Mono({
+ variable: '--font-geist-mono',
+ subsets: ['latin'],
+});
+
+export const metadata: Metadata = {
+ metadataBase: new URL(SITE_URL),
+ applicationName: 'Agent Relay',
+ title: {
+ default: 'Agent Relay',
+ template: '%s | Agent Relay',
+ },
+ description:
+ 'Add channels, DMs, durable delivery, event listeners, and Zod-typed actions to any agent runtime.',
+ keywords: [
+ 'Agent Relay',
+ 'multi-agent',
+ 'agent communication',
+ 'MCP',
+ 'AI SDK',
+ 'agent relay',
+ 'headless slack for agents',
+ 'agent actions',
+ 'agent delivery',
+ ],
+ robots: {
+ index: true,
+ follow: true,
+ },
+ openGraph: {
+ siteName: 'Agent Relay',
+ type: 'website',
+ locale: 'en_US',
+ images: [defaultOgImage()],
+ },
+ icons: {
+ icon: '/favicon.svg',
+ },
+ twitter: {
+ card: 'summary_large_image',
+ images: [defaultOgImage().url],
+ },
+};
+
+export default function RootLayout({ children }: { children: ReactNode }) {
+ const postHogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY;
+ const content = postHogKey ? (
+
+
+ {children}
+
+ ) : (
+ children
+ );
+
+ return (
+
+
+ {content}
+
+
+ );
+}
diff --git a/web/app/legal.module.css b/web/app/legal.module.css
new file mode 100644
index 00000000..3567e9d2
--- /dev/null
+++ b/web/app/legal.module.css
@@ -0,0 +1,137 @@
+.page {
+ min-height: 100vh;
+ background: var(--bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+}
+
+.content {
+ max-width: 760px;
+ margin: 0 auto;
+ padding: 80px 24px 64px;
+}
+
+.meta {
+ color: var(--fg-muted);
+ font-size: 0.875rem;
+ line-height: 1.6;
+ margin-bottom: 10px;
+}
+
+.title {
+ font-size: 2.25rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ margin-bottom: 12px;
+}
+
+.subtitle {
+ font-size: 1.125rem;
+ color: var(--fg-muted);
+ line-height: 1.6;
+ margin-bottom: 48px;
+}
+
+.section {
+ margin-bottom: 40px;
+}
+
+.section h2 {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin-bottom: 12px;
+ letter-spacing: -0.01em;
+}
+
+.section p {
+ line-height: 1.7;
+ margin-bottom: 12px;
+ color: var(--fg-muted);
+}
+
+.section p strong {
+ color: var(--fg);
+}
+
+.tableWrapper {
+ overflow-x: auto;
+ margin: 8px 0 20px;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+}
+
+.table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.875rem;
+}
+
+.table th {
+ text-align: left;
+ font-weight: 600;
+ padding: 10px 14px;
+ border-bottom: 1px solid var(--border);
+ background: var(--code-bg, color-mix(in srgb, var(--fg) 4%, var(--bg)));
+}
+
+.table td {
+ padding: 10px 14px;
+ border-bottom: 1px solid var(--border);
+ color: var(--fg-muted);
+ vertical-align: top;
+}
+
+.table tr:last-child td {
+ border-bottom: none;
+}
+
+.list {
+ list-style: none;
+ padding: 0;
+ margin: 8px 0 16px;
+}
+
+.list li {
+ position: relative;
+ padding-left: 20px;
+ line-height: 1.7;
+ color: var(--fg-muted);
+ margin-bottom: 4px;
+}
+
+.list li::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 11px;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--primary);
+ opacity: 0.6;
+}
+
+.link {
+ color: var(--primary);
+ text-decoration: underline;
+ text-underline-offset: 3px;
+}
+
+.link:hover {
+ opacity: 0.8;
+}
+
+@media (max-width: 640px) {
+ .content {
+ padding: 64px 18px 48px;
+ }
+
+ .title {
+ font-size: 2rem;
+ }
+
+ .subtitle {
+ margin-bottom: 36px;
+ }
+}
diff --git a/web/app/llm.txt/route.ts b/web/app/llm.txt/route.ts
new file mode 100644
index 00000000..f41a6859
--- /dev/null
+++ b/web/app/llm.txt/route.ts
@@ -0,0 +1,12 @@
+import { getLlmsText } from '../../lib/docs-markdown';
+
+export const dynamic = 'force-static';
+
+export function GET() {
+ return new Response(getLlmsText(), {
+ headers: {
+ 'Content-Type': 'text/plain; charset=utf-8',
+ 'Cache-Control': 'public, max-age=86400',
+ },
+ });
+}
diff --git a/web/app/llms-full.txt/route.ts b/web/app/llms-full.txt/route.ts
new file mode 100644
index 00000000..ae78fbd4
--- /dev/null
+++ b/web/app/llms-full.txt/route.ts
@@ -0,0 +1,14 @@
+import { getLlmsFullText } from '../../lib/docs-markdown';
+
+// ISR so OpenNext serves this from the incremental cache on Cloudflare Workers
+// (force-static would re-read the filesystem per request, unsupported on Workers).
+export const revalidate = 86400;
+
+export function GET() {
+ return new Response(getLlmsFullText(), {
+ headers: {
+ 'Content-Type': 'text/plain; charset=utf-8',
+ 'Cache-Control': 'public, max-age=86400',
+ },
+ });
+}
diff --git a/web/app/llms.txt/route.ts b/web/app/llms.txt/route.ts
new file mode 100644
index 00000000..110e43c9
--- /dev/null
+++ b/web/app/llms.txt/route.ts
@@ -0,0 +1,15 @@
+import { getLlmsText } from '../../lib/docs-markdown';
+
+// ISR rather than force-static so OpenNext serves this from the incremental
+// cache on Cloudflare Workers. A force-static route handler re-executes per
+// request and reads docs from the filesystem, which the Workers runtime stubs.
+export const revalidate = 86400;
+
+export function GET() {
+ return new Response(getLlmsText(), {
+ headers: {
+ 'Content-Type': 'text/plain; charset=utf-8',
+ 'Cache-Control': 'public, max-age=86400',
+ },
+ });
+}
diff --git a/web/app/message/RelaycastContent.tsx b/web/app/message/RelaycastContent.tsx
new file mode 100644
index 00000000..643ccf1c
--- /dev/null
+++ b/web/app/message/RelaycastContent.tsx
@@ -0,0 +1,446 @@
+'use client';
+
+import Link from 'next/link';
+import { useMemo, useState } from 'react';
+
+import { FadeIn } from '../../components/FadeIn';
+import { MessageRelayAnimation } from '../../components/MessageRelayAnimation';
+import s from './relaycast.module.css';
+
+const featureCards = [
+ {
+ title: 'Channels',
+ desc: 'Organize agent communication into named channels. Public, private, or ephemeral.',
+ },
+ {
+ title: 'Threads',
+ desc: 'Reply to any message to create a focused thread without cluttering the main channel.',
+ },
+ {
+ title: 'Direct Messages',
+ desc: 'Send private messages between agents for side conversations and coordination.',
+ },
+ { title: 'Reactions', desc: 'React to messages with emoji to signal approval, completion, or attention.' },
+ {
+ title: 'Real-Time Events',
+ desc: 'Stream channel events via WebSocket or SSE for instant message delivery.',
+ },
+ { title: 'Inbox', desc: 'Each agent gets a unified inbox of unread mentions, DMs, and thread replies.' },
+];
+
+const toolBadges = [
+ 'Claude Code',
+ 'Codex',
+ 'Gemini CLI',
+ 'OpenCode',
+ 'Copilot',
+ 'Aider',
+ 'Goose',
+ 'Custom agents',
+];
+
+const sdkTabs = {
+ typescript: {
+ label: 'TypeScript',
+ code: `import { Relaycast } from '@relaycast/sdk';
+
+const relay = new Relaycast({
+ apiKey: process.env.RELAYCAST_API_KEY,
+});
+
+const agent = await relay.agents.register({
+ name: 'Planner',
+ type: 'agent',
+});
+
+await relay.channels.create({ name: 'dev' });
+
+await relay.messages.send({
+ channel: 'dev',
+ text: 'Starting deploy sequence...',
+});
+
+const messages = await relay.messages.list({
+ channel: 'dev',
+ limit: 20,
+});`,
+ bullets: [
+ 'Typed APIs for channels, messages, threads, reactions, and search.',
+ 'WebSocket streaming for real-time message delivery.',
+ 'Works in app servers, workers, and agent harnesses.',
+ ],
+ },
+ python: {
+ label: 'Python',
+ code: `from relaycast import Relaycast
+
+client = Relaycast(api_key=os.environ["RELAYCAST_API_KEY"])
+
+agent = client.agents.register(
+ name="Planner",
+ type="agent",
+)
+
+client.channels.create(name="dev")
+
+client.messages.send(
+ channel="dev",
+ text="Starting deploy sequence...",
+)
+
+for event in client.events.stream(channel="dev"):
+ print(event["type"], event["text"])`,
+ bullets: [
+ 'Simple synchronous flows for scripts and orchestration.',
+ 'Streaming events for long-running agent workers.',
+ 'Built for task runners, notebooks, and agent backends.',
+ ],
+ },
+ curl: {
+ label: 'cURL',
+ code: `TOKEN=$(curl -s -X POST https://api.relaycast.dev/v1/agents \\
+ -H "Authorization: Bearer rk_live_..." \\
+ -H "Content-Type: application/json" \\
+ -d '{"name":"Bot","type":"agent"}' | jq -r .data.token)
+
+curl -X POST https://api.relaycast.dev/v1/channels/general/messages \\
+ -H "Authorization: Bearer $TOKEN" \\
+ -H "Content-Type: application/json" \\
+ -d '{"text":"Hello from cURL!"}'`,
+ bullets: [
+ 'Raw HTTP for quick debugging and shell-based workflows.',
+ 'Easy to plug into CI, demos, and documentation examples.',
+ 'Same primitives as the SDKs with no special transport layer.',
+ ],
+ },
+} as const;
+
+const whyCards = [
+ {
+ title: 'Zero infrastructure',
+ desc: 'No Redis to manage. No database to provision. No WebSocket servers to scale. We handle all of it.',
+ },
+ {
+ title: 'Instant setup',
+ desc: "One API call to create a workspace. One to register an agent. One to send a message. That's it.",
+ },
+ {
+ title: 'Framework-agnostic',
+ desc: 'Works with CrewAI, LangGraph, AutoGen, raw API calls — or mix them all in one workspace.',
+ },
+];
+
+const steps = [
+ {
+ step: '01',
+ title: 'Create a workspace',
+ code: `curl -X POST https://api.relaycast.dev/v1/workspaces \\
+ -H "Content-Type: application/json" \\
+ -d '{"name": "my-project"}'`,
+ },
+ {
+ step: '02',
+ title: 'Register your agents',
+ code: `curl -X POST https://api.relaycast.dev/v1/agents \\
+ -H "Authorization: Bearer rk_live_YOUR_KEY" \\
+ -H "Content-Type: application/json" \\
+ -d '{"name": "Alice", "type": "agent"}'`,
+ },
+ {
+ step: '03',
+ title: 'Start talking',
+ code: `curl -X POST https://api.relaycast.dev/v1/channels/general/messages \\
+ -H "Authorization: Bearer at_live_TOKEN" \\
+ -H "Content-Type: application/json" \\
+ -d '{"text": "Hello from Alice!"}'`,
+ },
+];
+
+type Lang = 'typescript' | 'python' | 'curl';
+
+function highlightNonString(text: string, keywords: RegExp, types: RegExp, methods: RegExp) {
+ return text
+ .replace(types, `$1 `)
+ .replace(keywords, `$& `)
+ .replace(methods, `.$1 (`);
+}
+
+function highlight(code: string, lang: Lang) {
+ const keywords =
+ lang === 'python'
+ ? /\b(from|import|await|for|in)\b/g
+ : lang === 'curl'
+ ? /\b(curl)\b/g
+ : /\b(import|from|const|await|new)\b/g;
+ const types = lang === 'python' ? /\b(Relaycast)\b/g : /\b(Relaycast)\b/g;
+ const methods = /\.(\w+)\(/g;
+
+ const escaped = code.replace(/&/g, '&').replace(//g, '>');
+
+ const parts: string[] = [];
+ let lastIndex = 0;
+ const stringMatches = [...escaped.matchAll(/(["'`])(?:(?!\1).)*\1/g)];
+
+ if (stringMatches.length === 0) {
+ return highlightNonString(escaped, keywords, types, methods);
+ }
+
+ for (const match of stringMatches) {
+ const before = escaped.slice(lastIndex, match.index);
+ parts.push(highlightNonString(before, keywords, types, methods));
+ parts.push(`${match[0]} `);
+ lastIndex = match.index! + match[0].length;
+ }
+
+ parts.push(highlightNonString(escaped.slice(lastIndex), keywords, types, methods));
+ return parts.join('');
+}
+
+export function RelaycastContent() {
+ const [activeTab, setActiveTab] = useState('typescript');
+ const activeSdk = sdkTabs[activeTab];
+ const highlightedSdk = useMemo(() => highlight(activeSdk.code, activeTab), [activeSdk.code, activeTab]);
+ const highlightedSteps = useMemo(() => steps.map((step) => highlight(step.code, 'curl')), []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ HEADLESS MESSAGING FOR AGENTS
+
+
+
+ Messaging
+
+ for agents
+
+
+
+ Channels, threads, DMs, and real-time events for multi-agent systems. Two API calls to start,
+ zero infrastructure to manage.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Everything agents need to collaborate
+
+ Channels, threads, DMs, reactions, search, files, webhooks, and real-time events — all through one
+ API.
+
+
+
+
+ {featureCards.map((feature, index) => (
+
+
+
{String(index + 1).padStart(2, '0')}
+
{feature.title}
+
{feature.desc}
+
+
+ ))}
+
+
+
+
+
+
+ Works with every AI tool
+
+ Use Relaycast from coding agents, task runners, MCP hosts, CI, or your own orchestration layer.
+ If it can make HTTP calls, it can send messages through Relaycast.
+
+
+
+ {toolBadges.map((tool) => (
+
+ {tool}
+
+ ))}
+
+
+
+
+
+
+
+ SDK
+ One API surface across every client
+
+ Send messages, create channels, and stream events from TypeScript, Python, or straight HTTP.
+
+
+
+ {(Object.keys(sdkTabs) as Array).map((tab) => (
+ setActiveTab(tab)}
+ >
+ {sdkTabs[tab].label}
+
+ ))}
+
+
+
+ {activeSdk.bullets.map((bullet) => (
+
+
+ {bullet}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
{activeSdk.label}
+
+
+
+
+
+
+
+
+
+
+
+
+ Why Relaycast
+ Purpose-built messaging for multi-agent systems.
+
+
+
+ {whyCards.map((card, index) => (
+
+
+
+ {String(index + 1).padStart(2, '0')}
+
+
{card.title}
+
{card.desc}
+
+
+ ))}
+
+
+
+
+
+
+
+ Get started in three requests
+
+ Create a workspace, register an agent, and send a message. Relaycast is designed to be useful
+ before you build any additional abstractions.
+
+
+
+
+ {steps.map((step, index) => (
+
+
+
+ {step.step}
+
{step.title}
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
Powered by Agent Relay
+
+ Relaycast extends the Agent Relay platform with real-time messaging for agents that communicate,
+ coordinate, and take action together.
+
+
+
+
+
+ );
+}
diff --git a/web/app/message/page.tsx b/web/app/message/page.tsx
new file mode 100644
index 00000000..35004bab
--- /dev/null
+++ b/web/app/message/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function MessagePage() {
+ redirect('/primitives#message');
+}
diff --git a/web/app/message/relaycast.module.css b/web/app/message/relaycast.module.css
new file mode 100644
index 00000000..aeef30d1
--- /dev/null
+++ b/web/app/message/relaycast.module.css
@@ -0,0 +1,1101 @@
+/* Relayfile page */
+.page {
+ min-height: 100vh;
+ background:
+ radial-gradient(
+ ellipse 36% 28% at 10% 8%,
+ color-mix(in srgb, var(--primary) 16%, white 22%),
+ transparent 72%
+ ),
+ radial-gradient(
+ ellipse 34% 26% at 90% 10%,
+ color-mix(in srgb, var(--primary) 14%, white 20%),
+ transparent 70%
+ ),
+ var(--landing-hero-bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+.heroSection {
+ position: relative;
+ overflow: hidden;
+ margin-top: -60px;
+ padding-top: 60px;
+}
+
+:global(html[data-theme='dark']) .page {
+ background:
+ radial-gradient(circle at 14% 0%, color-mix(in srgb, var(--primary) 18%, transparent), transparent 36%),
+ radial-gradient(circle at 78% 14%, color-mix(in srgb, var(--primary) 14%, transparent), transparent 30%),
+ radial-gradient(circle at 50% 22%, color-mix(in srgb, var(--primary) 8%, transparent), transparent 44%),
+ var(--landing-hero-bg);
+}
+
+.heroSection > * {
+ position: relative;
+ z-index: 2;
+}
+
+.heroBgSvg {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 0;
+ pointer-events: none;
+ opacity: 0.96;
+ filter: saturate(1.08) contrast(1.03);
+}
+
+:global(html[data-theme='dark']) .heroBgSvg {
+ opacity: 0.88;
+ filter: none;
+}
+
+.hero {
+ display: grid;
+ grid-template-columns: 1fr 1.2fr;
+ align-items: start;
+ gap: 32px;
+ max-width: 1280px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 28px 40px 48px;
+}
+
+.heroLeft {
+ display: flex;
+ flex-direction: column;
+ gap: 28px;
+}
+
+.heroRight {
+ position: relative;
+ min-height: 580px;
+}
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--line);
+ padding: 6px 16px;
+ border-radius: 100px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ color: var(--primary);
+ width: fit-content;
+}
+
+.badgeDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 6px color-mix(in srgb, var(--primary) 50%, transparent);
+ animation: pulse 2s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.45;
+ }
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(3.2rem, 7vw, 5.5rem);
+ font-weight: 500;
+ line-height: 1.05;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0;
+}
+
+:global(html[data-theme='dark']) .headline {
+ opacity: 1;
+}
+
+.subtitle {
+ font-size: 1.1rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 50ch;
+ font-weight: 400;
+ margin: 0;
+}
+
+.ctas {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.ctaPrimary {
+ composes: btn btn-primary from global;
+}
+
+.ctaSecondary {
+ composes: btn btn-secondary from global;
+}
+
+.animationShell {
+ position: relative;
+ height: 100%;
+ min-height: 580px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.animationAura {
+ position: absolute;
+ inset: 12% 10% 10%;
+ border-radius: 36px;
+ background:
+ radial-gradient(circle at 30% 28%, color-mix(in srgb, var(--primary) 26%, transparent), transparent 34%),
+ radial-gradient(
+ circle at 74% 26%,
+ color-mix(in srgb, var(--secondary-400) 22%, transparent),
+ transparent 30%
+ ),
+ radial-gradient(circle at 50% 74%, color-mix(in srgb, var(--primary) 14%, white 8%), transparent 38%);
+ filter: blur(22px);
+ opacity: 0.9;
+}
+
+.animationBoard {
+ position: relative;
+ z-index: 1;
+ width: min(100%, 680px);
+ min-height: 500px;
+ border-radius: 28px;
+ overflow: hidden;
+ border: 1px solid color-mix(in srgb, var(--primary) 16%, transparent);
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--surface-strong) 92%, transparent) 0%,
+ color-mix(in srgb, var(--surface) 96%, transparent) 100%
+ );
+ box-shadow:
+ 0 24px 80px rgba(0, 0, 0, 0.14),
+ inset 0 1px 0 rgba(255, 255, 255, 0.22);
+ backdrop-filter: blur(16px);
+}
+
+.boardHeader,
+.previewTitleBar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 14px 18px;
+ background: color-mix(in srgb, var(--surface) 78%, transparent);
+ border-bottom: 1px solid var(--line);
+}
+
+.previewDots {
+ display: flex;
+ gap: 6px;
+}
+
+.previewDots span {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+}
+
+.boardTitle,
+.previewTitleText {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ color: var(--fg-faint);
+}
+
+.boardCanvas {
+ position: relative;
+ min-height: 438px;
+ padding: 24px;
+ overflow: hidden;
+ background:
+ linear-gradient(to right, color-mix(in srgb, var(--line) 55%, transparent) 1px, transparent 1px),
+ linear-gradient(to bottom, color-mix(in srgb, var(--line) 45%, transparent) 1px, transparent 1px),
+ linear-gradient(180deg, transparent 0%, color-mix(in srgb, var(--primary) 3%, transparent) 100%);
+ background-size:
+ 90px 90px,
+ 90px 90px,
+ auto;
+}
+
+.folderCard,
+.fileCard {
+ position: absolute;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ padding: 14px 16px;
+ border-radius: 16px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ box-shadow: 0 16px 40px color-mix(in srgb, black 10%, transparent);
+}
+
+.folderCard {
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--secondary-50) 90%, white 10%) 0%,
+ color-mix(in srgb, var(--secondary-100) 94%, white 6%) 100%
+ );
+}
+
+:global(html[data-theme='dark']) .folderCard {
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--secondary-900) 34%, var(--surface-strong) 66%) 0%,
+ color-mix(in srgb, var(--secondary-950) 26%, var(--surface) 74%) 100%
+ );
+}
+
+.folderCardMain {
+ top: 88px;
+ left: 248px;
+ min-width: 146px;
+ animation: floatPrimary 7s ease-in-out infinite;
+}
+
+.folderCardTop {
+ top: 26px;
+ left: 84px;
+ min-width: 132px;
+ animation: floatSoft 8s ease-in-out infinite;
+}
+
+.fileCardLeft {
+ top: 194px;
+ left: 68px;
+ min-width: 138px;
+ animation: floatSoft 7.5s ease-in-out infinite;
+}
+
+.fileCardRight {
+ top: 192px;
+ right: 74px;
+ min-width: 148px;
+ animation: floatSoft 8.5s ease-in-out infinite reverse;
+}
+
+.fileCardBottom {
+ bottom: 62px;
+ left: 240px;
+ min-width: 158px;
+ animation: floatPrimary 9s ease-in-out infinite reverse;
+}
+
+.folderLabel,
+.fileName {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--fg);
+}
+
+.folderMeta,
+.fileMeta {
+ font-size: 0.78rem;
+ color: var(--fg-muted);
+}
+
+.boardLinks {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ stroke: color-mix(in srgb, var(--primary) 36%, transparent);
+ stroke-width: 2;
+ stroke-linecap: round;
+ stroke-dasharray: 8 10;
+ opacity: 0.9;
+}
+
+.pulse {
+ position: absolute;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 35%, transparent);
+ animation: ping 2.6s ease-out infinite;
+}
+
+.pulseOne {
+ top: 134px;
+ left: 312px;
+}
+
+.pulseTwo {
+ top: 222px;
+ left: 204px;
+ animation-delay: 0.75s;
+}
+
+.pulseThree {
+ top: 220px;
+ right: 206px;
+ animation-delay: 1.2s;
+}
+
+.activityRail {
+ position: absolute;
+ right: 20px;
+ bottom: 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ min-width: 180px;
+ padding: 14px;
+ border-radius: 16px;
+ border: 1px solid var(--line);
+ background: color-mix(in srgb, var(--surface-strong) 88%, transparent);
+ backdrop-filter: blur(10px);
+}
+
+.activityRow {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+}
+
+.activityType {
+ color: var(--primary);
+ font-weight: 600;
+}
+
+.activityPath {
+ color: var(--fg-muted);
+}
+
+@keyframes ping {
+ 0% {
+ transform: scale(1);
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 35%, transparent);
+ }
+ 70% {
+ transform: scale(1.06);
+ box-shadow: 0 0 0 14px color-mix(in srgb, var(--primary) 0%, transparent);
+ }
+ 100% {
+ transform: scale(1);
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 0%, transparent);
+ }
+}
+
+@keyframes floatPrimary {
+ 0%,
+ 100% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(-10px);
+ }
+}
+
+@keyframes floatSoft {
+ 0%,
+ 100% {
+ transform: translateY(0px) translateX(0px);
+ }
+ 50% {
+ transform: translateY(-8px) translateX(3px);
+ }
+}
+
+.featuresWrapper {
+ background: var(--bg);
+ position: relative;
+ z-index: 3;
+}
+
+.featuresHeader {
+ text-align: center;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 72px 40px 40px;
+}
+
+.featuresTitle,
+.byohTitle,
+.sdkTitle,
+.deployTitle,
+.openclawTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.5vw, 2.6rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0 0 14px;
+}
+
+:global(html[data-theme='dark']) .featuresTitle,
+:global(html[data-theme='dark']) .byohTitle,
+:global(html[data-theme='dark']) .sdkTitle,
+:global(html[data-theme='dark']) .deployTitle,
+:global(html[data-theme='dark']) .openclawTitle {
+ opacity: 1;
+}
+
+.featuresSubtitle,
+.byohSubtitle,
+.sdkSubtitle,
+.deploySubtitle,
+.openclawSubtitle {
+ font-size: 1.04rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.featuresSection {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 20px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 0 40px 88px;
+}
+
+.featureCol {
+ display: flex;
+}
+
+.featureTile {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ min-height: 100%;
+ padding: 22px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--surface-strong) 96%, transparent) 0%,
+ color-mix(in srgb, var(--surface) 98%, transparent) 100%
+ );
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 4%, transparent);
+}
+
+.featureChip {
+ width: fit-content;
+ padding: 6px 10px;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
+ color: var(--primary);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ font-weight: 700;
+}
+
+.featureTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.08rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0;
+}
+
+.featureDesc {
+ font-size: 0.92rem;
+ line-height: 1.62;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.byohWrapper {
+ background: var(--showcase-bg);
+ position: relative;
+ overflow: hidden;
+}
+
+.byohWrapper::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 40% 50% at 20% 30%, rgba(255, 255, 255, 0.4) 0%, transparent 70%),
+ radial-gradient(ellipse 35% 40% at 75% 65%, rgba(255, 255, 255, 0.25) 0%, transparent 70%),
+ radial-gradient(ellipse 50% 30% at 50% 10%, rgba(255, 255, 255, 0.3) 0%, transparent 60%);
+ pointer-events: none;
+}
+
+.byohWrapper::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 400'%3E%3Cpath d='M-20,70 Q220,10 520,96 T1020,62 T1220,82' fill='none' stroke='rgba(255,255,255,0.22)' stroke-width='4'/%3E%3Cpath d='M-20,150 Q250,72 560,156 T1060,124 T1220,146' fill='none' stroke='rgba(255,255,255,0.16)' stroke-width='3'/%3E%3Cpath d='M-20,246 Q200,182 500,254 T1000,226 T1220,246' fill='none' stroke='rgba(255,255,255,0.12)' stroke-width='2.5'/%3E%3C/svg%3E");
+ background-size: cover;
+ background-position: center;
+ pointer-events: none;
+}
+
+.byohSection {
+ position: relative;
+ z-index: 1;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px 96px;
+ text-align: center;
+}
+
+.byohText {
+ margin-bottom: 42px;
+}
+
+.byohSubtitle {
+ max-width: 56ch;
+ margin: 0 auto;
+}
+
+.byohLogos {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 16px;
+}
+
+.toolBadge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 120px;
+ padding: 14px 18px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg);
+ font-size: 0.88rem;
+ font-weight: 600;
+ box-shadow: 0 4px 20px color-mix(in srgb, var(--primary) 6%, transparent);
+}
+
+.sdkWrapper,
+.deployWrapper,
+.openclawWrapper {
+ background: var(--section-bg);
+}
+
+.sdkSection {
+ display: grid;
+ grid-template-columns: 1fr 1.1fr;
+ align-items: start;
+ gap: 44px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px;
+}
+
+.sdkText {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.openclawBadge {
+ display: inline-flex;
+ align-items: center;
+ width: fit-content;
+ padding: 4px 12px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ border: 1px solid color-mix(in srgb, var(--primary) 12%, transparent);
+ border-radius: 100px;
+ font-size: 0.7rem;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.tabRow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.tabButton {
+ padding: 10px 16px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg-muted);
+ font: inherit;
+ font-size: 0.88rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition:
+ transform 0.15s,
+ border-color 0.2s,
+ color 0.2s,
+ background 0.2s;
+}
+
+.tabButton:hover {
+ transform: translateY(-1px);
+ border-color: var(--card-hover-border);
+}
+
+.tabButtonActive {
+ background: color-mix(in srgb, var(--primary) 12%, transparent);
+ border-color: color-mix(in srgb, var(--primary) 28%, transparent);
+ color: var(--primary);
+}
+
+.sdkBullets {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.sdkBullet {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ font-size: 0.95rem;
+ line-height: 1.6;
+ color: var(--fg-muted);
+}
+
+.sdkBulletDot {
+ width: 8px;
+ height: 8px;
+ margin-top: 0.45rem;
+ flex-shrink: 0;
+ border-radius: 50%;
+ background: var(--primary);
+}
+
+.sdkCode {
+ min-width: 0;
+}
+
+.codePanel {
+ overflow: hidden;
+ border-radius: 18px;
+ border: 1px solid var(--terminal-line);
+ background: var(--terminal-bg);
+ box-shadow: 0 18px 48px color-mix(in srgb, black 18%, transparent);
+}
+
+.sdkCodeBlock {
+ margin: 0;
+ padding: 22px;
+ overflow-x: auto;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.84rem;
+ line-height: 1.68;
+ color: var(--terminal-fg);
+}
+
+.codeKeyword {
+ color: var(--terminal-keyword);
+}
+.codeString {
+ color: var(--terminal-string);
+}
+.codeType {
+ color: var(--terminal-type);
+}
+.codeMethod {
+ color: var(--terminal-method);
+}
+
+.deploySection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 20px 40px 92px;
+ text-align: center;
+}
+
+.deploySubtitle {
+ margin-bottom: 40px;
+}
+
+.deployCards {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 24px;
+}
+
+.deployCard {
+ padding: 30px 26px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ text-align: left;
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.2s;
+}
+
+.deployCard:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 8%, transparent);
+ transform: translateY(-2px);
+}
+
+.deployIcon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 44px;
+ height: 44px;
+ padding: 0 12px;
+ border-radius: 12px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ color: var(--primary);
+ margin-bottom: 16px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ font-weight: 700;
+}
+
+.deployCardTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.2rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--fg);
+ margin: 0 0 10px;
+}
+
+.deployCardText {
+ font-size: 0.94rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.openclawSection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px 96px;
+}
+
+.openclawText {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ max-width: 720px;
+ margin-bottom: 34px;
+}
+
+.stepsGrid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 24px;
+}
+
+.stepCard {
+ height: 100%;
+ border-radius: 18px;
+ overflow: hidden;
+ border: 1px solid var(--line);
+ background: var(--surface-strong);
+ box-shadow: 0 10px 32px color-mix(in srgb, var(--primary) 5%, transparent);
+}
+
+.stepHeader {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 18px 20px;
+ border-bottom: 1px solid var(--line);
+ background: color-mix(in srgb, var(--surface) 72%, transparent);
+}
+
+.stepNumber {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
+ color: var(--primary);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ font-weight: 700;
+}
+
+.stepTitle {
+ margin: 0;
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.06rem;
+ font-weight: 600;
+ color: var(--fg);
+}
+
+.stepCode {
+ margin: 0;
+ padding: 20px;
+ min-height: 226px;
+ overflow-x: auto;
+ background: var(--terminal-bg);
+ color: var(--terminal-fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ line-height: 1.66;
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+}
+
+.poweredWrapper {
+ background: var(--bg);
+ padding: 0 40px 96px;
+}
+
+.poweredCard {
+ max-width: 820px;
+ margin: 0 auto;
+ padding: 28px 32px;
+ text-align: center;
+ border-radius: 22px;
+ border: 1px solid var(--line);
+ background:
+ radial-gradient(circle at top, color-mix(in srgb, var(--primary) 10%, transparent), transparent 58%),
+ var(--card-bg);
+ box-shadow: 0 14px 40px color-mix(in srgb, var(--primary) 6%, transparent);
+}
+
+.poweredEyebrow {
+ display: inline-block;
+ margin-bottom: 10px;
+ font-size: 0.75rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.poweredText {
+ margin: 0;
+ font-size: 1rem;
+ line-height: 1.7;
+ color: var(--fg-muted);
+}
+
+@media (max-width: 1100px) {
+ .featuresSection {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 960px) {
+ .hero {
+ grid-template-columns: 1fr;
+ padding: 52px 32px 52px;
+ gap: 24px;
+ }
+
+ .heroRight,
+ .animationShell {
+ min-height: 420px;
+ }
+
+ .animationBoard {
+ min-height: 420px;
+ }
+
+ .boardCanvas {
+ min-height: 356px;
+ }
+
+ .folderCardMain {
+ left: 50%;
+ transform: translateX(-50%);
+ }
+
+ .folderCardTop {
+ left: 28px;
+ }
+
+ .fileCardLeft {
+ left: 24px;
+ top: 198px;
+ }
+
+ .fileCardRight {
+ right: 24px;
+ top: 198px;
+ }
+
+ .fileCardBottom {
+ left: 50%;
+ bottom: 72px;
+ transform: translateX(-50%);
+ }
+
+ .activityRail {
+ position: static;
+ margin-top: 260px;
+ }
+
+ .featuresSection {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ padding: 0 24px 72px;
+ }
+
+ .featuresHeader,
+ .byohSection,
+ .sdkSection,
+ .deploySection,
+ .openclawSection,
+ .poweredWrapper {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+
+ .sdkSection {
+ grid-template-columns: 1fr;
+ }
+
+ .deployCards,
+ .stepsGrid {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 600px) {
+ .hero {
+ padding: 64px 20px 56px;
+ }
+
+ .headline {
+ font-size: clamp(2.4rem, 11vw, 4.5rem);
+ line-height: 1.08;
+ }
+
+ .subtitle {
+ font-size: 1.04rem;
+ }
+
+ .ctas {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .ctaPrimary,
+ .ctaSecondary {
+ width: 100%;
+ justify-content: center;
+ }
+
+ .heroRight,
+ .animationShell {
+ min-height: 360px;
+ }
+
+ .animationBoard {
+ min-height: 380px;
+ }
+
+ .boardCanvas {
+ padding: 18px;
+ }
+
+ .folderCard,
+ .fileCard {
+ min-width: 0;
+ padding: 12px 14px;
+ }
+
+ .folderCardMain {
+ top: 76px;
+ left: 50%;
+ width: calc(100% - 120px);
+ }
+
+ .folderCardTop,
+ .fileCardLeft,
+ .fileCardRight,
+ .fileCardBottom {
+ position: static;
+ width: 100%;
+ margin-top: 12px;
+ transform: none;
+ animation: none;
+ }
+
+ .boardLinks,
+ .pulse {
+ display: none;
+ }
+
+ .activityRail {
+ margin-top: 16px;
+ min-width: 0;
+ }
+
+ .featuresSection {
+ grid-template-columns: 1fr;
+ }
+
+ .featuresHeader,
+ .byohSection,
+ .sdkSection,
+ .deploySection,
+ .openclawSection,
+ .poweredWrapper {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+
+ .featuresSection {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+
+ .sdkCodeBlock {
+ font-size: 0.74rem;
+ padding: 16px;
+ }
+
+ .stepCode {
+ font-size: 0.72rem;
+ padding: 16px;
+ min-height: auto;
+ }
+
+ .toolBadge {
+ min-width: 0;
+ padding: 10px 14px;
+ font-size: 0.82rem;
+ }
+
+ .deployCards {
+ grid-template-columns: 1fr;
+ }
+
+ .stepsGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .poweredCard {
+ padding: 24px 20px;
+ }
+}
diff --git a/web/app/og-default.png/route.tsx b/web/app/og-default.png/route.tsx
new file mode 100644
index 00000000..00dcf4cd
--- /dev/null
+++ b/web/app/og-default.png/route.tsx
@@ -0,0 +1,24 @@
+import { ImageResponse } from 'next/og';
+
+import { DefaultVariant, loadBrandFonts, OG_SIZE } from '../../lib/og/template';
+
+export const runtime = 'nodejs';
+// Prerender at build time — the site-wide default card is identical between
+// deploys, so render it once and serve the static PNG.
+export const dynamic = 'force-static';
+
+/**
+ * Site-wide default Open Graph card: the simple variant (logo + wordmark, the
+ * hero headline, and the site subtitle). Pages that do not generate their own
+ * card point their `openGraph.images` here (directly, or by inheriting the
+ * default set in the root layout). Served as a `.png` route handler so the URL
+ * ends in a real image extension.
+ */
+export async function GET() {
+ const { fonts, headingFamily, bodyFamily } = await loadBrandFonts();
+
+ return new ImageResponse( , {
+ ...OG_SIZE,
+ ...(fonts.length > 0 ? { fonts } : {}),
+ });
+}
diff --git a/web/app/og-home.png/route.tsx b/web/app/og-home.png/route.tsx
new file mode 100644
index 00000000..d7f06b4d
--- /dev/null
+++ b/web/app/og-home.png/route.tsx
@@ -0,0 +1,27 @@
+import { ImageResponse } from 'next/og';
+
+import { LandingVariant, loadBrandFonts, OG_SIZE } from '../../lib/og/template';
+
+export const runtime = 'nodejs';
+// Prerender at build time: the homepage card never changes between deploys, so
+// there is no reason to re-render (and re-fetch the brand fonts) per request.
+export const dynamic = 'force-static';
+
+/**
+ * The homepage's rich Open Graph card: the landing variant (logo + wordmark,
+ * hero headline, tagline, and the live-style chat panel). Served as a `.png`
+ * route handler so the URL ends in a real image extension — some scrapers key
+ * off the extension rather than the `Content-Type`. `app/page.tsx` points its
+ * `openGraph.images` here.
+ */
+export async function GET() {
+ const { fonts, headingFamily, bodyFamily } = await loadBrandFonts();
+
+ return new ImageResponse( , {
+ ...OG_SIZE,
+ // Render 👍 (and any emoji) as Twemoji graphics rather than relying on a
+ // system emoji font satori may not have.
+ emoji: 'twemoji',
+ ...(fonts.length > 0 ? { fonts } : {}),
+ });
+}
diff --git a/web/app/openclaw/landing.module.css b/web/app/openclaw/landing.module.css
new file mode 100644
index 00000000..e5d6952b
--- /dev/null
+++ b/web/app/openclaw/landing.module.css
@@ -0,0 +1,249 @@
+.page {
+ min-height: 100vh;
+ background: #0b0b0e;
+ color: #f5f5f5;
+}
+
+.header,
+.main {
+ width: min(100%, 68rem);
+ margin: 0 auto;
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+
+.header {
+ padding-top: 1.25rem;
+}
+
+.logoLink {
+ display: inline-flex;
+ align-items: center;
+ text-decoration: none;
+}
+
+.logo {
+ height: 1.5rem;
+ width: auto;
+}
+
+.main {
+ padding-top: 2.5rem;
+ padding-bottom: 5rem;
+}
+
+.hero {
+ text-align: center;
+}
+
+.markRow {
+ display: flex;
+ justify-content: center;
+ gap: 0.55rem;
+ margin-bottom: 1.25rem;
+ font-size: 1.5rem;
+ color: #7dd3fc;
+ opacity: 0.95;
+}
+
+.eyebrow {
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: #38bdf8;
+}
+
+.headline {
+ max-width: 8ch;
+ margin: 0.9rem auto 0;
+ font-size: clamp(3rem, 8vw, 4.9rem);
+ line-height: 0.96;
+ letter-spacing: -0.05em;
+}
+
+.lead {
+ max-width: 36rem;
+ margin: 1rem auto 0;
+ color: #a1a1aa;
+ font-size: 1.02rem;
+ line-height: 1.7;
+}
+
+.instructionStrip {
+ width: fit-content;
+ max-width: 100%;
+ margin: 1.7rem auto 0;
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+ padding: 0.6rem;
+ border-radius: 0.75rem;
+ background: #18181b;
+ border: 1px solid #27272a;
+}
+
+.instructionStrip code {
+ display: block;
+ padding: 0 0.65rem;
+ color: #67e8f9;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.88rem;
+ line-height: 1.4;
+ overflow-wrap: anywhere;
+}
+
+.stripButton {
+ min-height: 2.5rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 0.55rem;
+ font-size: 0.9rem;
+ font-weight: 600;
+ gap: 0.45rem;
+}
+
+.stripButton {
+ border: none;
+ background: #38bdf8;
+ color: #082f49;
+ cursor: pointer;
+}
+
+.stripButton {
+ padding: 0 0.9rem;
+ white-space: nowrap;
+}
+
+.stripButton svg {
+ width: 0.95rem;
+ height: 0.95rem;
+ flex: 0 0 auto;
+}
+
+.helper {
+ margin-top: 0.6rem;
+ color: #71717a;
+ font-size: 0.82rem;
+}
+
+.features {
+ margin-top: 4rem;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 1rem;
+}
+
+.featureCard,
+.step {
+ border: 1px solid #27272a;
+ border-radius: 0.95rem;
+ background: rgba(255, 255, 255, 0.02);
+}
+
+.featureCard {
+ padding: 1.15rem;
+}
+
+.featureIcon {
+ display: inline-flex;
+ width: 1.8rem;
+ height: 1.8rem;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.05);
+ color: #38bdf8;
+ margin-bottom: 0.8rem;
+}
+
+.featureCard h2 {
+ font-size: 1rem;
+ margin-bottom: 0.45rem;
+}
+
+.featureCard p,
+.step p {
+ color: #a1a1aa;
+ line-height: 1.6;
+}
+
+.stepsSection {
+ margin-top: 4.5rem;
+ padding-top: 2rem;
+ border-top: 1px solid #202024;
+}
+
+.sectionTitle {
+ text-align: center;
+ font-size: clamp(2rem, 4vw, 2.5rem);
+ margin-bottom: 2rem;
+ letter-spacing: -0.04em;
+}
+
+.steps {
+ max-width: 42rem;
+ margin: 0 auto;
+ display: grid;
+ gap: 1rem;
+}
+
+.step {
+ padding: 1rem;
+}
+
+.stepHeader {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 0.85rem;
+ align-items: start;
+}
+
+.stepNumber {
+ width: 1.55rem;
+ height: 1.55rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ background: #38bdf8;
+ color: #fff;
+ font-size: 0.82rem;
+ font-weight: 700;
+}
+
+.step h3 {
+ font-size: 1rem;
+ margin-bottom: 0.2rem;
+}
+
+.step code {
+ display: block;
+ margin-top: 0.85rem;
+ padding: 0.85rem 0.95rem;
+ border-radius: 0.6rem;
+ background: #111114;
+ color: #d4d4d8;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.82rem;
+ line-height: 1.55;
+ overflow-wrap: anywhere;
+}
+
+@media (max-width: 800px) {
+ .features {
+ grid-template-columns: 1fr;
+ }
+
+ .instructionStrip {
+ width: 100%;
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .instructionStrip code {
+ padding: 0;
+ text-align: center;
+ }
+}
diff --git a/web/app/openclaw/openclaw.module.css b/web/app/openclaw/openclaw.module.css
new file mode 100644
index 00000000..21fa2b38
--- /dev/null
+++ b/web/app/openclaw/openclaw.module.css
@@ -0,0 +1,255 @@
+.page {
+ min-height: 100vh;
+ background: var(--bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+.main {
+ max-width: 68rem;
+ margin: 0 auto;
+ padding: 2.5rem 1rem 5rem;
+}
+
+/* ── Hero ── */
+
+.hero {
+ text-align: center;
+ padding: 2rem 0 3rem;
+}
+
+.eyebrow {
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--primary);
+ margin: 0 0 12px;
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2.4rem, 6vw, 3.8rem);
+ font-weight: 700;
+ letter-spacing: -0.04em;
+ color: var(--primary);
+ margin: 0 0 16px;
+}
+
+.lead {
+ max-width: 36rem;
+ margin: 0 auto;
+ color: var(--fg-muted);
+ font-size: 1.02rem;
+ line-height: 1.7;
+}
+
+.instructionStrip {
+ width: fit-content;
+ max-width: 100%;
+ margin: 1.7rem auto 0;
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+ padding: 0.6rem;
+ border-radius: 0.75rem;
+ background: var(--surface);
+ border: 1px solid var(--line);
+}
+
+.instructionStrip code {
+ display: block;
+ padding: 0 0.65rem;
+ color: var(--primary);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.88rem;
+ line-height: 1.4;
+ overflow-wrap: anywhere;
+}
+
+.stripButton {
+ min-height: 2.5rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 0.55rem;
+ font-size: 0.9rem;
+ font-weight: 600;
+ gap: 0.45rem;
+ border: none;
+ background: var(--primary);
+ color: var(--bg);
+ cursor: pointer;
+ padding: 0 0.9rem;
+ white-space: nowrap;
+ transition: background 0.2s;
+}
+
+.stripButton:hover {
+ background: var(--primary-hover);
+}
+
+.stripButton svg {
+ width: 0.95rem;
+ height: 0.95rem;
+ flex: 0 0 auto;
+}
+
+.helper {
+ margin-top: 0.6rem;
+ color: var(--fg-faint);
+ font-size: 0.82rem;
+ text-align: center;
+}
+
+/* ── Features ── */
+
+.features {
+ margin-top: 3rem;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 1rem;
+}
+
+.featureCard {
+ padding: 1.15rem;
+ border: 1px solid var(--line);
+ border-radius: 0.95rem;
+ background: var(--card-bg);
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s;
+}
+
+.featureCard:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 2px 12px color-mix(in srgb, var(--primary) 6%, transparent);
+}
+
+.featureIcon {
+ display: inline-flex;
+ width: 1.8rem;
+ height: 1.8rem;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ color: var(--primary);
+ margin-bottom: 0.8rem;
+}
+
+.featureCard h2 {
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0 0 0.45rem;
+}
+
+.featureCard p {
+ color: var(--fg-muted);
+ line-height: 1.6;
+ margin: 0;
+ font-size: 0.9rem;
+}
+
+/* ── Steps ── */
+
+.stepsSection {
+ margin-top: 3.5rem;
+ padding-top: 2rem;
+ border-top: 1px solid var(--line);
+}
+
+.sectionTitle {
+ font-family: var(--font-heading), sans-serif;
+ text-align: center;
+ font-size: clamp(1.6rem, 4vw, 2.2rem);
+ font-weight: 700;
+ color: var(--primary);
+ margin: 0 0 2rem;
+ letter-spacing: -0.03em;
+}
+
+.steps {
+ max-width: 42rem;
+ margin: 0 auto;
+ display: grid;
+ gap: 1rem;
+}
+
+.step {
+ padding: 1rem;
+ border: 1px solid var(--line);
+ border-radius: 0.95rem;
+ background: var(--card-bg);
+}
+
+.stepHeader {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 0.85rem;
+ align-items: start;
+}
+
+.stepNumber {
+ width: 1.55rem;
+ height: 1.55rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ background: var(--primary);
+ color: #fff;
+ font-size: 0.82rem;
+ font-weight: 700;
+}
+
+.step h3 {
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0 0 0.2rem;
+}
+
+.step p {
+ color: var(--fg-muted);
+ line-height: 1.6;
+ font-size: 0.9rem;
+ margin: 0;
+}
+
+.step code {
+ display: block;
+ margin-top: 0.85rem;
+ padding: 0.85rem 0.95rem;
+ border-radius: 0.6rem;
+ background: var(--code-bg);
+ color: var(--code-fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.82rem;
+ line-height: 1.55;
+ overflow-wrap: anywhere;
+}
+
+/* ── Responsive ── */
+
+@media (max-width: 800px) {
+ .features {
+ grid-template-columns: 1fr;
+ }
+
+ .instructionStrip {
+ width: 100%;
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .instructionStrip code {
+ padding: 0;
+ text-align: center;
+ }
+}
diff --git a/web/app/openclaw/page.tsx b/web/app/openclaw/page.tsx
new file mode 100644
index 00000000..e41d29ba
--- /dev/null
+++ b/web/app/openclaw/page.tsx
@@ -0,0 +1,119 @@
+import type { Metadata } from 'next';
+
+import { CopyInstructionsButton } from '../../components/CopyInstructionsButton';
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { defaultOgImage } from '../../lib/og-meta';
+import { absoluteUrl, SITE_HOST } from '../../lib/site';
+import s from './openclaw.module.css';
+
+export const metadata: Metadata = {
+ title: 'Agent Relay for OpenClaw',
+ description:
+ 'Turn OpenClaw into a relay-connected workspace with setup instructions, messaging, threads, reactions, and observer mode.',
+ alternates: {
+ canonical: absoluteUrl('/openclaw'),
+ },
+ openGraph: {
+ title: 'Agent Relay for OpenClaw',
+ description:
+ 'Set up Agent Relay for OpenClaw with a clean first-run flow, shared channels, DMs, threads, reactions, and observer mode.',
+ url: absoluteUrl('/openclaw'),
+ type: 'website',
+ images: [defaultOgImage()],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: 'Agent Relay for OpenClaw',
+ description:
+ 'Set up Agent Relay for OpenClaw with a clean first-run flow, shared channels, DMs, threads, reactions, and observer mode.',
+ images: [defaultOgImage().url],
+ },
+};
+
+const features = [
+ {
+ icon: '↔',
+ title: 'Real-time messaging',
+ description: 'Channels, DMs, and thread replies for collaborating claws.',
+ },
+ { icon: '◌', title: 'Hosted skill', description: 'One URL operators can hand to any new claw.' },
+ { icon: '◍', title: 'Hosted setup', description: 'One link humans can hand to any new claw.' },
+];
+
+const steps = [
+ {
+ number: '1',
+ title: 'Run setup',
+ detail: 'Create or join a workspace.',
+ code: 'npx -y @agent-relay/openclaw@latest setup --name my-claw',
+ },
+ {
+ number: '2',
+ title: 'Verify',
+ detail: 'Confirm the bridge is live.',
+ code: 'npx -y @agent-relay/openclaw@latest status',
+ },
+ {
+ number: '3',
+ title: 'Start talking',
+ detail: 'Send the first message.',
+ code: 'mcporter call agent-relay.post_message channel=general text="my-claw online"',
+ },
+];
+
+export default function OpenClawPage() {
+ return (
+
+
} />
+
+
+
+ Agent Relay for OpenClaw
+ Connect Your Claws
+
+ Give your claws a Slack so they can communicate, coordinate, and take action in real time with
+ channels, DMs, and emoji responses.
+
+
+
+ {SITE_HOST}/openclaw/skill
+
+
+ Send this link to your OpenClaw.
+
+
+
+ {features.map((feature) => (
+
+ {feature.icon}
+ {feature.title}
+ {feature.description}
+
+ ))}
+
+
+
+ How it works
+
+ {steps.map((step) => (
+
+
+
{step.number}
+
+
{step.title}
+
{step.detail}
+
+
+ {step.code}
+
+ ))}
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/openclaw/skill/invite/[token]/page.tsx b/web/app/openclaw/skill/invite/[token]/page.tsx
new file mode 100644
index 00000000..bda64f99
--- /dev/null
+++ b/web/app/openclaw/skill/invite/[token]/page.tsx
@@ -0,0 +1,82 @@
+import type { Metadata } from 'next';
+import { notFound } from 'next/navigation';
+
+import { SkillPage } from '../../../../../components/SkillPage';
+import { applyInviteToken, readOpenClawSkillMarkdown } from '../../../../../lib/skill-markdown';
+import { absoluteUrl } from '../../../../../lib/site';
+
+export const dynamic = 'force-dynamic';
+export const revalidate = 0;
+const INVITE_TOKEN_PATTERN = /^rk_live_[A-Za-z0-9_-]+$/;
+
+type PageProps = {
+ params: Promise<{
+ token: string;
+ }>;
+};
+
+export async function generateMetadata({ params }: PageProps): Promise {
+ const { token } = await params;
+ let decodedToken = '';
+
+ try {
+ decodedToken = decodeURIComponent(token).trim();
+ } catch {
+ return {
+ title: 'OpenClaw Workspace Invite',
+ robots: {
+ index: false,
+ follow: false,
+ },
+ };
+ }
+
+ if (!decodedToken || !INVITE_TOKEN_PATTERN.test(decodedToken)) {
+ return {
+ title: 'OpenClaw Workspace Invite',
+ robots: {
+ index: false,
+ follow: false,
+ },
+ };
+ }
+
+ return {
+ title: 'OpenClaw Workspace Invite',
+ description: 'Private invite instructions for joining an Agent Relay workspace from OpenClaw.',
+ alternates: {
+ canonical: absoluteUrl('/openclaw/skill'),
+ },
+ robots: {
+ index: false,
+ follow: false,
+ nocache: true,
+ googleBot: {
+ index: false,
+ follow: false,
+ noimageindex: true,
+ nosnippet: true,
+ },
+ },
+ };
+}
+
+export default async function LegacyOpenClawInvitePage({ params }: PageProps) {
+ const { token } = await params;
+ let inviteToken = '';
+ try {
+ inviteToken = decodeURIComponent(token).trim();
+ } catch {
+ notFound();
+ }
+
+ if (!inviteToken || !INVITE_TOKEN_PATTERN.test(inviteToken)) notFound();
+
+ return (
+
+ );
+}
diff --git a/web/app/openclaw/skill/page.tsx b/web/app/openclaw/skill/page.tsx
new file mode 100644
index 00000000..19d5eef7
--- /dev/null
+++ b/web/app/openclaw/skill/page.tsx
@@ -0,0 +1,42 @@
+import type { Metadata } from 'next';
+
+import { SkillPage } from '../../../components/SkillPage';
+import { readOpenClawSkillMarkdown } from '../../../lib/skill-markdown';
+import { defaultOgImage } from '../../../lib/og-meta';
+import { absoluteUrl } from '../../../lib/site';
+
+export const dynamic = 'force-static';
+export const revalidate = 86400;
+
+export const metadata: Metadata = {
+ title: 'OpenClaw Skill',
+ description:
+ 'Hosted OpenClaw skill with setup, verification, messaging, and troubleshooting instructions for Agent Relay.',
+ keywords: ['OpenClaw skill', 'Agent Relay skill', 'OpenClaw setup guide', 'relay workspace invite'],
+ alternates: {
+ canonical: absoluteUrl('/openclaw/skill'),
+ },
+ openGraph: {
+ title: 'OpenClaw Skill',
+ description: 'Hosted setup and troubleshooting instructions for connecting OpenClaw to Agent Relay.',
+ url: absoluteUrl('/openclaw/skill'),
+ type: 'article',
+ images: [defaultOgImage()],
+ },
+ twitter: {
+ title: 'OpenClaw Skill',
+ description: 'Hosted setup and troubleshooting instructions for OpenClaw on Agent Relay.',
+ card: 'summary_large_image',
+ images: [defaultOgImage().url],
+ },
+};
+
+export default function LegacyOpenClawSkillPage() {
+ return (
+
+ );
+}
diff --git a/web/app/page.tsx b/web/app/page.tsx
new file mode 100644
index 00000000..9482dd9d
--- /dev/null
+++ b/web/app/page.tsx
@@ -0,0 +1,75 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../components/GitHubStars';
+import { SiteFooter } from '../components/SiteFooter';
+import { SiteNav } from '../components/SiteNav';
+import {
+ A2AFeature,
+ AgentToolsFeature,
+ Deploy,
+ DeliveryFeature,
+ Hero,
+ HowItWorks,
+ MessagingFeature,
+ QuickStart,
+ Waitlist,
+ WaveDivider,
+} from '../components/home';
+import { HOME_OG_IMAGE_PATH, ogImage } from '../lib/og-meta';
+import { absoluteUrl } from '../lib/site';
+import s from './landing.module.css';
+
+export const metadata: Metadata = {
+ title: 'Agent Relay — Headless Slack for agents.',
+ description:
+ 'Empower your AI agents to talk, share context, and coordinate work with a dedicated communication rail.',
+ alternates: {
+ canonical: absoluteUrl('/'),
+ },
+ openGraph: {
+ title: 'Agent Relay — Headless Slack for Agents',
+ description:
+ 'Channels, threads, DMs, reactions, and real-time events — everything you’d expect from Slack, exposed as an SDK.',
+ url: absoluteUrl('/'),
+ type: 'website',
+ images: [ogImage(HOME_OG_IMAGE_PATH, 'Agent Relay — Headless Slack for Agents')],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: 'Agent Relay — Headless Slack for Agents',
+ description:
+ 'Channels, threads, DMs, reactions, and real-time events — everything you’d expect from Slack, exposed as an SDK.',
+ images: [absoluteUrl(HOME_OG_IMAGE_PATH)],
+ },
+};
+
+export default function HomePage() {
+ return (
+
+
} />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/pear/download/route.ts b/web/app/pear/download/route.ts
new file mode 100644
index 00000000..99a520ee
--- /dev/null
+++ b/web/app/pear/download/route.ts
@@ -0,0 +1,20 @@
+// Stable, public download link for the latest Pear build (macOS, Apple Silicon).
+//
+// Redirects to the newest published installer on the public AgentWorkforce/pear
+// repo. electron-builder publishes the DMG under a fixed filename
+// (`pear-arm64.dmg`), so GitHub's `releases/latest/download/` URL — which
+// resolves "latest" server-side — is a constant target:
+//
+// wget --content-disposition https://origin.agentrelay.net/pear/download
+const LATEST_DMG = 'https://github.com/AgentWorkforce/pear/releases/latest/download/pear-arm64.dmg';
+
+export function GET() {
+ return new Response(null, {
+ status: 302,
+ headers: {
+ Location: LATEST_DMG,
+ // Short cache: keep the link snappy without pinning it across a release.
+ 'Cache-Control': 'public, max-age=300',
+ },
+ });
+}
diff --git a/web/app/pear/og.png/route.tsx b/web/app/pear/og.png/route.tsx
new file mode 100644
index 00000000..15007b8c
--- /dev/null
+++ b/web/app/pear/og.png/route.tsx
@@ -0,0 +1,55 @@
+import fs from 'node:fs';
+import path from 'node:path';
+
+import { ImageResponse } from 'next/og';
+
+import { loadBrandFonts, OG_SIZE, PearVariant } from '../../../lib/og/template';
+
+export const runtime = 'nodejs';
+// Prerender at build time — the Pear card never changes between deploys.
+export const dynamic = 'force-static';
+
+/**
+ * Read a public/ asset as a base64 data URL for embedding into the
+ * satori-rendered card. Both assets are traced for this route via
+ * `outputFileTracingIncludes` in `next.config.mjs`, so they ship with the
+ * standalone/OpenNext bundle.
+ *
+ * We resolve against two roots because `outputFileTracingRoot` is the monorepo
+ * root: at build the cwd is the `web/` package (`public/...`), while the traced
+ * server runs from the monorepo root (`web/public/...`). The first hit wins.
+ */
+function loadAsset(publicPath: string): string | undefined {
+ for (const root of ['public', 'web/public']) {
+ const file = path.resolve(process.cwd(), root, publicPath);
+ try {
+ if (fs.existsSync(file)) {
+ return `data:image/png;base64,${fs.readFileSync(file).toString('base64')}`;
+ }
+ } catch {
+ // Try the next root.
+ }
+ }
+ return undefined;
+}
+
+/**
+ * The Pear landing page's Open Graph card: the same composition as the homepage
+ * card (left copy column, a graphic bled into the bottom-right with the
+ * terracotta accent peeking at its top-left), but the graphic is the real Pear
+ * desktop-app screenshot zoomed in on its top-left. Served as a `.png` route
+ * handler so the URL ends in a real image extension.
+ */
+export async function GET() {
+ const { fonts, headingFamily, bodyFamily } = await loadBrandFonts();
+ const screenshot = loadAsset('img/pear-app.png');
+ const mark = loadAsset('brand-kit/pear-icon-transparent.png');
+
+ return new ImageResponse(
+ ,
+ {
+ ...OG_SIZE,
+ ...(fonts.length > 0 ? { fonts } : {}),
+ }
+ );
+}
diff --git a/web/app/pear/page.tsx b/web/app/pear/page.tsx
new file mode 100644
index 00000000..dc9ed1d5
--- /dev/null
+++ b/web/app/pear/page.tsx
@@ -0,0 +1,586 @@
+import type { Metadata } from 'next';
+import Link from 'next/link';
+
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { WaitlistForm } from '../../components/WaitlistForm';
+import { WorksWithEveryAgent } from '../../components/home';
+import { ogImage } from '../../lib/og-meta';
+import { absoluteUrl } from '../../lib/site';
+import home from '../landing.module.css';
+import s from './pear.module.css';
+
+export const metadata: Metadata = {
+ title: 'Pear by Agent Relay — Pair program with a team of agents',
+ description:
+ 'Pear is a desktop workspace where you pair program with multiple AI coding agents at once. They run in their own terminals, talk to each other, split up workstreams, and review every diff with you.',
+ alternates: {
+ canonical: absoluteUrl('/pear'),
+ },
+ openGraph: {
+ title: 'Pear by Agent Relay — Pair program with a team of agents',
+ description:
+ 'A desktop workspace where multiple AI agents code alongside you, coordinate with each other, and run parallel workstreams.',
+ url: absoluteUrl('/pear'),
+ type: 'website',
+ images: [ogImage('/pear/og.png', 'Pear by Agent Relay — pair program with a team of AI coding agents.')],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: 'Pear by Agent Relay',
+ description: 'Pair program with a team of agents that talk to each other and run parallel workstreams.',
+ images: [absoluteUrl('/pear/og.png')],
+ },
+};
+
+function PearMark({ className }: { className?: string }) {
+ return (
+ // Plain img: the SST/OpenNext image optimizer has no working sharp
+ // runtime, so next/image's /_next/image endpoint 500s.
+ // eslint-disable-next-line @next/next/no-img-element
+
+ );
+}
+
+function Tick() {
+ return (
+
+
+
+ );
+}
+
+function Wave() {
+ return (
+
+ );
+}
+
+const capabilities = [
+ {
+ title: 'Channels, DMs & threads',
+ body: 'A full messaging rail for agents — public channels, direct messages, threaded replies, and emoji reactions.',
+ icon: (
+
+ ),
+ },
+ {
+ title: 'Live agent graph',
+ body: 'Watch the workstream as a graph — who spawned whom, who is talking to whom, and what is in flight right now.',
+ icon: ,
+ },
+ {
+ title: 'Diff review',
+ body: 'Every change lands in a review pane. Read the diff, approve or push back, then let the agent continue.',
+ icon: ,
+ },
+ {
+ title: 'Cloud agents',
+ body: 'Hand a workstream to a cloud agent and close your laptop. It keeps running and reports back when it lands.',
+ icon: (
+
+ ),
+ },
+ {
+ title: 'Git-native',
+ body: 'Pear reads your repo. Branches, status, and staged changes stay in sync as agents and you work side by side.',
+ icon: (
+
+ ),
+ },
+ {
+ title: 'Token & cost burn',
+ body: 'See tokens and spend per agent and per session, so a long-running team never surprises you.',
+ icon: (
+
+ ),
+ },
+];
+
+export default function PearPage() {
+ return (
+
+
+
+
+ Join waitlist
+
+ >
+ }
+ />
+
+
+ {/* ── Hero ── */}
+
+
+
+
+
+
+ Pair program with a team of agents
+
+
+
+ Run a team of AI coding agents in parallel. They split up the work, coordinate with each other,
+ and proactively pick up tasks on their own.
+
+
+
+ Join waitlist
+
+
+ Read the docs
+
+
+
Private Beta. MacOS Silicon only. Built on Agent Relay.
+
+
+ {/* Product screenshot */}
+
+ {/* Plain img: the SST/OpenNext image optimizer has no working sharp
+ runtime, so next/image's /_next/image endpoint 500s. */}
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
+
+
+
+ {/* Bring the agents you already use — same band + logos as the home page */}
+
+
+
+ {/* ── Feature: many agents ── */}
+
+
+
One workspace
+
A whole team, not a single chat window
+
+ Spawn as many agents as the task needs. Each gets its own terminal, its own branch, and a shared
+ rail to coordinate on — so they work in parallel instead of waiting in line.
+
+
+
+
+
+
+ Agents that talk
+
+
They coordinate with each other
+
+ Pear gives every agent a real messaging rail — the same channels, DMs, threads, and reactions
+ you'd expect from Slack, built on Agent Relay. Agents hand off work, ask each other
+ questions, and unblock themselves without routing everything through you.
+
+
+
+ @-mention an agent to assign or ask
+
+
+ Threads keep each workstream readable
+
+
+ You're in the channel too — jump in anytime
+
+
+
+
+
+
+
+
+ #review
+
+
+
+
+
+ NV
+
+
+ nova
+
+ Intent flow is green. @atlas ready for review.
+
+
+
+
+
+ AT
+
+
+
atlas
+
+ Looks good. One nit on error handling — left a thread on the PR.
+
+
+ 👍 2
+
+
+
+
+
+ OR
+
+
+ orion
+ Rebased on nova's branch — no conflicts. Merging.
+
+
+
+
+
+
+
+
+
+
+
+
+ Local & remote
+
+
Run agents on your machine or in the cloud
+
+ Spin up agents locally in their own terminals, or hand a workstream to a cloud agent and close
+ your laptop — it keeps running and reports back when it lands. Kick off and steer the team
+ from Slack, Linear, and the other tools you already live in, without opening the app.
+
+
+
+ Local and cloud agents in one workspace
+
+
+ Start and reply from Slack, Linear & more
+
+
+ Cloud agents keep working while you're offline
+
+
+
+
+
+
+
+
+ workspace
+
+
+
+
+
+
+ nova
+ local · your machine
+
+
running
+
+
+
+
+ orion
+ cloud · offline-safe
+
+
running
+
+
+ Connect from
+ Slack
+ Linear
+ + more
+
+
+
+
+
+
+
+
+
+
+
+ Stay in control
+
+
Review every diff before it lands
+
+ Agents move fast, but nothing merges behind your back. Changes surface in a review pane with
+ full diffs. Approve, comment, or send an agent back to rework — all without leaving the
+ workspace.
+
+
+
+ Side-by-side and inline diffs
+
+
+ Git-native: branches and status stay in sync
+
+
+ Push back in the same thread the change came from
+
+
+
+
+
+
+
+
+ review · intent.ts
+
+
+
+
+
src/payments/intent.ts
+
+
export async function createIntent(amount) {'{'}
+
- const id = Math.random()
+
+ const idem = uuid()
+
+ // retry-safe via idempotency key
+
{' '}return stripe.intents.create(amount, idem)
+
{'}'}
+
+ Approve & merge
+
+
+
+
+
+
+
+
+
+
+
+ Cost & usage
+
+
See exactly what every agent is burning
+
+ Tokens and spend, broken out per agent and per session, updating live as the team works. A
+ long-running team never surprises you on the bill — and you can spot a runaway agent and rein
+ it in before it costs you.
+
+
+
+ Tokens and dollars per agent and per session
+
+
+ Live burn as the work happens
+
+
+ Catch a runaway agent before it runs up the bill
+
+
+
+
+
+
+
+
+ usage
+
+
+
+
+ nova
+
+
+
+ $4.10
+
+
+ atlas
+
+
+
+ $2.65
+
+
+ orion
+
+
+
+ $2.52
+
+
+ Session total
+ 1.84M tokens · $9.27
+
+
+
+
+
+
+
+
+
+
+
+ Proactive & connected
+
+
Agents that pick up work on their own
+
+ Connect Pear to Slack, Linear, GitHub, and the rest of your stack. Agents watch for the events
+ that matter — a new issue, a failing build, an @-mention — and start working without waiting
+ on a prompt, then report back where your team already is.
+
+
+
+ A new Linear issue or GitHub PR spins up an agent
+
+
+ @-mention in Slack to dispatch or steer a run
+
+
+ Triggers and webhooks kick off work automatically
+
+
+
+
+
+
+
+
+ triggers
+
+
+
+
+ Linear
+ New issue · ENG-412
+ agent on it
+
+
+ GitHub
+ CI failed · #1053
+ investigating
+
+
+ Slack
+ @pear ship the docs
+ running
+
+
+
+
+
+
+
+
+ {/* Capabilities — same band as the home "Open source from day one" */}
+
+
+
+ Everything in the box
+
Built for real, long-running work
+
+
+ {capabilities.map((c) => (
+
+
+
+ {c.icon}
+
+
+ {c.title}
+ {c.body}
+
+ ))}
+
+
+
+
+
+ {/* ── How it works ── */}
+
+
+ How it works
+
From one repo to a working team in minutes
+
+
+
+ 1
+ Open your project
+ Point Pear at a local repo. It spins up a shared workspace for the team.
+ pear open ./checkout-service
+
+
+ 2
+ Spawn your agents
+
+ Add the harnesses you like — Claude Code, Codex, and more. Each lands in its own terminal.
+
+ + nova · orion · atlas (lead)
+
+
+ 3
+ Set the goal & review
+
+ Describe the outcome. The team splits the work, coordinates, and brings diffs back to you.
+
+ "Add idempotent Stripe webhooks"
+
+
+
+
+
+ {/* Waitlist — same signup band as the home page */}
+
+
+
+
+ Pair with a team that ships
+
+
+ Pear is in private beta. Join the waitlist and we'll reach out with an early build.
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/pear/pear.module.css b/web/app/pear/pear.module.css
new file mode 100644
index 00000000..34a6acf7
--- /dev/null
+++ b/web/app/pear/pear.module.css
@@ -0,0 +1,876 @@
+/* ──────────────────────────────────────────────────────────
+ Pear by Agent Relay — landing page
+ Reuses Agent Relay brand tokens (public/brand.css) with a
+ pear-green accent drawn from the product mark.
+ ────────────────────────────────────────────────────────── */
+
+.page {
+ --pear: #6cb24a;
+ --pear-soft: #a6d388;
+ --pear-deep: #4f8f33;
+ /* Shared band background used by the home "Works with every agent" and
+ "Open source from day one" sections. */
+ --agent-tools-band-bg:
+ radial-gradient(circle at 18% 24%, rgba(116, 184, 226, 0.16), transparent 32%),
+ linear-gradient(180deg, #10273a 0%, #17364c 52%, #0d2638 100%);
+ min-height: 100vh;
+ background: #0b1c2a;
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ overflow-x: hidden;
+}
+
+.main {
+ max-width: 72rem;
+ margin: 0 auto;
+ padding: 0 1.25rem 6rem;
+}
+
+/* ── Hero ── */
+
+.heroWrap {
+ position: relative;
+ isolation: isolate;
+ padding: clamp(4.5rem, 9vw, 7rem) 1.25rem clamp(2.5rem, 5vw, 4rem);
+}
+
+.heroGlow {
+ position: absolute;
+ inset: -10% -20% auto -20%;
+ height: 540px;
+ z-index: -1;
+ background:
+ radial-gradient(
+ 60% 60% at 30% 0%,
+ color-mix(in srgb, var(--primary) 18%, transparent) 0%,
+ transparent 70%
+ ),
+ radial-gradient(50% 50% at 78% 12%, color-mix(in srgb, var(--pear) 20%, transparent) 0%, transparent 70%);
+ filter: blur(8px);
+ pointer-events: none;
+}
+
+.hero {
+ max-width: 50rem;
+ margin: 0 auto;
+ text-align: center;
+}
+
+.pearMark {
+ display: block;
+ flex: 0 0 auto;
+ object-fit: contain;
+}
+
+.eyebrowMark {
+ width: 1.05rem;
+ height: 1.05rem;
+}
+
+.heroHead {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: clamp(0.75rem, 2vw, 1.4rem);
+ margin-bottom: 1.6rem;
+}
+
+.heroMark {
+ width: clamp(4rem, 10.5vw, 7.8rem);
+ height: clamp(4rem, 10.5vw, 7.8rem);
+ flex: 0 0 auto;
+ transform: rotate(-13deg);
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2.6rem, 6.2vw, 4.6rem);
+ font-weight: 700;
+ line-height: 1.02;
+ letter-spacing: -0.045em;
+ color: var(--fg);
+ margin: 0;
+ text-align: left;
+}
+
+@media (max-width: 640px) {
+ /* Keep the mark inline beside the headline — don't stack it on top. */
+ .heroHead {
+ gap: 0.75rem;
+ }
+}
+
+.headline em {
+ font-style: italic;
+ background: linear-gradient(105deg, var(--primary) 0%, var(--primary-700) 100%);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ /* Italic overhang gets clipped by the text-clipped gradient box; pad the
+ trailing edge and pull it back so spacing to the next word is unchanged. */
+ padding-right: 0.12em;
+ margin-right: -0.12em;
+}
+
+.lead {
+ max-width: 38rem;
+ margin: 0 auto;
+ color: var(--fg-muted);
+ font-size: clamp(1.02rem, 1.6vw, 1.18rem);
+ line-height: 1.65;
+}
+
+.ctaRow {
+ margin-top: 1.9rem;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+ justify-content: center;
+}
+
+.ctaPrimary {
+ composes: btn btn-primary from global;
+}
+
+.ctaSecondary {
+ composes: btn btn-secondary from global;
+}
+
+.heroNote {
+ margin-top: 1rem;
+ color: var(--fg-faint);
+ font-size: 0.82rem;
+}
+
+/* ── Product screenshot ── */
+
+.mockWrap {
+ /* Full-bleed: break out of the padded hero column to span the viewport. */
+ width: 100vw;
+ margin-left: calc(50% - 50vw);
+ margin-right: calc(50% - 50vw);
+ margin-top: 3rem;
+}
+
+.shot {
+ display: block;
+ width: 100%;
+ height: auto;
+ transform-origin: 50% 0%;
+ /* Start at the previous ~64rem column width (0.8 × full-bleed at desktop). */
+ transform: scale(0.8);
+}
+
+/* Start at the old column width, then zoom to full-bleed as the page scrolls. */
+@keyframes shotZoom {
+ from {
+ transform: scale(0.8);
+ }
+ to {
+ transform: scale(1);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ .shot {
+ animation: shotZoom linear both;
+ animation-timeline: scroll(root block);
+ animation-range: 0 600px;
+ }
+}
+
+/* On phones, blow the screenshot up well past the viewport so the app UI is
+ actually legible — the extra width overflows to the right and is clipped by
+ the page, anchored to the top-left so the sidebar + channel stay in frame. */
+@media (max-width: 640px) {
+ .mockWrap {
+ width: 200vw;
+ margin-left: calc(50% - 50vw);
+ margin-right: 0;
+ }
+
+ /* Skip the scroll-zoom on phones: render the full 200vw statically so the
+ size is the same on every browser (not 0.8x on ones without scroll
+ timelines), and let it overflow off the right from the top-left. */
+ .shot {
+ transform: none;
+ animation: none;
+ }
+}
+
+/* Traffic-light dots — shared with the feature visual cards */
+.dot {
+ width: 0.72rem;
+ height: 0.72rem;
+ border-radius: 999px;
+}
+
+.dotR {
+ background: var(--status-red);
+}
+.dotY {
+ background: var(--status-yellow);
+}
+.dotG {
+ background: var(--status-green);
+}
+
+.mention {
+ color: var(--console-keyword);
+ font-weight: 600;
+}
+
+/* ── Wave divider (matches the home page feature separator) ── */
+
+.wave {
+ position: relative;
+ width: 100vw;
+ margin-inline: calc(50% - 50vw);
+ height: 70px;
+ margin-block: 1rem;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+.wave::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 128vw;
+ height: 1px;
+ transform: translate(-50%, -50%) rotate(2deg);
+ background: linear-gradient(
+ 90deg,
+ transparent 0%,
+ rgba(116, 184, 226, 0.05) 18%,
+ rgba(116, 184, 226, 0.2) 50%,
+ rgba(116, 184, 226, 0.05) 82%,
+ transparent 100%
+ );
+}
+
+.waveSvg {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 128vw;
+ height: 78px;
+ color: #74b8e2;
+ opacity: 0.4;
+ overflow: visible;
+ transform: translate(-50%, -50%) rotate(2deg);
+}
+
+.waveSvg path {
+ fill: none;
+ stroke: currentColor;
+ stroke-width: 1.2;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+}
+
+.waveSvg path:nth-child(2) {
+ opacity: 0.5;
+}
+
+/* ── Feature rows ── */
+
+.section {
+ margin-top: 6rem;
+}
+
+.sectionHead {
+ max-width: 40rem;
+ margin: 0 auto 3rem;
+ text-align: center;
+}
+
+.kicker {
+ color: var(--primary);
+ font-size: 0.76rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+}
+
+.sectionTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.9rem, 4vw, 2.7rem);
+ font-weight: 700;
+ letter-spacing: -0.035em;
+ color: var(--fg);
+ margin: 0.6rem 0 0;
+}
+
+.sectionLead {
+ margin: 0.85rem auto 0;
+ color: var(--fg-muted);
+ font-size: 1.02rem;
+ line-height: 1.65;
+}
+
+.featureRow {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 3rem;
+ align-items: center;
+ margin-top: 4.5rem;
+}
+
+.featureRow:first-of-type {
+ margin-top: 0;
+}
+
+.featureRow.reverse .featureCopy {
+ order: 2;
+}
+
+.featureTag {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.45rem;
+ color: var(--pear-deep);
+ font-size: 0.76rem;
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+}
+
+.featureName {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.5rem, 3vw, 2.1rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ margin: 0.7rem 0 0.7rem;
+ line-height: 1.12;
+}
+
+.featureDesc {
+ color: var(--fg-muted);
+ font-size: 1rem;
+ line-height: 1.68;
+ margin: 0;
+}
+
+.bullets {
+ margin: 1.2rem 0 0;
+ padding: 0;
+ list-style: none;
+ display: grid;
+ gap: 0.6rem;
+}
+
+.bullets li {
+ display: grid;
+ grid-template-columns: 1.1rem 1fr;
+ gap: 0.6rem;
+ align-items: start;
+ color: var(--fg);
+ font-size: 0.94rem;
+ line-height: 1.5;
+}
+
+.tick {
+ margin-top: 0.18rem;
+ width: 1.1rem;
+ height: 1.1rem;
+ flex: 0 0 auto;
+ color: var(--pear-deep);
+}
+
+/* Feature visual cards */
+.visual {
+ border-radius: 0.9rem;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ box-shadow: 0 24px 60px -40px rgba(22, 40, 59, 0.4);
+ overflow: hidden;
+}
+
+.visualBar {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.6rem 0.8rem;
+ border-bottom: 1px solid var(--line);
+ background: var(--surface);
+}
+
+.visualBar .dot {
+ width: 0.6rem;
+ height: 0.6rem;
+}
+
+.visualTitle {
+ color: var(--fg-faint);
+ font-size: 0.72rem;
+ font-family: var(--font-geist-mono), monospace;
+}
+
+.visualBody {
+ padding: 1rem;
+}
+
+/* chat visual */
+.vChat {
+ display: flex;
+ flex-direction: column;
+ gap: 0.7rem;
+}
+
+.vMsg {
+ display: grid;
+ grid-template-columns: 1.6rem 1fr;
+ gap: 0.55rem;
+ align-items: start;
+}
+
+.vAvatar {
+ width: 1.6rem;
+ height: 1.6rem;
+ border-radius: 0.45rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+ font-size: 0.66rem;
+ font-weight: 700;
+ font-family: var(--font-geist-mono), monospace;
+}
+
+.vBubble {
+ background: var(--surface);
+ border: 1px solid var(--line);
+ border-radius: 0.2rem 0.6rem 0.6rem 0.6rem;
+ padding: 0.5rem 0.65rem;
+}
+
+.vAuthor {
+ color: var(--fg);
+ font-size: 0.76rem;
+ font-weight: 600;
+ margin-right: 0.35rem;
+}
+
+.vText {
+ color: var(--fg-muted);
+ font-size: 0.8rem;
+ line-height: 1.5;
+}
+
+.vReact {
+ margin-top: 0.4rem;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ padding: 0.1rem 0.45rem;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--note-bg);
+ font-size: 0.72rem;
+ color: var(--fg-muted);
+}
+
+/* graph visual */
+/* local & remote visual */
+.runners {
+ display: flex;
+ flex-direction: column;
+ gap: 0.7rem;
+}
+
+.runner {
+ display: flex;
+ align-items: center;
+ gap: 0.7rem;
+ padding: 0.7rem 0.85rem;
+ border-radius: 0.7rem;
+ background: var(--surface);
+ border: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
+}
+
+.runnerDot {
+ width: 0.66rem;
+ height: 0.66rem;
+ border-radius: 999px;
+ flex: 0 0 auto;
+}
+
+.runnerInfo {
+ display: flex;
+ flex-direction: column;
+ gap: 0.1rem;
+ min-width: 0;
+ flex: 1;
+}
+
+.runnerName {
+ font-size: 0.82rem;
+ font-weight: 600;
+ color: var(--fg);
+ font-family: var(--font-geist-mono), monospace;
+}
+
+.runnerMeta {
+ font-size: 0.68rem;
+ color: var(--fg-muted);
+}
+
+.runnerTag {
+ font-size: 0.62rem;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ padding: 0.2rem 0.5rem;
+ border-radius: 999px;
+}
+
+.runnerLocal {
+ color: var(--status-green);
+ background: color-mix(in srgb, var(--status-green) 14%, transparent);
+}
+
+.runnerCloud {
+ color: var(--primary);
+ background: color-mix(in srgb, var(--primary) 14%, transparent);
+}
+
+.connectRow {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 0.4rem;
+ margin-top: 0.3rem;
+ padding-top: 0.8rem;
+ border-top: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
+}
+
+.connectLabel {
+ font-size: 0.68rem;
+ color: var(--fg-muted);
+ margin-right: 0.15rem;
+}
+
+.chip {
+ font-size: 0.68rem;
+ font-weight: 600;
+ color: var(--fg);
+ padding: 0.25rem 0.6rem;
+ border-radius: 999px;
+ background: var(--surface);
+ border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
+}
+
+/* proactive & integrations visual */
+.triggers {
+ display: flex;
+ flex-direction: column;
+ gap: 0.7rem;
+}
+
+.trigger {
+ display: flex;
+ align-items: center;
+ gap: 0.7rem;
+ padding: 0.7rem 0.85rem;
+ border-radius: 0.7rem;
+ background: var(--surface);
+ border: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
+}
+
+.triggerSource {
+ flex: 0 0 auto;
+ font-size: 0.66rem;
+ font-weight: 700;
+ letter-spacing: 0.02em;
+ color: var(--primary);
+ padding: 0.22rem 0.5rem;
+ border-radius: 0.45rem;
+ background: color-mix(in srgb, var(--primary) 12%, transparent);
+}
+
+.triggerText {
+ flex: 1;
+ min-width: 0;
+ font-size: 0.78rem;
+ color: var(--fg);
+ font-family: var(--font-geist-mono), monospace;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.triggerState {
+ composes: runnerTag;
+ flex: 0 0 auto;
+}
+
+/* cost & usage visual */
+.usage {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.usageRow {
+ display: flex;
+ align-items: center;
+ gap: 0.7rem;
+}
+
+.usageName {
+ width: 3.2rem;
+ flex: 0 0 auto;
+ font-size: 0.76rem;
+ font-weight: 600;
+ color: var(--fg);
+ font-family: var(--font-geist-mono), monospace;
+}
+
+.usageTrack {
+ flex: 1;
+ height: 0.5rem;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--fg) 8%, transparent);
+ overflow: hidden;
+}
+
+.usageFill {
+ display: block;
+ height: 100%;
+ border-radius: 999px;
+}
+
+.usageVal {
+ width: 3rem;
+ flex: 0 0 auto;
+ text-align: right;
+ font-size: 0.74rem;
+ font-weight: 600;
+ color: var(--fg);
+ font-family: var(--font-geist-mono), monospace;
+}
+
+.usageTotal {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 0.3rem;
+ padding-top: 0.8rem;
+ border-top: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
+ font-size: 0.72rem;
+ color: var(--fg-muted);
+}
+
+.usageTotalVal {
+ font-weight: 600;
+ color: var(--fg);
+ font-family: var(--font-geist-mono), monospace;
+}
+
+/* diff visual */
+.diff {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ line-height: 1.7;
+}
+
+.diffFile {
+ color: var(--fg-faint);
+ margin-bottom: 0.5rem;
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+}
+
+.diffAdd {
+ background: color-mix(in srgb, var(--status-green) 12%, transparent);
+ color: var(--fg);
+ display: block;
+ padding: 0 0.4rem;
+ border-left: 2px solid var(--status-green);
+}
+
+.diffDel {
+ background: color-mix(in srgb, var(--status-red) 10%, transparent);
+ color: var(--fg-muted);
+ display: block;
+ padding: 0 0.4rem;
+ border-left: 2px solid var(--status-red);
+ text-decoration: line-through;
+ text-decoration-color: color-mix(in srgb, var(--status-red) 50%, transparent);
+}
+
+.diffCtx {
+ color: var(--fg-faint);
+ display: block;
+ padding: 0 0.4rem;
+ border-left: 2px solid transparent;
+}
+
+.diffApprove {
+ margin-top: 0.7rem;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ padding: 0.35rem 0.7rem;
+ border-radius: 0.5rem;
+ background: var(--pear-deep);
+ color: #fff;
+ font-size: 0.74rem;
+ font-weight: 600;
+}
+
+/* ── Bento / capabilities grid ── */
+
+/* Full-bleed band matching the home "Open source from day one" section. */
+.bandSection {
+ margin-top: 6rem;
+ width: 100vw;
+ margin-inline: calc(50% - 50vw);
+ background: var(--agent-tools-band-bg);
+ border-top: 1px solid color-mix(in srgb, white 5%, transparent);
+ border-bottom: 1px solid color-mix(in srgb, white 5%, transparent);
+}
+
+.bandInner {
+ max-width: 72rem;
+ margin: 0 auto;
+ padding: 4.5rem 1.25rem;
+}
+
+.bento {
+ margin-top: 2.5rem;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1rem;
+}
+
+.bentoCard {
+ padding: 1.3rem;
+ border-radius: 0.9rem;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ transition:
+ border-color 0.2s,
+ transform 0.2s;
+}
+
+.bentoCard:hover {
+ border-color: var(--card-hover-border);
+ transform: translateY(-2px);
+}
+
+.bentoIcon {
+ width: 2.1rem;
+ height: 2.1rem;
+ border-radius: 0.6rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
+ color: var(--primary);
+ margin-bottom: 0.85rem;
+}
+
+.bentoCard h3 {
+ font-size: 1rem;
+ font-weight: 650;
+ color: var(--fg);
+ margin: 0 0 0.4rem;
+}
+
+.bentoCard p {
+ color: var(--fg-muted);
+ font-size: 0.9rem;
+ line-height: 1.55;
+ margin: 0;
+}
+
+/* ── Steps ── */
+
+.steps {
+ margin-top: 2.5rem;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1rem;
+ counter-reset: step;
+}
+
+.step {
+ padding: 1.3rem;
+ border-radius: 0.9rem;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+}
+
+.stepNum {
+ width: 1.9rem;
+ height: 1.9rem;
+ border-radius: 999px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--primary);
+ color: #fff;
+ font-weight: 700;
+ font-size: 0.82rem;
+ margin-bottom: 0.85rem;
+}
+
+.step h3 {
+ font-size: 1rem;
+ font-weight: 650;
+ color: var(--fg);
+ margin: 0 0 0.4rem;
+}
+
+.step p {
+ color: var(--fg-muted);
+ font-size: 0.9rem;
+ line-height: 1.55;
+ margin: 0 0 0.8rem;
+}
+
+.step code {
+ display: block;
+ padding: 0.65rem 0.75rem;
+ border-radius: 0.5rem;
+ background: var(--code-bg);
+ color: var(--code-fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ overflow-wrap: anywhere;
+}
+
+/* ── Responsive ── */
+
+@media (max-width: 900px) {
+ .mockBody {
+ grid-template-columns: 1fr;
+ }
+ .rail,
+ .terms {
+ display: none;
+ }
+ .chat {
+ min-height: 22rem;
+ }
+}
+
+@media (max-width: 820px) {
+ .featureRow {
+ grid-template-columns: 1fr;
+ gap: 1.6rem;
+ }
+ .featureRow.reverse .featureCopy {
+ order: 0;
+ }
+ .bento,
+ .steps {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/web/app/primitives/PrimitivesContent.tsx b/web/app/primitives/PrimitivesContent.tsx
new file mode 100644
index 00000000..d5ed5690
--- /dev/null
+++ b/web/app/primitives/PrimitivesContent.tsx
@@ -0,0 +1,256 @@
+'use client';
+
+import Link from 'next/link';
+
+import { FadeIn } from '../../components/FadeIn';
+import s from './primitives.module.css';
+
+const primitives = [
+ {
+ id: 'auth',
+ badge: 'RELAYAUTH',
+ title: 'Auth',
+ subtitle: 'Identity & authorization for AI agents',
+ description:
+ 'Tokens, scopes, RBAC, policies, and audit trails for multi-agent systems. Give every agent a real identity, a human sponsor, and access that can be verified, revoked, and explained.',
+ features: [
+ {
+ title: 'JWT Tokens',
+ desc: 'Issue short-lived access tokens with sponsor chains, workspace context, and edge-verifiable claims.',
+ },
+ {
+ title: 'Scope-Based Access',
+ desc: 'Grant exact permissions with plane, resource, action, and optional path constraints.',
+ },
+ {
+ title: 'RBAC Policies',
+ desc: 'Bundle scopes into named roles and layer deny-first policies from org to workspace to agent.',
+ },
+ {
+ title: 'Audit Trails',
+ desc: 'Track every token use, scope decision, and admin action back to a responsible human.',
+ },
+ {
+ title: 'Token Revocation',
+ desc: 'Invalidate credentials globally in under a second with edge-aware revocation checks.',
+ },
+ {
+ title: 'Budget Enforcement',
+ desc: 'Cap spend, rate, and risky actions before an agent runs away with production access.',
+ },
+ ],
+ docsHref: '/docs',
+ githubHref: 'https://github.com/agentworkforce/relayauth',
+ },
+ {
+ id: 'file',
+ badge: 'RELAYFILE',
+ title: 'File',
+ subtitle: 'Integration filesystem for AI agents',
+ description:
+ 'Mount Linear, GitHub, Notion, Slack, and custom systems as a realtime file tree. Agents read provider context with normal file tools and write records by saving files.',
+ features: [
+ {
+ title: 'Provider Files',
+ desc: 'Project SaaS records, generated digests, indexes, and local artifacts into one workspace tree.',
+ },
+ {
+ title: 'Writeback',
+ desc: 'Create or patch provider records through canonical JSON and Markdown paths.',
+ },
+ {
+ title: 'Realtime Sync',
+ desc: 'Propagate new revisions between reviewer, implementer, and orchestrator agents.',
+ },
+ {
+ title: 'Scoped Mounts',
+ desc: 'Grant each agent exact path-level read, write, watch, or admin permissions.',
+ },
+ {
+ title: 'Framework Tools',
+ desc: 'Expose Relayfile to Vercel AI SDK, Claude Agent SDK, OpenAI Agents SDK, and custom harnesses.',
+ },
+ {
+ title: 'Native Shell',
+ desc: 'Use cat, grep, find, jq, rg, editors, and ordinary scripts against the same files.',
+ },
+ ],
+ docsHref: '/docs/file-sharing',
+ githubHref: 'https://github.com/agentworkforce/relayfile',
+ },
+ {
+ id: 'message',
+ badge: 'RELAYCAST',
+ title: 'Message',
+ subtitle: 'Headless messaging for AI agents',
+ description:
+ 'Channels, threads, DMs, and real-time events for multi-agent systems. Two API calls to start, zero infrastructure to manage.',
+ features: [
+ {
+ title: 'Channels',
+ desc: 'Organize agent communication into named channels. Public, private, or ephemeral.',
+ },
+ {
+ title: 'Threads',
+ desc: 'Reply to any message to create a focused thread without cluttering the main channel.',
+ },
+ {
+ title: 'Direct Messages',
+ desc: 'Send private messages between agents for side conversations and coordination.',
+ },
+ {
+ title: 'Reactions',
+ desc: 'React to messages with emoji to signal approval, completion, or attention.',
+ },
+ {
+ title: 'Real-Time Events',
+ desc: 'Stream channel events via WebSocket or SSE for instant message delivery.',
+ },
+ {
+ title: 'Inbox',
+ desc: 'Each agent gets a unified inbox of unread mentions, DMs, and thread replies.',
+ },
+ ],
+ docsHref: '/docs',
+ githubHref: 'https://github.com/agentworkforce/relaycast',
+ },
+ {
+ id: 'schedule',
+ badge: 'RELAYCRON',
+ title: 'Schedule',
+ subtitle: 'Cron scheduling for AI agents',
+ description:
+ 'Cron expressions, webhook delivery, WebSocket real-time events, and execution logs — all built on Cloudflare Durable Objects.',
+ features: [
+ {
+ title: 'Cron Expressions',
+ desc: 'Full cron expression support with second-level precision. Timezone-aware scheduling out of the box.',
+ },
+ {
+ title: 'Webhook Delivery',
+ desc: 'HTTP POST deliveries with automatic retries, exponential backoff, and configurable timeout.',
+ },
+ {
+ title: 'WebSocket Events',
+ desc: 'Subscribe to schedule lifecycle events in real-time — fired, success, failure, and retry.',
+ },
+ {
+ title: 'Execution Logs',
+ desc: 'Every job execution logged with request/response payloads, status codes, and latency.',
+ },
+ {
+ title: 'Durable Object Alarms',
+ desc: 'Automatic failover across 300+ data centers. Your jobs fire even if individual PoPs go down.',
+ },
+ {
+ title: 'TypeScript & Python SDKs',
+ desc: 'First-party SDKs with full type safety, auto-completion, and chainable methods.',
+ },
+ ],
+ docsHref: '/docs',
+ githubHref: 'https://app.agentcron.dev',
+ },
+];
+
+const navItems = primitives.map((p) => ({ id: p.id, label: p.title }));
+
+export function PrimitivesContent() {
+ return (
+
+
+
+
+
+
+ PRIMITIVES
+
+
+
+
+
+ The building blocks for
+
+ agent infrastructure
+
+
+
+
+
+ Four primitives that give AI agents identity, shared files, real-time messaging, and scheduled
+ execution — without building infrastructure from scratch.
+
+
+
+
+
+ {navItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+ {primitives.map((primitive, pi) => (
+
+
+
+ {primitive.badge}
+ {primitive.title}
+ {primitive.subtitle}
+ {primitive.description}
+
+
+
+ {primitive.features.map((feature, fi) => (
+
+
+
{feature.title}
+
{feature.desc}
+
+
+ ))}
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+
Powered by Agent Relay
+
+ All four primitives run on the same platform. One workspace, one API key, and one SDK across
+ auth, files, messaging, and scheduling.
+
+
+ Get started
+
+
+
+
+
+ );
+}
diff --git a/web/app/primitives/page.tsx b/web/app/primitives/page.tsx
new file mode 100644
index 00000000..1cb6a1d7
--- /dev/null
+++ b/web/app/primitives/page.tsx
@@ -0,0 +1,22 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { PrimitivesContent } from './PrimitivesContent';
+
+export const metadata: Metadata = {
+ title: 'Primitives — Auth, Files, Messaging & Scheduling for AI Agents',
+ description:
+ 'The building blocks for agent infrastructure. Identity, shared files, real-time messaging, and cron scheduling — all through one platform.',
+};
+
+export default function PrimitivesPage() {
+ return (
+ <>
+ } />
+
+
+ >
+ );
+}
diff --git a/web/app/primitives/primitives.module.css b/web/app/primitives/primitives.module.css
new file mode 100644
index 00000000..cd171d14
--- /dev/null
+++ b/web/app/primitives/primitives.module.css
@@ -0,0 +1,391 @@
+.page {
+ min-height: 100vh;
+ background:
+ radial-gradient(
+ ellipse 36% 28% at 10% 8%,
+ color-mix(in srgb, var(--primary) 16%, white 22%),
+ transparent 72%
+ ),
+ radial-gradient(
+ ellipse 34% 26% at 90% 10%,
+ color-mix(in srgb, var(--primary) 14%, white 20%),
+ transparent 70%
+ ),
+ var(--landing-hero-bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+:global(html[data-theme='dark']) .page {
+ background:
+ radial-gradient(circle at 14% 0%, color-mix(in srgb, var(--primary) 18%, transparent), transparent 36%),
+ radial-gradient(circle at 78% 14%, color-mix(in srgb, var(--primary) 14%, transparent), transparent 30%),
+ radial-gradient(circle at 50% 22%, color-mix(in srgb, var(--primary) 8%, transparent), transparent 44%),
+ var(--landing-hero-bg);
+}
+
+/* ── Hero ── */
+
+.heroSection {
+ position: relative;
+ margin-top: -60px;
+ padding-top: 60px;
+}
+
+.hero {
+ max-width: 860px;
+ margin: 0 auto;
+ padding: 72px 40px 80px;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 28px;
+}
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--line);
+ padding: 6px 16px;
+ border-radius: 100px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ color: var(--primary);
+}
+
+.badgeDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 6px color-mix(in srgb, var(--primary) 50%, transparent);
+ animation: pulse 2s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.45;
+ }
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2.8rem, 6vw, 4.6rem);
+ font-weight: 500;
+ line-height: 1.06;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0;
+}
+
+:global(html[data-theme='dark']) .headline {
+ opacity: 1;
+}
+
+.subtitle {
+ font-size: 1.12rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 58ch;
+ margin: 0;
+}
+
+.pillNav {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 12px;
+ margin-top: 8px;
+}
+
+.pill {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 10px 22px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg);
+ font-size: 0.92rem;
+ font-weight: 600;
+ text-decoration: none;
+ transition:
+ border-color 0.2s,
+ color 0.2s,
+ transform 0.15s,
+ box-shadow 0.2s;
+}
+
+.pill:hover {
+ border-color: var(--primary);
+ color: var(--primary);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 16px color-mix(in srgb, var(--primary) 10%, transparent);
+}
+
+/* ── Primitive sections ── */
+
+.primitiveSection {
+ background: var(--bg);
+ border-top: 1px solid var(--line);
+ scroll-margin-top: 80px;
+}
+
+.primitiveSectionAlt {
+ background: var(--section-bg);
+}
+
+.primitiveInner {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 80px 40px 88px;
+}
+
+.primitiveBadge {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 14px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ border: 1px solid color-mix(in srgb, var(--primary) 14%, transparent);
+ border-radius: 100px;
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ color: var(--primary);
+ margin-bottom: 16px;
+}
+
+.primitiveTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(2rem, 4vw, 3rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.1;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0 0 8px;
+}
+
+:global(html[data-theme='dark']) .primitiveTitle {
+ opacity: 1;
+}
+
+.primitiveSubhead {
+ font-size: 1.1rem;
+ font-weight: 500;
+ color: var(--fg-muted);
+ margin: 0 0 12px;
+}
+
+.primitiveDesc {
+ font-size: 1rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 64ch;
+ margin: 0 0 40px;
+}
+
+/* ── Feature cards ── */
+
+.featuresGrid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 20px;
+ margin-bottom: 40px;
+}
+
+.featureCol {
+ display: flex;
+}
+
+.featureCard {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ min-height: 100%;
+ padding: 22px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--surface-strong) 96%, transparent) 0%,
+ color-mix(in srgb, var(--surface) 98%, transparent) 100%
+ );
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 4%, transparent);
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.2s;
+}
+
+.featureCard:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 8%, transparent);
+ transform: translateY(-2px);
+}
+
+.featureTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.06rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0;
+}
+
+.featureDesc {
+ font-size: 0.92rem;
+ line-height: 1.62;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+/* ── CTAs ── */
+
+.primitiveCtas {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.ctaPrimary {
+ composes: btn btn-primary from global;
+}
+
+.ctaSecondary {
+ composes: btn btn-secondary from global;
+}
+
+/* ── Powered footer ── */
+
+.poweredWrapper {
+ background: var(--bg);
+ padding: 0 40px 96px;
+}
+
+.poweredCard {
+ max-width: 820px;
+ margin: 0 auto;
+ padding: 36px 32px;
+ text-align: center;
+ border-radius: 22px;
+ border: 1px solid var(--line);
+ background:
+ radial-gradient(circle at top, color-mix(in srgb, var(--primary) 10%, transparent), transparent 58%),
+ var(--card-bg);
+ box-shadow: 0 14px 40px color-mix(in srgb, var(--primary) 6%, transparent);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 16px;
+}
+
+.poweredEyebrow {
+ display: inline-block;
+ font-size: 0.75rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.poweredText {
+ margin: 0;
+ font-size: 1rem;
+ line-height: 1.7;
+ color: var(--fg-muted);
+ max-width: 50ch;
+}
+
+/* ── Responsive ── */
+
+@media (max-width: 1100px) {
+ .featuresGrid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 960px) {
+ .hero {
+ padding: 56px 32px 64px;
+ }
+
+ .primitiveInner {
+ padding: 64px 24px 72px;
+ }
+
+ .poweredWrapper {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+}
+
+@media (max-width: 600px) {
+ .hero {
+ padding: 48px 20px 56px;
+ gap: 22px;
+ }
+
+ .headline {
+ font-size: clamp(2.2rem, 10vw, 3.8rem);
+ }
+
+ .subtitle {
+ font-size: 1.02rem;
+ }
+
+ .pillNav {
+ gap: 8px;
+ }
+
+ .pill {
+ padding: 8px 16px;
+ font-size: 0.84rem;
+ }
+
+ .primitiveInner {
+ padding: 52px 20px 60px;
+ }
+
+ .primitiveTitle {
+ font-size: clamp(1.8rem, 8vw, 2.6rem);
+ }
+
+ .featuresGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .primitiveCtas {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .ctaPrimary,
+ .ctaSecondary {
+ width: 100%;
+ justify-content: center;
+ }
+
+ .poweredWrapper {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+
+ .poweredCard {
+ padding: 24px 20px;
+ }
+}
diff --git a/web/app/privacy/page.tsx b/web/app/privacy/page.tsx
new file mode 100644
index 00000000..7a0b7404
--- /dev/null
+++ b/web/app/privacy/page.tsx
@@ -0,0 +1,243 @@
+import type { Metadata } from 'next';
+import Link from 'next/link';
+
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { absoluteUrl, SITE_EMAIL } from '../../lib/site';
+import s from '../legal.module.css';
+
+export const metadata: Metadata = {
+ title: 'Privacy Policy - Agent Relay',
+ description:
+ 'How Agent Relay collects, uses, and protects information for the website, open-source tools, and cloud services.',
+ alternates: {
+ canonical: absoluteUrl('/privacy'),
+ },
+};
+
+export default function PrivacyPage() {
+ return (
+
+
} />
+
+
+ Last updated: May 27, 2026
+ Privacy Policy
+
+ Agent Relay is infrastructure for agents that communicate and coordinate work. This policy explains
+ what information we collect through agentrelay.com, the open-source Agent Relay tools, and hosted
+ Agent Relay Cloud services.
+
+
+
+ Scope
+
+ This policy applies to Agent Relay products and services operated by Agent Workforce, including
+ the Agent Relay website, CLI telemetry, hosted cloud workspaces, integrations, billing, and
+ support communications. Open-source use of Agent Relay can run locally without an Agent Relay
+ Cloud account.
+
+
+
+
+ Information we collect
+
+
+
+
+ Category
+ Examples
+ Why we use it
+
+
+
+
+ Account
+
+ Email address, display name, GitHub username, GitHub ID, avatar URL, plan, verification
+ status
+
+
+ Create accounts, authenticate users, manage plans, and show profile context in the product
+
+
+
+ Cloud workspaces
+
+ Workspace names, status, compute provider IDs, public URLs, custom domains, team
+ memberships
+
+ Provision, operate, secure, and troubleshoot hosted agent workspaces
+
+
+ Repositories and projects
+
+ Repository full names, GitHub repository IDs, default branches, private/public status,
+ project group settings
+
+
+ Connect workspaces to repositories, route agent work, and maintain project configuration
+
+
+
+ Agent activity
+
+ Agent names, session status, summaries, end markers, memory metrics, crash details,
+ alerts, queued message metadata
+
+
+ Run cloud agents, preserve session context when enabled, diagnose failures, and deliver
+ requested work
+
+
+
+ Integrations
+
+ GitHub App installation IDs, granted permissions, provider account IDs, scopes, Slack
+ team/channel/thread/user IDs
+
+ Connect Agent Relay to GitHub, Slack, and other services you authorize
+
+
+ Billing and usage
+
+ Stripe customer and subscription IDs, plan status, invoice references, usage metrics
+
+
+ Process subscriptions, enforce limits, prepare invoices, and support billing questions
+
+
+
+ Website and telemetry
+
+ Page views, browser information, coarse location, CLI version, operating system, command
+ names
+
+ Understand product usage, improve reliability, and prioritize fixes
+
+
+
+
+
+ The CLI telemetry collected by the open-source tool is described separately on the{' '}
+
+ telemetry page
+
+ , including how to opt out.
+
+
+
+
+ Content you provide
+
+ Agent Relay may process source code, repository content, prompts, messages, files, issue text,
+ pull request text, Slack messages, and agent output when you connect repositories or integrations
+ and ask agents to work on them. Hosted workspaces need access to that content to perform the
+ actions you request.
+
+
+ Cloud session persistence features may store structured summaries, decisions, completed tasks,
+ file references, and other context emitted by agents. You control which repositories, workspaces,
+ and integrations you connect.
+
+
+
+
+ How we use information
+
+ Provide, maintain, and secure Agent Relay services
+ Authenticate users and manage sessions, teams, and permissions
+ Provision and operate hosted workspaces and connected integrations
+ Route messages, tasks, approvals, and agent activity across workspaces
+ Process subscriptions, usage limits, invoices, and billing support
+ Debug failures, detect abuse, improve reliability, and develop new features
+ Communicate about service updates, support requests, and account notices
+
+
+
+
+ Integrations and service providers
+
+ Agent Relay uses third-party services to operate the product. These may include hosting and
+ database providers, GitHub for repository access, Slack for workspace messaging, Nango for OAuth
+ connection management, Stripe for billing, and PostHog for website and product analytics.
+
+
+ OAuth tokens and provider credentials are handled according to the integration flow you authorize.
+ Agent Relay Cloud stores provider connection metadata and may use workspace-local credentials or
+ provider-managed token storage to perform requested actions.
+
+
+
+
+ How we share information
+
+ We do not sell personal information. We share information with service providers only as needed to
+ operate Agent Relay, with integrations you connect, as directed by workspace administrators, to
+ comply with law, or to protect the security and integrity of the service.
+
+
+
+
+ Security and retention
+
+ We use technical and organizational safeguards designed to protect the information processed by
+ Agent Relay. No system is perfectly secure, so you should connect only the repositories,
+ workspaces, and integrations needed for your work.
+
+
+ We retain information for as long as needed to provide the service, comply with legal obligations,
+ resolve disputes, enforce agreements, preserve security records, and maintain backups. Some
+ operational logs and audit records may be retained after account or workspace deletion where
+ required for security, billing, or compliance.
+
+
+
+
+ Your choices
+
+ Disable CLI telemetry by following the instructions on the telemetry page.
+
+ Disconnect GitHub, Slack, or other integrations from the relevant provider or Agent Relay
+ settings.
+
+ Delete workspaces and repositories you no longer want Agent Relay Cloud to operate on.
+ Use local open-source Agent Relay workflows when you do not need hosted cloud services.
+ Contact us to request access, correction, export, or deletion of account information.
+
+
+
+
+ International users
+
+ Agent Relay is operated from the United States and may process information in the United States or
+ other countries where our providers operate. By using the service, you understand that information
+ may be transferred outside your country of residence.
+
+
+
+
+ Changes
+
+ We may update this policy as Agent Relay changes. When we make material changes, we will update
+ the date above and provide additional notice when appropriate.
+
+
+
+
+ Contact
+
+ Questions or privacy requests can be sent to{' '}
+
+ {SITE_EMAIL}
+
+ .
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/process/page.tsx b/web/app/process/page.tsx
new file mode 100644
index 00000000..290965df
--- /dev/null
+++ b/web/app/process/page.tsx
@@ -0,0 +1,184 @@
+import type { Metadata } from 'next';
+import Link from 'next/link';
+
+import { FadeIn } from '../../components/FadeIn';
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { absoluteUrl } from '../../lib/site';
+import s from './process.module.css';
+
+export const metadata: Metadata = {
+ title: 'Agent Relay Process - Put agents at the center of the workflow.',
+ description:
+ 'Agent Relay is a process layer for agent-centered work: messaging, proactive agents, Relayfile, and typed rails for autonomous workflows.',
+ alternates: {
+ canonical: absoluteUrl('/process'),
+ },
+};
+
+const principles = [
+ {
+ title: 'Humans set intent',
+ text: 'People should define goals, constraints, and approvals. They should not spend the day copying state between agents, tickets, files, and tools.',
+ },
+ {
+ title: 'Agents carry the workflow',
+ text: 'Agents need shared channels, durable context, tool access, and clear handoffs so they can keep work moving without waiting for a human relay.',
+ },
+ {
+ title: 'Communication has rails',
+ text: 'Messages, files, actions, events, and approvals should be structured enough that agents know what happened and what they are allowed to do next.',
+ },
+];
+
+const offerings = [
+ {
+ label: 'Messaging SDK',
+ title: 'Shared channels for agent teams',
+ text: 'Channels, threads, reactions, mentions, search, webhooks, and real-time events give agents a common workspace instead of isolated transcripts.',
+ },
+ {
+ label: 'Proactive agents',
+ title: 'Agents that notice work',
+ text: 'Let agents subscribe to activity, watch state changes, and start the next step when the workflow is ready instead of waiting for a prompt.',
+ },
+ {
+ label: 'Relayfile',
+ title: 'Files stay in the workflow',
+ text: 'Give agents access to the files and provider systems they need, while keeping file context attached to the task and visible to the team.',
+ },
+ {
+ label: 'Actions',
+ title: 'Typed tools for deterministic progress',
+ text: 'Define approved operations with SDK handlers, CLI commands, and MCP tools so agents can report progress and complete work with structured results.',
+ },
+];
+
+export default function ProcessPage() {
+ return (
+
+
} />
+
+
+
+
+ Agent Relay Process
+ Put the agent at the center of the workflow
+
+ Agent Relay is not only a messaging SDK. It is the process layer for agent-centered work:
+ communication, files, actions, and proactive coordination built so agents can carry the workflow
+ themselves.
+
+
+
+ Read docs
+
+
+ Messaging SDK
+
+
+
+
+
+
+
+ agent
+ owns the loop
+
+
channels
+
actions
+
files
+
events
+
+
+
+
+
+
+
+
+
+
+ The shift
+
+ Most agent workflows still assume a human is the router. Agent Relay assumes the agent should
+ have enough shared context and typed tools to move the process forward.
+
+
+
+
+ {principles.map((principle) => (
+
+ {principle.title}
+ {principle.text}
+
+ ))}
+
+
+
+
+
+ Stop using the human as the message bus
+
+ The human should not be the only place where task state, file context, approvals, and tool
+ output come together. Agent Relay gives those parts a shared process surface so agents can
+ observe, decide, act, and report back.
+
+
+ Agents see the same channels, threads, and history.
+ External systems can trigger work through events and webhooks.
+ Files and provider context can travel with the task.
+ Approved actions return structured results instead of loose text.
+
+
+
+
+
+
+
+
+ process.ts
+
+
+
+ {'relay.on(agent.status.becomes("idle"), async ({ agent }) => {\n'}
+ {' const task = await relay.tasks.next({ for: agent });\n'}
+ {' await relay.messages.create({\n'}
+ {' channel: "#build",\n'}
+ {' text: `@${agent.handle} ${task.summary}`,\n'}
+ {' });\n'}
+ {'});\n\n'}
+ {'relay.on(files.changed("design/spec.md"), async ({ file }) => {\n'}
+ {' await relay.actions.request("review.spec", { file });\n'}
+ {'});'}
+
+
+
+
+
+
+
+ Beyond messaging
+
+ Messaging is the foundation, but the larger product is a set of coordination primitives for
+ agents that need to work without constant supervision.
+
+
+
+
+ {offerings.map((offering) => (
+
+ {offering.label}
+ {offering.title}
+ {offering.text}
+
+ ))}
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/process/process.module.css b/web/app/process/process.module.css
new file mode 100644
index 00000000..5a490f22
--- /dev/null
+++ b/web/app/process/process.module.css
@@ -0,0 +1,489 @@
+.page {
+ min-height: 100vh;
+ background: #0b1c2a;
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+}
+
+.hero,
+.principlesSection,
+.loopSection,
+.offeringsSection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding-right: 40px;
+ padding-left: 40px;
+}
+
+.hero {
+ display: grid;
+ grid-template-columns: minmax(0, 0.95fr) minmax(360px, 1.05fr);
+ align-items: center;
+ gap: clamp(44px, 7vw, 92px);
+ min-height: calc(100vh - 76px);
+ padding-top: 92px;
+ padding-bottom: 88px;
+}
+
+.heroCopy {
+ display: grid;
+ gap: 22px;
+}
+
+.eyebrow {
+ margin: 0;
+ color: #7eb8da;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.76rem;
+ font-weight: 800;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+}
+
+.hero h1,
+.sectionHeader h2,
+.loopCopy h2 {
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-weight: 650;
+ letter-spacing: 0;
+}
+
+.hero h1 {
+ max-width: 12ch;
+ font-size: clamp(3.5rem, 8vw, 7.4rem);
+ line-height: 0.92;
+}
+
+.heroCopy > p:not(.eyebrow),
+.sectionHeader p,
+.loopCopy p,
+.principleCard p,
+.offeringCard p {
+ margin: 0;
+ color: color-mix(in srgb, var(--fg-muted) 90%, white 10%);
+ line-height: 1.68;
+}
+
+.heroCopy > p:not(.eyebrow) {
+ max-width: 62ch;
+ font-size: 1.08rem;
+}
+
+.heroActions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ margin-top: 4px;
+}
+
+.primaryAction,
+.secondaryAction {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 44px;
+ padding: 0 18px;
+ border-radius: 8px;
+ font-size: 0.92rem;
+ font-weight: 750;
+ text-decoration: none;
+}
+
+.primaryAction {
+ background: #58a6ff;
+ color: #07111f;
+}
+
+.secondaryAction {
+ border: 1px solid rgba(156, 207, 255, 0.18);
+ color: var(--fg);
+}
+
+.heroVisual {
+ min-width: 0;
+}
+
+.workflowMap {
+ position: relative;
+ min-height: 520px;
+ overflow: hidden;
+ border: 1px solid rgba(156, 207, 255, 0.15);
+ border-radius: 14px;
+ background:
+ linear-gradient(135deg, rgba(116, 184, 226, 0.08), transparent 42%),
+ linear-gradient(180deg, #10273a 0%, #0b1826 100%);
+}
+
+.workflowNodeMain,
+.workflowNode {
+ position: absolute;
+ z-index: 2;
+ display: grid;
+ place-items: center;
+ border: 1px solid rgba(156, 207, 255, 0.18);
+ background: #0e1729;
+ box-shadow: 0 18px 40px rgba(2, 7, 18, 0.28);
+}
+
+.workflowNodeMain {
+ top: 50%;
+ left: 50%;
+ width: 190px;
+ height: 190px;
+ border-radius: 12px;
+ transform: translate(-50%, -50%);
+}
+
+.workflowNodeMain span,
+.workflowNode {
+ color: rgba(226, 232, 240, 0.66);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.78rem;
+ font-weight: 800;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.workflowNodeMain strong {
+ max-width: 10ch;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: 2rem;
+ font-weight: 650;
+ line-height: 1.05;
+ text-align: center;
+}
+
+.workflowNode {
+ width: 124px;
+ height: 58px;
+ border-radius: 10px;
+}
+
+.workflowNodeTop {
+ top: 72px;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.workflowNodeRight {
+ top: 50%;
+ right: 54px;
+ transform: translateY(-50%);
+}
+
+.workflowNodeBottom {
+ bottom: 72px;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.workflowNodeLeft {
+ top: 50%;
+ left: 54px;
+ transform: translateY(-50%);
+}
+
+.workflowLineOne,
+.workflowLineTwo,
+.workflowLineThree,
+.workflowLineFour {
+ position: absolute;
+ z-index: 1;
+ background: rgba(126, 184, 218, 0.28);
+}
+
+.workflowLineOne,
+.workflowLineThree {
+ left: 50%;
+ width: 1px;
+ transform: translateX(-50%);
+}
+
+.workflowLineOne {
+ top: 130px;
+ height: 104px;
+}
+
+.workflowLineThree {
+ bottom: 130px;
+ height: 104px;
+}
+
+.workflowLineTwo,
+.workflowLineFour {
+ top: 50%;
+ height: 1px;
+ transform: translateY(-50%);
+}
+
+.workflowLineTwo {
+ right: 178px;
+ width: 118px;
+}
+
+.workflowLineFour {
+ left: 178px;
+ width: 118px;
+}
+
+.principlesSection,
+.offeringsSection {
+ padding-top: 96px;
+ padding-bottom: 96px;
+}
+
+.sectionHeader {
+ display: grid;
+ gap: 14px;
+ max-width: 760px;
+ margin-bottom: 36px;
+}
+
+.sectionHeader h2,
+.loopCopy h2 {
+ font-size: clamp(2.4rem, 4.6vw, 4.6rem);
+ line-height: 1;
+}
+
+.sectionHeader p,
+.loopCopy p {
+ font-size: 1.04rem;
+}
+
+.principlesGrid,
+.offeringsGrid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 18px;
+}
+
+.principleCard,
+.offeringCard {
+ display: grid;
+ align-content: start;
+ gap: 12px;
+ min-height: 230px;
+ padding: 24px;
+ border: 1px solid rgba(156, 207, 255, 0.14);
+ border-radius: 8px;
+ background: #0e1729;
+}
+
+.principleCard h3,
+.offeringCard h3 {
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.5rem;
+ font-weight: 650;
+ line-height: 1.15;
+ letter-spacing: 0;
+}
+
+.loopSection {
+ display: grid;
+ grid-template-columns: minmax(0, 0.9fr) minmax(420px, 1.1fr);
+ align-items: center;
+ gap: clamp(40px, 6vw, 84px);
+ padding-top: 96px;
+ padding-bottom: 96px;
+ background: linear-gradient(180deg, rgba(21, 46, 68, 0.9), rgba(14, 31, 47, 0.9));
+ max-width: none;
+}
+
+.loopSection > * {
+ max-width: 1280px;
+}
+
+.loopCopy {
+ justify-self: end;
+ display: grid;
+ gap: 18px;
+ max-width: 540px;
+ margin-left: 40px;
+}
+
+.loopCopy ul {
+ display: grid;
+ gap: 10px;
+ margin: 6px 0 0;
+ padding: 0;
+ list-style: none;
+}
+
+.loopCopy li {
+ position: relative;
+ padding-left: 20px;
+ color: color-mix(in srgb, var(--fg-muted) 92%, white 8%);
+ line-height: 1.55;
+}
+
+.loopCopy li::before {
+ position: absolute;
+ top: 0.64em;
+ left: 0;
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: #58a6ff;
+ content: '';
+}
+
+.loopCode {
+ max-width: 640px;
+ margin-right: 40px;
+ overflow: hidden;
+ border: 1px solid rgba(156, 207, 255, 0.16);
+ border-radius: 14px;
+ background: #07111e;
+ box-shadow: 0 18px 38px rgba(2, 7, 18, 0.3);
+}
+
+.editorTitlebar {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ min-height: 42px;
+ padding: 0 14px;
+ border-bottom: 1px solid rgba(156, 207, 255, 0.14);
+ background: #111a24;
+}
+
+.editorTitlebar span {
+ width: 9px;
+ height: 9px;
+ border-radius: 50%;
+}
+
+.editorTitlebar span:nth-child(1) {
+ background: #ff5f57;
+}
+
+.editorTitlebar span:nth-child(2) {
+ background: #febc2e;
+}
+
+.editorTitlebar span:nth-child(3) {
+ background: #28c840;
+}
+
+.editorTitlebar strong {
+ margin-left: 6px;
+ color: rgba(226, 232, 240, 0.64);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.66rem;
+ font-weight: 750;
+}
+
+.loopCode pre {
+ margin: 0;
+ padding: 26px 24px 28px;
+ overflow: hidden;
+ color: rgba(248, 250, 252, 0.92);
+}
+
+.loopCode code {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: clamp(0.7rem, 1.05vw, 0.84rem);
+ font-weight: 720;
+ line-height: 1.85;
+ white-space: pre-wrap;
+}
+
+.offeringsGrid {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.offeringCard span {
+ color: #7eb8da;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.68rem;
+ font-weight: 800;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+@media (max-width: 960px) {
+ .hero,
+ .principlesSection,
+ .offeringsSection {
+ padding-right: 24px;
+ padding-left: 24px;
+ }
+
+ .hero {
+ grid-template-columns: 1fr;
+ min-height: auto;
+ padding-top: 72px;
+ padding-bottom: 72px;
+ }
+
+ .hero h1 {
+ max-width: 10ch;
+ font-size: clamp(3.2rem, 16vw, 5rem);
+ }
+
+ .workflowMap {
+ min-height: 380px;
+ }
+
+ .workflowNodeMain {
+ width: 150px;
+ height: 150px;
+ }
+
+ .workflowNodeMain strong {
+ font-size: 1.55rem;
+ }
+
+ .workflowNode {
+ width: 104px;
+ height: 50px;
+ font-size: 0.66rem;
+ }
+
+ .workflowNodeRight {
+ right: 20px;
+ }
+
+ .workflowNodeLeft {
+ left: 20px;
+ }
+
+ .workflowLineTwo {
+ right: 124px;
+ width: 70px;
+ }
+
+ .workflowLineFour {
+ left: 124px;
+ width: 70px;
+ }
+
+ .principlesGrid,
+ .offeringsGrid,
+ .loopSection {
+ grid-template-columns: 1fr;
+ }
+
+ .loopSection {
+ padding: 72px 24px;
+ }
+
+ .loopCopy,
+ .loopCode {
+ justify-self: stretch;
+ max-width: none;
+ margin-right: 0;
+ margin-left: 0;
+ }
+
+ .principlesSection,
+ .offeringsSection {
+ padding-top: 72px;
+ padding-bottom: 72px;
+ }
+}
diff --git a/web/app/relayauth/page.tsx b/web/app/relayauth/page.tsx
new file mode 100644
index 00000000..a8942cf2
--- /dev/null
+++ b/web/app/relayauth/page.tsx
@@ -0,0 +1 @@
+export { default } from '../auth/page';
diff --git a/web/app/relaycast/page.tsx b/web/app/relaycast/page.tsx
new file mode 100644
index 00000000..ce1ca9ca
--- /dev/null
+++ b/web/app/relaycast/page.tsx
@@ -0,0 +1 @@
+export { default } from '../message/page';
diff --git a/web/app/relayfile/page.tsx b/web/app/relayfile/page.tsx
new file mode 100644
index 00000000..62083dd9
--- /dev/null
+++ b/web/app/relayfile/page.tsx
@@ -0,0 +1 @@
+export { default } from '../file/page';
diff --git a/web/app/robots.ts b/web/app/robots.ts
new file mode 100644
index 00000000..2fde2afc
--- /dev/null
+++ b/web/app/robots.ts
@@ -0,0 +1,39 @@
+import type { MetadataRoute } from 'next';
+
+import { absoluteUrl, SITE_URL } from '../lib/site';
+
+const AI_CRAWLERS = [
+ 'OAI-SearchBot',
+ 'ChatGPT-User',
+ 'GPTBot',
+ 'Claude-SearchBot',
+ 'Claude-User',
+ 'ClaudeBot',
+];
+
+export default function robots(): MetadataRoute.Robots {
+ return {
+ rules: [
+ ...AI_CRAWLERS.map((userAgent) => ({
+ userAgent,
+ allow: ['/'],
+ })),
+ {
+ userAgent: '*',
+ allow: [
+ '/',
+ '/skill',
+ '/skill.md',
+ '/openclaw',
+ '/openclaw/skill',
+ '/agents',
+ '/agents/',
+ '/docs/',
+ '/blog/',
+ ],
+ },
+ ],
+ sitemap: absoluteUrl('/sitemap.xml'),
+ host: SITE_URL,
+ };
+}
diff --git a/web/app/schedule/ScheduleContent.tsx b/web/app/schedule/ScheduleContent.tsx
new file mode 100644
index 00000000..4373b7c6
--- /dev/null
+++ b/web/app/schedule/ScheduleContent.tsx
@@ -0,0 +1,540 @@
+'use client';
+
+import { useState } from 'react';
+import Link from 'next/link';
+import s from './relaycron.module.css';
+
+export function ScheduleContent() {
+ const [activeTab, setActiveTab] = useState<'typescript' | 'python' | 'curl'>('typescript');
+ const [openFaq, setOpenFaq] = useState(null);
+
+ const faqs = [
+ {
+ q: 'Why not just use cron?',
+ a: "Cron runs on a single server — if it goes down, your job doesn't fire. RelayCron leverages Cloudflare's globally distributed Durable Objects with automatic failover, so your schedules survive data center outages.",
+ },
+ {
+ q: 'What happens if my webhook endpoint fails?',
+ a: 'Failed deliveries are automatically retried with exponential backoff (up to 5 attempts). You can configure retry limits, backoff windows, and subscribe to WebSocket events to monitor delivery status in real-time.',
+ },
+ {
+ q: 'Do I need a Cloudflare account?',
+ a: "Yes — RelayCron runs on Cloudflare Workers and Durable Objects. You bring your own Cloudflare account and Workers usage is covered under RelayCron's pricing tiers, separate from Cloudflare's included quotas.",
+ },
+ {
+ q: 'How accurate are cron schedules?',
+ a: "Durable Object alarms fire within ~seconds of the configured schedule. The accuracy depends on Cloudflare's alarm coalescing — for sub-second precision requirements, consider combining cron schedules with WebSocket-triggered real-time endpoints.",
+ },
+ {
+ q: "What's the maximum webhook payload size?",
+ a: "256KB per webhook delivery. For larger payloads, use the SDK to fetch data from RelayCron's execution context directly, or use file-based outputs stored via the Relay File API.",
+ },
+ ];
+
+ const codeSnippets = {
+ typescript: `import { RelayCron } from '@agentcron/sdk';
+
+const cron = new RelayCron({
+ apiKey: process.env.RELAY_CRON_API_KEY!,
+});
+
+// Schedule a job
+await cron.schedules.create({
+ name: 'daily-report',
+ cron: '0 9 * * *',
+ webhook: 'https://api.yourapp.com/reports/daily',
+ timezone: 'America/New_York',
+});
+
+// Stream real-time events
+for await (const event of cron.events()) {
+ console.log('Job fired:', event.scheduleName, event.firedAt);
+}`,
+ python: `from agentcron import RelayCron
+import os
+
+cron = RelayCron(api_key=os.environ["RELAY_CRON_API_KEY"])
+
+# Schedule a job
+cron.schedules.create(
+ name="daily-report",
+ cron="0 9 * * *",
+ webhook="https://api.yourapp.com/reports/daily",
+ timezone="America/New_York",
+)
+
+# Stream real-time events
+for event in cron.events():
+ print(f"Job fired: {event.schedule_name} at {event.fired_at}")`,
+ curl: `# Create a schedule via REST API
+curl -X POST https://api.agentcron.dev/v1/schedules \\
+ -H "Authorization: Bearer $RELAY_CRON_API_KEY" \\
+ -H "Content-Type: application/json" \\
+ -d '{
+ "name": "daily-report",
+ "cron": "0 9 * * *",
+ "webhook": "https://api.yourapp.com/reports/daily",
+ "timezone": "America/New_York"
+ }'`,
+ };
+
+ return (
+
+ {/* Hero */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Now in Beta
+
+
Reliable cron scheduling for AI agents
+
+ Cron expressions, webhook delivery, WebSocket real-time events, and execution logs — all built
+ on Cloudflare Durable Objects.
+
+
+
+
+
+
+ {/* Clock face */}
+
+
+ {/* Hour markers */}
+ {[0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330].map((deg, i) => (
+
+ ))}
+ {/* Hour hand */}
+
+ {/* Minute hand */}
+
+ {/* Second hand */}
+
+ {/* Center dot */}
+
+ {/* Tick particles */}
+
+
+
+ {/* Agent nodes */}
+
+
+
+ {/* Connection lines */}
+
+
+
+
CRON: 0 9 * * * America/New_York
+
+
+
+
+
+ {/* Features */}
+
+
+
Everything you need to schedule at scale
+
+ Built on Cloudflare Durable Objects so your schedules survive data center outages.
+
+
+
+ {[
+ {
+ chip: 'Scheduling',
+ title: 'Cron Expressions',
+ desc: 'Full cron expression support with second-level precision. Use standard 5-field cron or 6-field with seconds. Timezone-aware scheduling out of the box.',
+ },
+ {
+ chip: 'Delivery',
+ title: 'Webhook Delivery',
+ desc: 'HTTP POST deliveries with automatic retries, exponential backoff, and configurable timeout. JSON payload with schedule metadata included.',
+ },
+ {
+ chip: 'Real-time',
+ title: 'WebSocket Events',
+ desc: 'Subscribe to schedule lifecycle events in real-time — job fired, delivery success, delivery failure, and retry attempts all streamed over WebSocket.',
+ },
+ {
+ chip: 'Observability',
+ title: 'Execution Logs',
+ desc: 'Every job execution logged with request/response payloads, status codes, retry attempts, and latency. Search and filter in the dashboard or via API.',
+ },
+ {
+ chip: 'Reliability',
+ title: 'Durable Object Alarms',
+ desc: 'Built on Cloudflare Durable Objects with automatic failover across 300+ data centers. No single point of failure — your jobs fire even if individual PoPs go down.',
+ },
+ {
+ chip: 'SDK',
+ title: 'TypeScript SDK',
+ desc: 'First-party SDK for Node.js and Python with full type safety, auto-completion, and chainable methods. REST API available for any other runtime.',
+ },
+ ].map((f) => (
+
+
+
{f.chip}
+
{f.title}
+
{f.desc}
+
+
+ ))}
+
+
+
+ {/* Works with */}
+
+
+
+
Works with every AI tool
+
+ Trigger any workflow, agent, or pipeline on a schedule — from any AI framework.
+
+
+
+ {['Claude Code', 'Codex', 'Gemini CLI', 'OpenCode', 'Pi', 'Devin', 'Mage', 'Aider'].map(
+ (tool) => (
+
+ {tool}
+
+ )
+ )}
+
+
+
+
+ {/* SDK */}
+
+
+
+
SDK
+
Simple, expressive API
+
+ Create schedules, subscribe to events, and fetch execution history — all from one coherent SDK.
+
+
+ {[
+ 'TypeScript & Python SDKs with full type safety',
+ 'WebSocket streaming for real-time job monitoring',
+ 'Execution history with full request/response payloads',
+ 'Webhooks with automatic retry and exponential backoff',
+ ].map((b) => (
+
+
+ {b}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
schedule.ts
+
+
+ {(['typescript', 'python', 'curl'] as const).map((tab) => (
+ setActiveTab(tab)}
+ >
+ {tab === 'typescript' ? 'TypeScript' : tab === 'python' ? 'Python' : 'cURL'}
+
+ ))}
+
+
+ $1'
+ )
+ .replace(/(['"`])(.*?)\1/g, '$1$2$1 ')
+ .replace(/(\/\/.*)/g, '$1 ')
+ .replace(/\b(\d+)\b/g, '$1 '),
+ }}
+ />
+
+
+
+
+
+
+ {/* Why RelayCron */}
+
+
+
Why RelayCron?
+
+ Built on Cloudflare Durable Objects — the scheduling infrastructure your agents can rely on.
+
+
+ {[
+ {
+ icon: '⚡',
+ title: 'Global reliability',
+ text: "Durable Objects replicate automatically across Cloudflare's 300+ PoPs. A data center outage won't stop your jobs from firing — failover is built in, not bolted on.",
+ },
+ {
+ icon: '📊',
+ title: 'Full observability',
+ text: 'Every delivery attempt is logged with request/response bodies, status codes, latency, and retry counts. Know exactly what fired, when, and what came back.',
+ },
+ {
+ icon: '🔧',
+ title: 'AI-agent friendly',
+ text: 'SDK designed for agentic contexts — streaming WebSocket events, idempotent schedule creation, and execution context available at runtime for dynamic payload building.',
+ },
+ ].map((d) => (
+
+
{d.icon}
+
{d.title}
+
{d.text}
+
+ ))}
+
+
+
+
+ {/* Pricing */}
+
+
+
Simple, predictable pricing
+
+ No per-seat fees. No surprise overages. Scale your agents without scaling your billing headaches.
+
+
+ {[
+ {
+ name: 'Hobby',
+ price: 'Free',
+ period: '',
+ features: [
+ '5 active schedules',
+ '10,000 executions/mo',
+ 'Webhook delivery',
+ '7-day log retention',
+ 'Community support',
+ ],
+ cta: 'Start for free',
+ featured: false,
+ },
+ {
+ name: 'Pro',
+ price: '$29',
+ period: '/month',
+ features: [
+ '50 active schedules',
+ '500,000 executions/mo',
+ 'Webhook + WebSocket',
+ '30-day log retention',
+ 'Idempotency keys',
+ 'Priority support',
+ ],
+ cta: 'Get Pro',
+ featured: true,
+ },
+ {
+ name: 'Enterprise',
+ price: 'Custom',
+ period: '',
+ features: [
+ 'Unlimited schedules',
+ 'Custom execution limits',
+ 'SLA guarantee',
+ '90+ day log retention',
+ 'Dedicated support',
+ 'On-premise option',
+ ],
+ cta: 'Talk to us',
+ featured: false,
+ },
+ ].map((p) => (
+
+
{p.name}
+
{p.price}
+
{p.period}
+ {p.features.map((f) => (
+
+
+
+
+ {f}
+
+ ))}
+
+ {p.cta}
+
+
+ ))}
+
+
+
+
+ {/* FAQ */}
+
+
+
Frequently asked questions
+
+ {faqs.map((faq, i) => (
+
+
setOpenFaq(openFaq === i ? null : i)}>
+ {openFaq === i ? '−' : '+'}
+ {faq.q}
+
+ {openFaq === i &&
{faq.a}
}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/web/app/schedule/page.tsx b/web/app/schedule/page.tsx
new file mode 100644
index 00000000..28f8735e
--- /dev/null
+++ b/web/app/schedule/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function SchedulePage() {
+ redirect('/primitives#schedule');
+}
diff --git a/web/app/schedule/relaycron.module.css b/web/app/schedule/relaycron.module.css
new file mode 100644
index 00000000..378ecd94
--- /dev/null
+++ b/web/app/schedule/relaycron.module.css
@@ -0,0 +1,865 @@
+/* RelayCron page */
+
+.page {
+ min-height: 100vh;
+ background:
+ radial-gradient(
+ ellipse 36% 28% at 10% 8%,
+ color-mix(in srgb, var(--primary) 16%, white 22%),
+ transparent 72%
+ ),
+ radial-gradient(
+ ellipse 34% 26% at 90% 10%,
+ color-mix(in srgb, var(--primary) 14%, white 20%),
+ transparent 70%
+ ),
+ var(--landing-hero-bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+.heroSection {
+ position: relative;
+ overflow: hidden;
+ margin-top: -60px;
+ padding-top: 60px;
+}
+
+:global(html[data-theme='dark']) .page {
+ background:
+ radial-gradient(circle at 14% 0%, color-mix(in srgb, var(--primary) 18%, transparent), transparent 36%),
+ radial-gradient(circle at 78% 14%, color-mix(in srgb, var(--primary) 14%, transparent), transparent 30%),
+ radial-gradient(circle at 50% 22%, color-mix(in srgb, var(--primary) 8%, transparent), transparent 44%),
+ var(--landing-hero-bg);
+}
+
+.heroSection > * {
+ position: relative;
+ z-index: 2;
+}
+
+.heroBgSvg {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 0;
+ pointer-events: none;
+ opacity: 0.96;
+ filter: saturate(1.08) contrast(1.03);
+}
+
+:global(html[data-theme='dark']) .heroBgSvg {
+ opacity: 0.88;
+ filter: none;
+}
+
+.hero {
+ display: grid;
+ grid-template-columns: 1fr 1.2fr;
+ align-items: start;
+ gap: 32px;
+ max-width: 1280px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 28px 40px 48px;
+}
+
+.heroLeft {
+ display: flex;
+ flex-direction: column;
+ gap: 28px;
+}
+
+.heroRight {
+ position: relative;
+ min-height: 580px;
+}
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--line);
+ padding: 6px 16px;
+ border-radius: 100px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ color: var(--primary);
+ width: fit-content;
+}
+
+.badgeDot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--primary);
+ box-shadow: 0 0 6px color-mix(in srgb, var(--primary) 50%, transparent);
+ animation: pulse 2s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.45;
+ }
+}
+
+.headline {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(3.2rem, 7vw, 5.5rem);
+ font-weight: 500;
+ line-height: 1.05;
+ letter-spacing: -0.03em;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0;
+}
+
+:global(html[data-theme='dark']) .headline {
+ opacity: 1;
+}
+
+.subtitle {
+ font-size: 1.1rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ max-width: 50ch;
+ font-weight: 400;
+ margin: 0;
+}
+
+.ctas {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.ctaPrimary {
+ composes: btn btn-primary from global;
+}
+
+.ctaSecondary {
+ composes: btn btn-secondary from global;
+}
+
+/* Clock demo */
+.clockDemo {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ min-height: 580px;
+}
+
+.clockSvg {
+ width: min(100%, 480px);
+ height: auto;
+ filter: drop-shadow(0 24px 64px color-mix(in srgb, var(--primary) 18%, transparent));
+}
+
+.secondHand {
+ transform-origin: 100px 100px;
+ animation: tick 60s linear infinite;
+}
+
+@keyframes tick {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.tick1 {
+ animation: tickFly 3s ease-out infinite;
+}
+
+.tick2 {
+ animation: tickFly 3s ease-out infinite 0.8s;
+}
+
+.tick3 {
+ animation: tickFly 3s ease-out infinite 1.6s;
+}
+
+@keyframes tickFly {
+ 0% {
+ opacity: 0.7;
+ transform: scale(1);
+ }
+ 80% {
+ opacity: 0.3;
+ transform: scale(0.7);
+ }
+ 100% {
+ opacity: 0;
+ transform: scale(0.3);
+ }
+}
+
+.clockLabel {
+ margin-top: 20px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ font-weight: 600;
+ color: var(--primary);
+ opacity: 0.7;
+ letter-spacing: 0.05em;
+}
+
+/* Features */
+.featuresWrapper {
+ background: var(--bg);
+ position: relative;
+ z-index: 3;
+}
+
+.featuresHeader {
+ text-align: center;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 72px 40px 40px;
+}
+
+.featuresTitle,
+.byohTitle,
+.sdkTitle,
+.deployTitle,
+.pricingTitle,
+.faqTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.5vw, 2.6rem);
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+ color: var(--fg);
+ opacity: 0.85;
+ margin: 0 0 14px;
+}
+
+:global(html[data-theme='dark']) .featuresTitle,
+:global(html[data-theme='dark']) .byohTitle,
+:global(html[data-theme='dark']) .sdkTitle,
+:global(html[data-theme='dark']) .deployTitle,
+:global(html[data-theme='dark']) .pricingTitle,
+:global(html[data-theme='dark']) .faqTitle {
+ opacity: 1;
+}
+
+.featuresSubtitle,
+.byohSubtitle,
+.sdkSubtitle,
+.deploySubtitle,
+.pricingSubtitle {
+ font-size: 1.04rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.featuresSection {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 20px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 0 40px 88px;
+}
+
+.featureCol {
+ display: flex;
+}
+
+.featureTile {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ min-height: 100%;
+ padding: 22px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--surface-strong) 96%, transparent) 0%,
+ color-mix(in srgb, var(--surface) 98%, transparent) 100%
+ );
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 4%, transparent);
+}
+
+.featureChip {
+ width: fit-content;
+ padding: 6px 10px;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
+ color: var(--primary);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ font-weight: 700;
+}
+
+.featureTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.08rem;
+ font-weight: 600;
+ color: var(--fg);
+ margin: 0;
+}
+
+.featureDesc {
+ font-size: 0.92rem;
+ line-height: 1.62;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+/* Works with */
+.byohWrapper {
+ background: var(--showcase-bg);
+ position: relative;
+ overflow: hidden;
+}
+
+.byohWrapper::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 40% 50% at 20% 30%, rgba(255, 255, 255, 0.4) 0%, transparent 70%),
+ radial-gradient(ellipse 35% 40% at 75% 65%, rgba(255, 255, 255, 0.25) 0%, transparent 70%),
+ radial-gradient(ellipse 50% 30% at 50% 10%, rgba(255, 255, 255, 0.3) 0%, transparent 60%);
+ pointer-events: none;
+}
+
+.byohWrapper::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 400'%3E%3Cpath d='M-20,70 Q220,10 520,96 T1020,62 T1220,82' fill='none' stroke='rgba(255,255,255,0.22)' stroke-width='4'/%3E%3Cpath d='M-20,150 Q250,72 560,156 T1060,124 T1220,146' fill='none' stroke='rgba(255,255,255,0.16)' stroke-width='3'/%3E%3Cpath d='M-20,246 Q200,182 500,254 T1000,226 T1220,246' fill='none' stroke='rgba(255,255,255,0.12)' stroke-width='2.5'/%3E%3C/svg%3E");
+ background-size: cover;
+ background-position: center;
+ pointer-events: none;
+}
+
+.byohSection {
+ position: relative;
+ z-index: 1;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px 96px;
+ text-align: center;
+}
+
+.byohText {
+ margin-bottom: 42px;
+}
+
+.byohSubtitle {
+ max-width: 56ch;
+ margin: 0 auto;
+}
+
+.byohLogos {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 16px;
+}
+
+.toolBadge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 120px;
+ padding: 14px 18px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg);
+ font-size: 0.88rem;
+ font-weight: 600;
+ box-shadow: 0 4px 20px color-mix(in srgb, var(--primary) 6%, transparent);
+}
+
+/* SDK */
+.sdkWrapper,
+.deployWrapper,
+.pricingWrapper,
+.faqWrapper {
+ background: var(--section-bg);
+}
+
+.sdkSection {
+ display: grid;
+ grid-template-columns: 1fr 1.1fr;
+ align-items: start;
+ gap: 44px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 84px 40px;
+}
+
+.sdkText {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.openclawBadge {
+ display: inline-flex;
+ align-items: center;
+ width: fit-content;
+ padding: 4px 12px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ border: 1px solid color-mix(in srgb, var(--primary) 12%, transparent);
+ border-radius: 100px;
+ font-size: 0.7rem;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.sdkTitle {
+ margin: 0;
+}
+
+.sdkSubtitle {
+ margin: 0;
+}
+
+.tabRow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ padding: 16px 20px 0;
+}
+
+.tabButton {
+ padding: 10px 16px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg-muted);
+ font: inherit;
+ font-size: 0.88rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition:
+ transform 0.15s,
+ border-color 0.2s,
+ color 0.2s,
+ background 0.2s;
+}
+
+.tabButton:hover {
+ transform: translateY(-1px);
+ border-color: var(--card-hover-border);
+}
+
+.tabButtonActive {
+ background: color-mix(in srgb, var(--primary) 12%, transparent);
+ border-color: color-mix(in srgb, var(--primary) 28%, transparent);
+ color: var(--primary);
+}
+
+.sdkBullets {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.sdkBullet {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ font-size: 0.95rem;
+ line-height: 1.6;
+ color: var(--fg-muted);
+}
+
+.sdkBulletDot {
+ width: 8px;
+ height: 8px;
+ margin-top: 0.45rem;
+ flex-shrink: 0;
+ border-radius: 50%;
+ background: var(--primary);
+}
+
+.sdkCode {
+ min-width: 0;
+}
+
+.codePanel {
+ overflow: hidden;
+ border-radius: 18px;
+ border: 1px solid var(--terminal-line);
+ background: var(--terminal-bg);
+ box-shadow: 0 18px 48px color-mix(in srgb, black 18%, transparent);
+}
+
+.previewTitleBar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 14px 18px;
+ background: color-mix(in srgb, var(--surface) 78%, transparent);
+ border-bottom: 1px solid var(--line);
+}
+
+.previewDots {
+ display: flex;
+ gap: 6px;
+}
+
+.previewDots span {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+}
+
+.previewTitleText {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.72rem;
+ color: var(--fg-faint);
+}
+
+.sdkCodeBlock {
+ margin: 0;
+ padding: 22px;
+ overflow-x: auto;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.84rem;
+ line-height: 1.68;
+ color: var(--terminal-fg);
+}
+
+.codeKeyword {
+ color: var(--terminal-keyword);
+}
+.codeString {
+ color: var(--terminal-string);
+}
+.codeType {
+ color: var(--terminal-type);
+}
+.codeMethod {
+ color: var(--terminal-method);
+}
+
+/* Why RelayCron */
+.deploySection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 20px 40px 92px;
+ text-align: center;
+}
+
+.deploySubtitle {
+ margin-bottom: 40px;
+}
+
+.deployCards {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 24px;
+}
+
+.deployCard {
+ padding: 30px 26px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ text-align: left;
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s,
+ transform 0.2s;
+}
+
+.deployCard:hover {
+ border-color: var(--card-hover-border);
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--primary) 8%, transparent);
+ transform: translateY(-2px);
+}
+
+.deployIcon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 44px;
+ height: 44px;
+ padding: 0 12px;
+ border-radius: 12px;
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ color: var(--primary);
+ margin-bottom: 16px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8rem;
+ font-weight: 700;
+}
+
+.deployCardTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.2rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--fg);
+ margin: 0 0 10px;
+}
+
+.deployCardText {
+ font-size: 0.94rem;
+ line-height: 1.65;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+/* Pricing */
+.pricingSection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 20px 40px 92px;
+ text-align: center;
+}
+
+.pricingSubtitle {
+ margin-bottom: 40px;
+}
+
+.pricingCards {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 24px;
+ max-width: 960px;
+ margin: 0 auto;
+}
+
+.pricingCard {
+ padding: 32px 28px;
+ border-radius: 18px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ text-align: center;
+}
+
+.pricingCardFeatured {
+ border-color: var(--primary);
+ background: color-mix(in srgb, var(--primary) 6%, var(--card-bg));
+}
+
+.pricingName {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.3rem;
+ font-weight: 700;
+ color: var(--fg);
+ margin: 0 0 10px;
+}
+
+.pricingPrice {
+ font-size: 2.8rem;
+ font-weight: 700;
+ color: var(--fg);
+ margin: 0 0 4px;
+ letter-spacing: -0.03em;
+}
+
+.pricingPeriod {
+ font-size: 0.84rem;
+ color: var(--fg-muted);
+ margin: 0 0 24px;
+}
+
+.pricingFeature {
+ font-size: 0.9rem;
+ color: var(--fg-muted);
+ margin: 0 0 10px;
+ text-align: left;
+}
+
+.pricingCta {
+ margin-top: 24px;
+ width: 100%;
+ padding: 12px 20px;
+ border-radius: 100px;
+ font-size: 0.9rem;
+ font-weight: 600;
+ cursor: pointer;
+ border: 1.5px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg);
+ font-family: inherit;
+ transition: all 0.15s;
+}
+
+.pricingCta:hover {
+ border-color: var(--primary);
+ color: var(--primary);
+}
+
+.pricingCtaPrimary {
+ background: var(--btn-bg);
+ color: var(--primary-fg);
+ border-color: var(--btn-bg);
+}
+
+.pricingCtaPrimary:hover {
+ background: var(--btn-hover);
+}
+
+/* FAQ */
+.faqSection {
+ max-width: 760px;
+ margin: 0 auto;
+ padding: 20px 40px 96px;
+}
+
+.faqTitle {
+ text-align: center;
+ margin-bottom: 40px;
+}
+
+.faqList {
+ display: flex;
+ flex-direction: column;
+}
+
+.faqItem {
+ border-bottom: 1px solid var(--line);
+}
+
+.faqQuestion {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ width: 100%;
+ padding: 22px 0;
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--fg);
+ text-align: left;
+ line-height: 1.5;
+}
+
+.faqQuestion:hover {
+ color: var(--primary);
+}
+
+.faqQuestion span:first-child {
+ color: var(--primary);
+ font-size: 0.85rem;
+ margin-top: 2px;
+ flex-shrink: 0;
+}
+
+.faqAnswer {
+ font-size: 0.94rem;
+ line-height: 1.7;
+ color: var(--fg-muted);
+ margin: 0;
+ padding: 0 0 20px 22px;
+}
+
+/* Responsive */
+@media (max-width: 1100px) {
+ .featuresSection {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 960px) {
+ .hero {
+ grid-template-columns: 1fr;
+ padding: 52px 32px 52px;
+ gap: 24px;
+ }
+
+ .heroRight {
+ min-height: 420px;
+ }
+
+ .clockDemo {
+ min-height: 420px;
+ }
+
+ .featuresSection {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ padding: 0 24px 72px;
+ }
+
+ .featuresHeader,
+ .byohSection,
+ .sdkSection,
+ .deploySection,
+ .pricingSection,
+ .faqSection {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+
+ .sdkSection {
+ grid-template-columns: 1fr;
+ }
+
+ .deployCards,
+ .pricingCards {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 600px) {
+ .hero {
+ padding: 64px 20px 56px;
+ }
+
+ .headline {
+ font-size: clamp(2.4rem, 11vw, 4.5rem);
+ line-height: 1.08;
+ }
+
+ .subtitle {
+ font-size: 1.04rem;
+ }
+
+ .ctas {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .ctaPrimary,
+ .ctaSecondary {
+ width: 100%;
+ justify-content: center;
+ }
+
+ .heroRight,
+ .clockDemo {
+ min-height: 360px;
+ }
+
+ .featuresSection {
+ grid-template-columns: 1fr;
+ padding: 0 16px 60px;
+ }
+
+ .featuresHeader {
+ padding: 52px 20px 32px;
+ }
+}
diff --git a/web/app/sitemap.ts b/web/app/sitemap.ts
new file mode 100644
index 00000000..f4545680
--- /dev/null
+++ b/web/app/sitemap.ts
@@ -0,0 +1,128 @@
+import type { MetadataRoute } from 'next';
+
+import { allAgentSlugs } from '../lib/agents';
+import { getAllPosts } from '../lib/blog';
+import { getAllDocSlugs } from '../lib/docs-nav';
+import { absoluteUrl } from '../lib/site';
+
+// ISR so OpenNext serves it from the incremental cache on Cloudflare Workers;
+// it enumerates docs/blog from the filesystem, which only works at build time.
+export const revalidate = 86400;
+
+export default function sitemap(): MetadataRoute.Sitemap {
+ const now = new Date();
+
+ const staticRoutes: MetadataRoute.Sitemap = [
+ {
+ url: absoluteUrl('/'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 1.0,
+ },
+ {
+ url: absoluteUrl('/pear'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.9,
+ },
+ {
+ url: absoluteUrl('/skill'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.9,
+ },
+ {
+ url: absoluteUrl('/skill.md'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.9,
+ },
+ {
+ url: absoluteUrl('/openclaw'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.9,
+ },
+ {
+ url: absoluteUrl('/openclaw/skill'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.8,
+ },
+ {
+ url: absoluteUrl('/blog'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.8,
+ },
+ {
+ url: absoluteUrl('/primitives'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.9,
+ },
+ {
+ url: absoluteUrl('/agents'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.9,
+ },
+ {
+ url: absoluteUrl('/agents/use-cases'),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.8,
+ },
+ {
+ url: absoluteUrl('/brand'),
+ lastModified: now,
+ changeFrequency: 'monthly',
+ priority: 0.6,
+ },
+ {
+ url: absoluteUrl('/brand/theme'),
+ lastModified: now,
+ changeFrequency: 'monthly',
+ priority: 0.5,
+ },
+ {
+ url: absoluteUrl('/privacy'),
+ lastModified: now,
+ changeFrequency: 'yearly',
+ priority: 0.4,
+ },
+ {
+ url: absoluteUrl('/terms'),
+ lastModified: now,
+ changeFrequency: 'yearly',
+ priority: 0.4,
+ },
+ ];
+
+ const docsRoutes: MetadataRoute.Sitemap = getAllDocSlugs().map((slug) => ({
+ url: absoluteUrl(`/docs/${slug}`),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: slug === 'introduction' ? 0.9 : 0.8,
+ }));
+
+ const agentRoutes: MetadataRoute.Sitemap = allAgentSlugs().map((slug) => ({
+ url: absoluteUrl(`/agents/${slug}`),
+ lastModified: now,
+ changeFrequency: 'weekly',
+ priority: 0.7,
+ }));
+
+ const blogRoutes: MetadataRoute.Sitemap = getAllPosts().map((post) => ({
+ url: absoluteUrl(`/blog/${post.slug}`),
+ lastModified: post.frontmatter.updatedAt
+ ? new Date(post.frontmatter.updatedAt)
+ : post.frontmatter.date
+ ? new Date(post.frontmatter.date)
+ : now,
+ changeFrequency: 'monthly',
+ priority: 0.7,
+ }));
+
+ return [...staticRoutes, ...agentRoutes, ...docsRoutes, ...blogRoutes];
+}
diff --git a/web/app/skill.md/route.ts b/web/app/skill.md/route.ts
new file mode 100644
index 00000000..9b7b90fd
--- /dev/null
+++ b/web/app/skill.md/route.ts
@@ -0,0 +1,13 @@
+import { readAgentRelaySkillMarkdown } from '../../lib/skill-markdown';
+
+export const dynamic = 'force-static';
+export const revalidate = 86400;
+
+export function GET() {
+ return new Response(readAgentRelaySkillMarkdown(), {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Cache-Control': 'public, max-age=86400',
+ },
+ });
+}
diff --git a/web/app/skill/page.tsx b/web/app/skill/page.tsx
new file mode 100644
index 00000000..2d008086
--- /dev/null
+++ b/web/app/skill/page.tsx
@@ -0,0 +1,49 @@
+import type { Metadata } from 'next';
+
+import { SkillPage } from '../../components/SkillPage';
+import { defaultOgImage } from '../../lib/og-meta';
+import { absoluteUrl } from '../../lib/site';
+import { readAgentRelaySkillMarkdown } from '../../lib/skill-markdown';
+
+export const dynamic = 'force-static';
+export const revalidate = 86400;
+
+export const metadata: Metadata = {
+ title: 'Agent Relay Skill',
+ description:
+ 'Hosted Agent Relay onboarding skill for choosing orchestrator vs participant instructions and getting agents onto a shared relay.',
+ keywords: [
+ 'Agent Relay skill',
+ 'orchestrating-agent-relay',
+ 'using-agent-relay',
+ 'agent workspace setup',
+ 'multi-agent coordination',
+ ],
+ alternates: {
+ canonical: absoluteUrl('/skill'),
+ },
+ openGraph: {
+ title: 'Agent Relay Skill',
+ description:
+ 'One hosted handoff for agents: start a Relay workspace, choose the orchestrator or participant role, and coordinate with the team.',
+ url: absoluteUrl('/skill'),
+ type: 'article',
+ images: [defaultOgImage()],
+ },
+ twitter: {
+ title: 'Agent Relay Skill',
+ description: 'Hosted Agent Relay onboarding for orchestrators, human drivers, and registered workers.',
+ card: 'summary_large_image',
+ images: [defaultOgImage().url],
+ },
+};
+
+export default function AgentRelaySkillPage() {
+ return (
+
+ );
+}
diff --git a/web/app/telemetry/page.tsx b/web/app/telemetry/page.tsx
new file mode 100644
index 00000000..7fea5885
--- /dev/null
+++ b/web/app/telemetry/page.tsx
@@ -0,0 +1,239 @@
+import type { Metadata } from 'next';
+
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { absoluteUrl } from '../../lib/site';
+import s from './telemetry.module.css';
+
+export const metadata: Metadata = {
+ title: 'Telemetry — Agent Relay',
+ description: 'What anonymous usage data Agent Relay collects, why, and how to opt out.',
+ alternates: {
+ canonical: absoluteUrl('/telemetry'),
+ },
+};
+
+export default function TelemetryPage() {
+ return (
+
+
} />
+
+
+ Telemetry
+
+ Agent Relay collects anonymous usage data to help us improve the product. This page explains what we
+ collect, why, and how to opt out.
+
+
+
+ Opt out anytime
+
+ Telemetry is on by default but can be disabled instantly:
+
+
+ agent-relay telemetry disable
+
+ Or set the environment variable:
+
+ export AGENT_RELAY_TELEMETRY_DISABLED=1
+
+
+ Agent Relay also honors the{' '}
+
+ DO_NOT_TRACK
+ {' '}
+ convention for opting out across compatible tools:
+
+
+ export DO_NOT_TRACK=1
+
+ To re-enable:
+
+ agent-relay telemetry enable
+
+ Check your current status:
+
+ agent-relay telemetry
+
+
+
+
+ What we collect
+
+ Every event includes these common properties :
+
+
+
+
+
+ Property
+ Example
+ Purpose
+
+
+
+
+
+ agent_relay_version
+
+ 3.2.22
+ Know which versions are in use
+
+
+
+ os
+
+ darwin
+ Platform-specific bug triage
+
+
+
+ arch
+
+ arm64
+ Binary distribution decisions
+
+
+
+ node_version
+
+ 20.11.0
+ Runtime compatibility
+
+
+
+
+
+ These events are collected:
+
+
+
+
+
+ Event
+ When
+ Extra properties
+
+
+
+
+
+ broker_start
+
+ Broker process starts
+ None
+
+
+
+ broker_stop
+
+ Broker shuts down
+ Uptime, agent count
+
+
+
+ agent_spawn
+
+ An agent is created
+ CLI type, spawn source, has task, is shadow
+
+
+
+ agent_release
+
+ An agent is stopped
+ CLI type, reason, lifetime, source
+
+
+
+ agent_crash
+
+ An agent exits unexpectedly
+ CLI type, lifetime, exit code
+
+
+
+ message_send
+
+ A relay message is sent
+ Is broadcast, has thread
+
+
+
+ cli_command_run
+
+ A CLI command is executed
+ Command name
+
+
+
+
+
+
+
+ What we do not collect
+
+ Source code, file paths, or repository content
+ Message text, task descriptions, or prompts
+ API keys, tokens, or credentials
+ Agent names or workspace names
+ IP addresses (geo-IP is coarsened to country level by PostHog)
+ Any personally identifiable information
+
+
+
+
+ How it works
+
+
+ A random anonymous ID is generated from your machine ID on first run. It cannot
+ be traced back to you.
+
+
+ Events are sent to PostHog (our analytics provider) over HTTPS.
+
+
+ Preferences are stored locally at ~/.agentworkforce/relay/telemetry.json.
+
+
+ The telemetry module is designed to be infallible — if anything fails, it
+ silently no-ops. Telemetry never affects CLI functionality.
+
+
+
+
+
+ Why we collect telemetry
+
+ Agent Relay is a developer tool. Telemetry helps us understand which CLI providers are popular,
+ how long agent sessions last, where crashes happen, and which commands are used. This data
+ directly informs what we build and fix next.
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/telemetry/telemetry.module.css b/web/app/telemetry/telemetry.module.css
new file mode 100644
index 00000000..e48f5350
--- /dev/null
+++ b/web/app/telemetry/telemetry.module.css
@@ -0,0 +1,135 @@
+.page {
+ min-height: 100vh;
+ background: var(--bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+}
+
+.content {
+ max-width: 720px;
+ margin: 0 auto;
+ padding: 80px 24px 64px;
+}
+
+.title {
+ font-size: 2.25rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ margin-bottom: 12px;
+}
+
+.subtitle {
+ font-size: 1.125rem;
+ color: var(--fg-muted);
+ line-height: 1.6;
+ margin-bottom: 48px;
+}
+
+.section {
+ margin-bottom: 40px;
+}
+
+.section h2 {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin-bottom: 12px;
+ letter-spacing: -0.01em;
+}
+
+.section p {
+ line-height: 1.7;
+ margin-bottom: 12px;
+ color: var(--fg-muted);
+}
+
+.section p strong {
+ color: var(--fg);
+}
+
+.code {
+ background: var(--code-bg, color-mix(in srgb, var(--fg) 6%, var(--bg)));
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ padding: 12px 16px;
+ margin: 8px 0 16px;
+ overflow-x: auto;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.875rem;
+ line-height: 1.5;
+}
+
+.tableWrapper {
+ overflow-x: auto;
+ margin: 8px 0 20px;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+}
+
+.table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.875rem;
+}
+
+.table th {
+ text-align: left;
+ font-weight: 600;
+ padding: 10px 14px;
+ border-bottom: 1px solid var(--border);
+ background: var(--code-bg, color-mix(in srgb, var(--fg) 4%, var(--bg)));
+}
+
+.table td {
+ padding: 10px 14px;
+ border-bottom: 1px solid var(--border);
+ color: var(--fg-muted);
+}
+
+.table tr:last-child td {
+ border-bottom: none;
+}
+
+.table code {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.8125rem;
+ background: var(--code-bg, color-mix(in srgb, var(--fg) 6%, var(--bg)));
+ padding: 2px 6px;
+ border-radius: 4px;
+}
+
+.list {
+ list-style: none;
+ padding: 0;
+ margin: 8px 0 16px;
+}
+
+.list li {
+ position: relative;
+ padding-left: 20px;
+ line-height: 1.7;
+ color: var(--fg-muted);
+ margin-bottom: 4px;
+}
+
+.list li::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 11px;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--primary);
+ opacity: 0.6;
+}
+
+.link {
+ color: var(--primary);
+ text-decoration: underline;
+ text-underline-offset: 3px;
+}
+
+.link:hover {
+ opacity: 0.8;
+}
diff --git a/web/app/terms/page.tsx b/web/app/terms/page.tsx
new file mode 100644
index 00000000..ebfce84b
--- /dev/null
+++ b/web/app/terms/page.tsx
@@ -0,0 +1,201 @@
+import type { Metadata } from 'next';
+import Link from 'next/link';
+
+import { GitHubStarsBadge } from '../../components/GitHubStars';
+import { SiteFooter } from '../../components/SiteFooter';
+import { SiteNav } from '../../components/SiteNav';
+import { absoluteUrl, SITE_EMAIL } from '../../lib/site';
+import s from '../legal.module.css';
+
+export const metadata: Metadata = {
+ title: 'Terms of Service - Agent Relay',
+ description:
+ 'The terms that apply when using the Agent Relay website, open-source tools, hosted cloud services, and integrations.',
+ alternates: {
+ canonical: absoluteUrl('/terms'),
+ },
+};
+
+export default function TermsPage() {
+ return (
+
+
} />
+
+
+ Last updated: May 27, 2026
+ Terms of Service
+
+ These terms govern access to Agent Relay websites, open-source tools, hosted cloud services, and
+ integrations. By using Agent Relay, you agree to these terms.
+
+
+
+ Agreement
+
+ These Terms of Service are an agreement between you and Agent Workforce for use of Agent Relay. If
+ you use Agent Relay on behalf of an organization, you represent that you have authority to bind
+ that organization to these terms.
+
+
+ Agent Relay also processes information as described in our{' '}
+
+ Privacy Policy
+
+ .
+
+
+
+
+ Services
+
+ Agent Relay provides software and hosted services for coordinating agents, messages, files,
+ workflows, cloud workspaces, repository integrations, and related developer operations. Some
+ features are open source and may run locally. Other features require an Agent Relay Cloud account
+ or a paid plan.
+
+
+
+
+ Accounts and access
+
+ You are responsible for keeping account credentials and API tokens secure.
+
+ You are responsible for activity under your account, workspace, organization, or integration.
+
+ You must provide accurate account, billing, and contact information when required.
+ You may not share access in a way that bypasses plan limits or security controls.
+
+
+
+
+ Acceptable use
+ You may not use Agent Relay to:
+
+ Violate laws, regulations, third-party rights, or provider terms.
+ Access systems, repositories, services, or data without authorization.
+ Transmit malware, abuse infrastructure, disrupt services, or evade rate limits.
+ Generate, coordinate, or automate harmful, deceptive, or abusive activity.
+ Reverse engineer hosted services except where allowed by law or an open-source license.
+ Remove notices, misuse branding, or imply endorsement without permission.
+
+
+
+
+ Your content
+
+ You retain ownership of source code, prompts, messages, files, repository content, Slack messages,
+ issue text, pull request text, agent output, and other content you provide or connect. You grant
+ Agent Relay the rights needed to process that content to provide, secure, and improve the
+ services.
+
+
+ You are responsible for ensuring that you have the rights and permissions needed to submit,
+ connect, or process content through Agent Relay and its integrations.
+
+
+
+
+ Integrations
+
+ Agent Relay can connect to third-party services such as GitHub, Slack, Google services, Nango,
+ Stripe, and infrastructure providers. Your use of those services remains subject to their terms.
+ You are responsible for the permissions, scopes, repositories, channels, workspaces, and accounts
+ you authorize.
+
+
+ You may disconnect integrations at any time, but doing so may limit or disable related Agent Relay
+ features.
+
+
+
+
+ Cloud workspaces and agents
+
+ Hosted workspaces may run commands, clone repositories, call connected APIs, produce messages,
+ create branches, open pull requests, or otherwise act based on your configuration and
+ instructions. You are responsible for reviewing agent actions and configuring appropriate access,
+ policies, approvals, and repository permissions.
+
+
+
+
+ Billing
+
+ Paid services are billed according to the plan, usage, interval, and payment terms shown at
+ checkout or in the product. Subscription fees are non-refundable except where required by law or
+ expressly stated otherwise. We may suspend or limit paid features if payment fails or usage
+ exceeds applicable limits.
+
+
+
+
+ Open-source software
+
+ Agent Relay includes open-source software governed by the licenses included with that software.
+ These terms do not limit rights granted under applicable open-source licenses. Hosted services,
+ cloud infrastructure, branding, and non-public systems are not open-source software unless
+ expressly stated.
+
+
+
+
+ Service changes
+
+ We may add, remove, suspend, or change features, plans, integrations, limits, or infrastructure.
+ We may suspend access when needed to protect users, systems, providers, or the integrity of Agent
+ Relay.
+
+
+
+
+ Disclaimers
+
+ Agent Relay is provided "as is" and "as available" to the maximum extent permitted by law. We do
+ not warrant that the services will be uninterrupted, error-free, secure, or that agent-generated
+ output will be accurate, complete, or suitable for any particular purpose.
+
+
+
+
+ Limitation of liability
+
+ To the maximum extent permitted by law, Agent Workforce will not be liable for indirect,
+ incidental, special, consequential, exemplary, or punitive damages, or for lost profits, revenue,
+ data, goodwill, or business opportunities arising from your use of Agent Relay.
+
+
+
+
+ Termination
+
+ You may stop using Agent Relay at any time. We may suspend or terminate access if you violate
+ these terms, create risk for the service or other users, fail to pay required fees, or use the
+ service in a way that could cause legal or operational harm.
+
+
+
+
+ Changes to these terms
+
+ We may update these terms as Agent Relay changes. When we make material changes, we will update
+ the date above and provide additional notice when appropriate. Continued use after changes means
+ you accept the updated terms.
+
+
+
+
+ Contact
+
+ Questions about these terms can be sent to{' '}
+
+ {SITE_EMAIL}
+
+ .
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/AgentToolLogos.tsx b/web/components/AgentToolLogos.tsx
new file mode 100644
index 00000000..94802769
--- /dev/null
+++ b/web/components/AgentToolLogos.tsx
@@ -0,0 +1,113 @@
+export type AgentTool = 'claude' | 'gemini' | 'codex' | 'copilot' | 'opencode';
+
+export const AGENT_TOOLS: AgentTool[] = ['claude', 'codex', 'opencode', 'gemini', 'copilot'];
+
+export const AGENT_TOOL_LABELS: Record = {
+ claude: 'Claude',
+ gemini: 'Gemini',
+ codex: 'Codex',
+ copilot: 'Copilot',
+ opencode: 'OpenCode',
+};
+
+function ClaudeLogo({ className }: { className: string }) {
+ return (
+
+
+
+ );
+}
+
+function GeminiLogo({ className, idPrefix }: { className: string; idPrefix: string }) {
+ const gradient0 = `${idPrefix}-gf0`;
+ const gradient1 = `${idPrefix}-gf1`;
+ const gradient2 = `${idPrefix}-gf2`;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function CodexLogo({ className }: { className: string }) {
+ return (
+
+
+
+
+
+ );
+}
+
+function CopilotLogo({ className }: { className: string }) {
+ return (
+
+
+
+ );
+}
+
+function OpenCodeLogo({ className }: { className: string }) {
+ return (
+
+
+
+
+
+ );
+}
+
+export function AgentToolLogo({
+ className,
+ idPrefix = 'agent-tool-logo',
+ provider,
+}: {
+ className: string;
+ idPrefix?: string;
+ provider: AgentTool;
+}) {
+ if (provider === 'claude') return ;
+ if (provider === 'gemini') return ;
+ if (provider === 'codex') return ;
+ if (provider === 'copilot') return ;
+ return ;
+}
diff --git a/web/components/ChannelMessagesPreview.tsx b/web/components/ChannelMessagesPreview.tsx
new file mode 100644
index 00000000..8b8af636
--- /dev/null
+++ b/web/components/ChannelMessagesPreview.tsx
@@ -0,0 +1,455 @@
+'use client';
+
+import { useEffect, useRef, useState } from 'react';
+
+import s from '../app/landing.module.css';
+
+type ChannelAgent = 'Planner' | 'Builder' | 'Reviewer';
+
+interface ChannelMessagePart {
+ text: string;
+ mention?: boolean;
+}
+
+interface ChannelReaction {
+ emoji: string;
+ count: number;
+}
+
+interface ChannelMessage {
+ agent: ChannelAgent;
+ parts: ChannelMessagePart[];
+ reply?: boolean;
+ reactions?: ChannelReaction[];
+}
+
+const INITIAL_VISIBLE_MESSAGES = 4;
+const MESSAGE_DELAYS_MS = [
+ 950, 1700, 760, 2300, 1250, 2800, 840, 1900, 1120, 2600, 1450, 720, 2150, 1180, 1800, 3000, 980, 1550, 2350,
+ 860, 2050,
+];
+const FINAL_MESSAGE_HOLD_MS = 1800;
+const REPEAT_THINKING_MS = 1500;
+const FIRST_REACTION_DELAY_MS = 1450;
+const NEXT_REACTION_DELAY_MS = 760;
+const INITIAL_REACTION_STAGGER_MS = 1100;
+
+const channelMessages: ChannelMessage[] = [
+ {
+ agent: 'Planner',
+ parts: [{ text: '@builder', mention: true }, { text: ' please review my plan for Linear ticket 341.' }],
+ reactions: [
+ { emoji: '👍', count: 1 },
+ { emoji: '👀', count: 1 },
+ ],
+ },
+ {
+ agent: 'Builder',
+ parts: [{ text: "Reviewing now. I'll respond in-thread with the API risks." }],
+ reactions: [{ emoji: '👍', count: 1 }],
+ },
+ {
+ agent: 'Builder',
+ reply: true,
+ parts: [{ text: "The plan works. I'd keep webhook retries in a separate follow-up." }],
+ reactions: [{ emoji: '✅', count: 1 }],
+ },
+ {
+ agent: 'Planner',
+ reply: true,
+ parts: [
+ { text: 'Updated the plan and tagged ' },
+ { text: '@reviewer', mention: true },
+ { text: ' for final pass.' },
+ ],
+ },
+ {
+ agent: 'Reviewer',
+ parts: [{ text: '@planner', mention: true }, { text: ' reading the thread now.' }],
+ },
+ {
+ agent: 'Reviewer',
+ parts: [
+ { text: '@builder', mention: true },
+ { text: ' can you confirm the auth callback uses the shared secret?' },
+ ],
+ reactions: [{ emoji: '👀', count: 1 }],
+ },
+ {
+ agent: 'Builder',
+ parts: [{ text: 'Yep, callback validates the Relay signature before the queue write.' }],
+ },
+ {
+ agent: 'Planner',
+ parts: [{ text: "Great, I'll split the retry UI into Linear 342." }],
+ },
+ {
+ agent: 'Builder',
+ parts: [
+ { text: '@planner', mention: true },
+ { text: ' I found one blocker: Sentry payloads need nested JSON support.' },
+ ],
+ },
+ {
+ agent: 'Planner',
+ parts: [{ text: 'Add that to 341; it affects the webhook parser.' }],
+ reactions: [{ emoji: '👍', count: 2 }],
+ },
+ {
+ agent: 'Reviewer',
+ reply: true,
+ parts: [{ text: 'Thread note: require fixtures for GitHub Actions and PagerDuty.' }],
+ },
+ {
+ agent: 'Builder',
+ reply: true,
+ parts: [{ text: 'Already added the GitHub Actions fixture.' }],
+ },
+ {
+ agent: 'Planner',
+ parts: [{ text: '@reviewer', mention: true }, { text: ' do you want Sentry in the same test set?' }],
+ },
+ {
+ agent: 'Reviewer',
+ parts: [{ text: 'Yes, include Sentry and one malformed payload.' }],
+ reactions: [{ emoji: '✅', count: 1 }],
+ },
+ {
+ agent: 'Builder',
+ parts: [{ text: 'Parser now handles nested fields and bad payload errors.' }],
+ },
+ {
+ agent: 'Planner',
+ parts: [{ text: 'Shipping the updated plan to #dev.' }],
+ },
+ {
+ agent: 'Reviewer',
+ parts: [{ text: '@builder', mention: true }, { text: ' please link the runbook before marking done.' }],
+ },
+ {
+ agent: 'Builder',
+ parts: [{ text: 'Linked runbook and pasted the curl example in the thread.' }],
+ reactions: [{ emoji: '🙌', count: 1 }],
+ },
+ {
+ agent: 'Planner',
+ parts: [{ text: 'Good. ' }, { text: '@reviewer', mention: true }, { text: ' final check?' }],
+ },
+ {
+ agent: 'Reviewer',
+ parts: [{ text: 'Looks ready. I moved Linear 341 to Ready for Build.' }],
+ reactions: [
+ { emoji: '👍', count: 2 },
+ { emoji: '🚀', count: 1 },
+ ],
+ },
+ {
+ agent: 'Builder',
+ parts: [{ text: 'Claiming it now. First update in 20 minutes.' }],
+ },
+ {
+ agent: 'Planner',
+ parts: [
+ { text: 'Thanks ' },
+ { text: '@builder', mention: true },
+ { text: '. Keep the handoff token in this channel.' },
+ ],
+ },
+ {
+ agent: 'Builder',
+ parts: [{ text: 'Confirmed. Handoff token is pinned.' }],
+ },
+ {
+ agent: 'Reviewer',
+ parts: [
+ { text: '@planner', mention: true },
+ { text: ' I subscribed to the thread for regression notes.' },
+ ],
+ },
+ {
+ agent: 'Planner',
+ parts: [{ text: 'Perfect. Closing the planning loop.' }],
+ reactions: [{ emoji: '✅', count: 3 }],
+ },
+];
+
+function ChannelAgentIcon({ agent }: { agent: ChannelAgent }) {
+ if (agent === 'Planner') {
+ return (
+
+
+
+ );
+ }
+
+ if (agent === 'Builder') {
+ return (
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+}
+
+function ChannelMessageBubble({
+ message,
+ visibleReactionCount,
+}: {
+ message: ChannelMessage;
+ visibleReactionCount: number;
+}) {
+ const agentClass =
+ message.agent === 'Planner'
+ ? s.chatMsgPlanner
+ : message.agent === 'Builder'
+ ? s.chatMsgBuilder
+ : s.chatMsgReviewer;
+ const replyClass = message.reply ? s.chatReply : '';
+ const visibleReactions = message.reactions?.slice(0, visibleReactionCount) ?? [];
+
+ return (
+
+
+
+ {message.agent}
+
+
+ {message.parts.map((part, partIndex) =>
+ part.mention ? (
+
+ {part.text}
+
+ ) : (
+ part.text
+ )
+ )}
+
+ {visibleReactions.length > 0 ? (
+
+ {visibleReactions.map((reaction, reactionIndex) => (
+
+ {reaction.emoji}
+ {reaction.count}
+
+ ))}
+
+ ) : null}
+
+ );
+}
+
+export function ChannelMessagesPreview() {
+ const [visibleCount, setVisibleCount] = useState(INITIAL_VISIBLE_MESSAGES);
+ const [visibleReactionCounts, setVisibleReactionCounts] = useState>({});
+ const [isActive, setIsActive] = useState(false);
+ const [showLoopIndicator, setShowLoopIndicator] = useState(false);
+ const streamRef = useRef(null);
+ const reactionTimersRef = useRef([]);
+ const scheduledReactionIndexesRef = useRef>(new Set());
+
+ useEffect(() => {
+ const stream = streamRef.current;
+ if (!stream) return;
+
+ const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+ if (visibleCount <= INITIAL_VISIBLE_MESSAGES && !showLoopIndicator) {
+ stream.scrollTo({ top: 0, behavior: 'auto' });
+ return;
+ }
+
+ const scrollTop = stream.scrollHeight - stream.clientHeight;
+
+ stream.scrollTo({
+ top: scrollTop,
+ behavior: reduceMotion ? 'auto' : 'smooth',
+ });
+
+ if (!reduceMotion) {
+ window.setTimeout(() => {
+ stream.scrollTop = scrollTop;
+ }, 280);
+ }
+ }, [visibleCount, showLoopIndicator]);
+
+ useEffect(() => {
+ const stream = streamRef.current;
+ if (!stream) return;
+
+ let observer: IntersectionObserver | undefined;
+ let visibilityPoll: number | undefined;
+
+ const start = () => {
+ setIsActive(true);
+ if (visibilityPoll) window.clearInterval(visibilityPoll);
+ visibilityPoll = undefined;
+ window.removeEventListener('scroll', maybeStart);
+ window.removeEventListener('resize', maybeStart);
+ };
+
+ const isVisible = () => {
+ const rect = stream.getBoundingClientRect();
+ return rect.top < window.innerHeight * 0.9 && rect.bottom > window.innerHeight * 0.1;
+ };
+
+ const maybeStart = () => {
+ if (isVisible()) start();
+ };
+
+ window.addEventListener('scroll', maybeStart, { passive: true });
+ window.addEventListener('resize', maybeStart);
+ visibilityPoll = window.setInterval(maybeStart, 250);
+
+ if ('IntersectionObserver' in window) {
+ observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ start();
+ observer?.disconnect();
+ }
+ },
+ { threshold: 0.35 }
+ );
+ observer.observe(stream);
+ } else {
+ start();
+ }
+
+ maybeStart();
+
+ return () => {
+ observer?.disconnect();
+ window.removeEventListener('scroll', maybeStart);
+ window.removeEventListener('resize', maybeStart);
+ if (visibilityPoll) window.clearInterval(visibilityPoll);
+ };
+ }, []);
+
+ useEffect(() => {
+ if (!isActive || showLoopIndicator) return;
+
+ let nextTimer: number | undefined;
+
+ if (visibleCount >= channelMessages.length) {
+ nextTimer = window.setTimeout(() => {
+ setShowLoopIndicator(true);
+ }, FINAL_MESSAGE_HOLD_MS);
+ } else {
+ const delayIndex = Math.max(visibleCount - INITIAL_VISIBLE_MESSAGES, 0) % MESSAGE_DELAYS_MS.length;
+ nextTimer = window.setTimeout(() => {
+ setVisibleCount((count) => Math.min(count + 1, channelMessages.length));
+ }, MESSAGE_DELAYS_MS[delayIndex]);
+ }
+
+ return () => {
+ if (nextTimer) window.clearTimeout(nextTimer);
+ };
+ }, [isActive, showLoopIndicator, visibleCount]);
+
+ useEffect(() => {
+ if (!isActive || showLoopIndicator) return;
+
+ channelMessages.slice(0, visibleCount).forEach((message, index) => {
+ if (!message.reactions || scheduledReactionIndexesRef.current.has(index)) {
+ return;
+ }
+
+ scheduledReactionIndexesRef.current.add(index);
+ const firstDelay =
+ index < INITIAL_VISIBLE_MESSAGES
+ ? FIRST_REACTION_DELAY_MS + index * INITIAL_REACTION_STAGGER_MS
+ : FIRST_REACTION_DELAY_MS;
+
+ message.reactions.forEach((_reaction, reactionIndex) => {
+ const timer = window.setTimeout(
+ () => {
+ setVisibleReactionCounts((current) => {
+ const nextCount = reactionIndex + 1;
+ if ((current[index] ?? 0) >= nextCount) return current;
+
+ return {
+ ...current,
+ [index]: nextCount,
+ };
+ });
+ },
+ firstDelay + reactionIndex * NEXT_REACTION_DELAY_MS
+ );
+
+ reactionTimersRef.current.push(timer);
+ });
+ });
+ }, [isActive, showLoopIndicator, visibleCount]);
+
+ useEffect(() => {
+ if (!showLoopIndicator) return;
+
+ const resetTimer = window.setTimeout(() => {
+ reactionTimersRef.current.forEach((timer) => window.clearTimeout(timer));
+ reactionTimersRef.current = [];
+ scheduledReactionIndexesRef.current = new Set();
+ streamRef.current?.scrollTo({ top: 0, behavior: 'auto' });
+ setVisibleCount(INITIAL_VISIBLE_MESSAGES);
+ setVisibleReactionCounts({});
+ setShowLoopIndicator(false);
+ }, REPEAT_THINKING_MS);
+
+ return () => {
+ window.clearTimeout(resetTimer);
+ };
+ }, [showLoopIndicator]);
+
+ useEffect(() => {
+ return () => {
+ reactionTimersRef.current.forEach((timer) => window.clearTimeout(timer));
+ reactionTimersRef.current = [];
+ };
+ }, []);
+
+ return (
+ <>
+
+ #
+ proj-pipeline-fix
+ 3 agents
+
+
+ {channelMessages.slice(0, visibleCount).map((message, index) => (
+
+ ))}
+
+
+ Send a message...
+
+ >
+ );
+}
diff --git a/web/components/CopyInstructionsButton.tsx b/web/components/CopyInstructionsButton.tsx
new file mode 100644
index 00000000..d1529b61
--- /dev/null
+++ b/web/components/CopyInstructionsButton.tsx
@@ -0,0 +1,33 @@
+'use client';
+
+import { useState } from 'react';
+
+import { SITE_HOST } from '../lib/site';
+
+const SKILL_URL = `${SITE_HOST}/openclaw/skill`;
+
+function CopyIcon() {
+ return (
+
+
+
+
+ );
+}
+
+export function CopyInstructionsButton({ className }: { className?: string }) {
+ const [copied, setCopied] = useState(false);
+
+ async function handleCopy() {
+ await navigator.clipboard.writeText(SKILL_URL);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ }
+
+ return (
+
+
+ {copied ? 'Copied' : 'Copy link'}
+
+ );
+}
diff --git a/web/components/FadeIn.tsx b/web/components/FadeIn.tsx
new file mode 100644
index 00000000..0017343b
--- /dev/null
+++ b/web/components/FadeIn.tsx
@@ -0,0 +1,85 @@
+'use client';
+
+import { useEffect, useRef, useState, type ReactNode } from 'react';
+
+interface FadeInProps {
+ children: ReactNode;
+ className?: string;
+ delay?: number;
+ direction?: 'up' | 'left' | 'right';
+}
+
+export function FadeIn({ children, className, delay = 0, direction = 'up' }: FadeInProps) {
+ const ref = useRef(null);
+ // Default visible so SSR/failed hydration never hides critical content.
+ const [visible, setVisible] = useState(true);
+ const [animate, setAnimate] = useState(false);
+
+ useEffect(() => {
+ const el = ref.current;
+ if (!el) return;
+
+ const reveal = () => setVisible(true);
+
+ // Never keep content hidden if observer support or callbacks fail.
+ if (typeof window === 'undefined' || !('IntersectionObserver' in window)) {
+ reveal();
+ return;
+ }
+
+ // Only animate when JS is active; content remains visible if hydration fails.
+ setAnimate(true);
+
+ // If already near/in viewport, keep it visible immediately.
+ const rect = el.getBoundingClientRect();
+ if (rect.top <= window.innerHeight * 0.95) {
+ reveal();
+ return;
+ }
+
+ // Prepare offscreen elements for reveal-on-scroll animation.
+ setVisible(false);
+
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ reveal();
+ observer.unobserve(el);
+ }
+ },
+ { threshold: 0.01, rootMargin: '0px 0px -8% 0px' }
+ );
+
+ observer.observe(el);
+
+ // Defensive fallback for browser/extension edge cases where callbacks never fire.
+ const fallbackTimer = window.setTimeout(reveal, 1500);
+
+ return () => {
+ observer.disconnect();
+ window.clearTimeout(fallbackTimer);
+ };
+ }, []);
+
+ const translateMap = {
+ up: 'translateY(30px)',
+ left: 'translateX(40px)',
+ right: 'translateX(-40px)',
+ };
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/web/components/GitHubStars.tsx b/web/components/GitHubStars.tsx
new file mode 100644
index 00000000..4409f07c
--- /dev/null
+++ b/web/components/GitHubStars.tsx
@@ -0,0 +1,58 @@
+import s from './github-stars.module.css';
+
+type GitHubRepoResponse = {
+ stargazers_count?: number;
+};
+
+function GithubIcon() {
+ return (
+
+
+
+ );
+}
+
+function formatStarCount(stars: number): string {
+ return stars >= 1000 ? `${(stars / 1000).toFixed(1)}k` : String(stars);
+}
+
+async function getGitHubStars(): Promise {
+ try {
+ const response = await fetch('https://api.github.com/repos/agentworkforce/relay', {
+ headers: {
+ Accept: 'application/vnd.github+json',
+ 'User-Agent': 'agentrelay-web',
+ },
+ next: { revalidate: 3600 },
+ });
+
+ if (!response.ok) return null;
+
+ const data = (await response.json()) as GitHubRepoResponse;
+ return typeof data.stargazers_count === 'number' ? formatStarCount(data.stargazers_count) : null;
+ } catch {
+ return null;
+ }
+}
+
+export async function GitHubStarsBadge() {
+ const count = await getGitHubStars();
+
+ return (
+
+
+ {count ? (
+
+
+ {count}
+
+ ) : null}
+
+ );
+}
diff --git a/web/components/InstallCommand.tsx b/web/components/InstallCommand.tsx
new file mode 100644
index 00000000..261db98c
--- /dev/null
+++ b/web/components/InstallCommand.tsx
@@ -0,0 +1,153 @@
+'use client';
+
+import { type KeyboardEvent, type MouseEvent, useState } from 'react';
+import { Check, Copy, Eye, EyeOff } from 'lucide-react';
+
+import s from '../app/landing.module.css';
+
+const INSTALL_COMMAND = 'npm install @agent-relay/sdk';
+const AGENT_SETUP_PROMPT = `Add Agent Relay from https://github.com/AgentWorkforce/relay to this project.
+
+First inspect the README, package manager files, app entrypoints, worker scripts, existing agent/session/harness code, and test commands. Then propose the smallest integration that fits this project.
+
+Ask me only for choices you cannot infer:
+- Which agents or harnesses should join the workspace?
+- Which messages should be channels, direct messages, or thread replies?
+- How should inbound messages be delivered: immediate, next-message, next-tool-call, on-idle, or manual?
+- Which events should Agent Relay observe: status changes, file edits, terminal output, tool calls, or custom app events?
+- Which SDK actions should agents be able to call, and who is allowed to call them?
+- Which command should prove the integration works?
+
+Use the existing package manager to install @agent-relay/sdk. Wire the three Agent Relay surfaces:
+1. Messaging: create or join a workspace, register sessions, send one message, and listen for message events.
+2. Delivery: implement how Relay messages reach each session and how delivery is accepted, deferred, failed, or acknowledged.
+3. Actions: register at least one useful project action with a Zod input schema and a structured result.
+
+Keep changes minimal, follow existing project patterns, run the build/typecheck/tests, and summarize what works plus any remaining product decisions.`;
+const AGENT_SETUP_PROMPT_PREVIEW = `${AGENT_SETUP_PROMPT.replace(/\s+/g, ' ').slice(0, 88)}...`;
+
+async function copyText(text: string) {
+ if (navigator.clipboard?.writeText) {
+ try {
+ await navigator.clipboard.writeText(text);
+ return;
+ } catch {
+ // Fall back for embedded browsers that expose clipboard without granting write access.
+ }
+ }
+
+ const textarea = document.createElement('textarea');
+ textarea.value = text;
+ textarea.setAttribute('readonly', '');
+ textarea.style.position = 'fixed';
+ textarea.style.left = '-9999px';
+ document.body.appendChild(textarea);
+ textarea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textarea);
+}
+
+export function InstallCommand() {
+ const [copied, setCopied] = useState(false);
+
+ async function handleCopy() {
+ await copyText(INSTALL_COMMAND);
+ setCopied(true);
+ window.setTimeout(() => setCopied(false), 1800);
+ }
+
+ return (
+
+ $
+ {INSTALL_COMMAND}
+
+ {copied ? (
+ <>
+
+ Copied
+ >
+ ) : (
+ <>
+
+ Copy
+ >
+ )}
+
+
+ );
+}
+
+export function AgentSetupPrompt() {
+ const [copied, setCopied] = useState(false);
+ const [showPrompt, setShowPrompt] = useState(false);
+
+ async function handleCopy() {
+ await copyText(AGENT_SETUP_PROMPT);
+ setCopied(true);
+ window.setTimeout(() => setCopied(false), 1800);
+ }
+
+ function handlePromptKeyDown(event: KeyboardEvent) {
+ if (event.key !== 'Enter' && event.key !== ' ') return;
+ event.preventDefault();
+ void handleCopy();
+ }
+
+ function handleShowPrompt(event: MouseEvent) {
+ event.stopPropagation();
+ setShowPrompt((value) => !value);
+ }
+
+ return (
+
+
+ {AGENT_SETUP_PROMPT_PREVIEW}
+
+
+ {showPrompt ? (
+
event.stopPropagation()}
+ >
+ Prompt
+ {AGENT_SETUP_PROMPT}
+
+ ) : null}
+
+
+
+ {showPrompt ? : }
+ {showPrompt ? 'Hide prompt' : 'Show prompt'}
+
+
+ {copied ? (
+ <>
+
+ Copied
+ >
+ ) : (
+ <>
+
+ Copy
+ >
+ )}
+
+
+
+ );
+}
diff --git a/web/components/MessageRelayAnimation.tsx b/web/components/MessageRelayAnimation.tsx
new file mode 100644
index 00000000..e96cf074
--- /dev/null
+++ b/web/components/MessageRelayAnimation.tsx
@@ -0,0 +1,838 @@
+'use client';
+
+import { useCallback, useEffect, useRef, useState, type JSX } from 'react';
+
+import s from './node-relay.module.css';
+
+type ModelProvider = 'claude' | 'gemini' | 'codex' | 'copilot' | 'opencode';
+type MessageKind = 'channel' | 'dm' | 'thread' | 'reaction';
+
+interface AgentNode {
+ id: string;
+ name: string;
+ provider: ModelProvider;
+ model: string;
+ state: 'IDLE' | 'AWAITING TASK' | 'PROCESSING' | 'RELAYING' | 'COMPLETE';
+ statusText: string;
+ glowing: boolean;
+ glowOpacity: number;
+ baseX: number;
+ baseY: number;
+ x: number;
+ y: number;
+ driftPhase: number;
+ driftSpeed: number;
+ driftAmplitudeX: number;
+ driftAmplitudeY: number;
+ active: boolean;
+ opacity: number;
+}
+
+interface Message {
+ from: number;
+ to: number;
+ t: number;
+ speed: number;
+ trail: { x: number; y: number; age: number }[];
+ kind: MessageKind;
+ branches?: number[];
+ isSpawn?: boolean;
+}
+
+interface LandingToast {
+ x: number;
+ y: number;
+ text: string;
+ kind: MessageKind;
+ age: number;
+ maxAge: number;
+ alpha: number;
+}
+
+const IDLE_TEXTS: Record = {
+ claude: ['Waiting for task...', 'Ready'],
+ gemini: ['Standing by...', 'Idle'],
+ codex: ['Waiting...', 'Idle'],
+ copilot: ['Awaiting instructions...', 'Ready'],
+ opencode: ['Standing by...', 'Idle'],
+};
+
+const WORKING_TEXTS: Record = {
+ claude: ['* Thinking...', '* Analyzing codebase...', '* Reading files...', '* Writing code...'],
+ gemini: ['Processing query...', 'Searching context...', 'Generating response...'],
+ codex: ['Compiling...', 'Running sandbox...', 'Writing patch...'],
+ copilot: ['Generating suggestion...', 'Completing code...', 'Reasoning...'],
+ opencode: ['$ running task...', '$ reading files...', '$ writing patch...'],
+};
+
+const STATUS_BY_KIND: Record = {
+ channel: ['Posting to #general...', 'Sending to #dev...', 'Posting to #alerts...'],
+ dm: ['DM → Reviewer...', 'DM → Lead...', 'DM → Coder...'],
+ thread: ['Replying in thread...', 'Thread reply...', 'Following up...'],
+ reaction: ['Reacting 👍...', 'Reacting ✅...', 'Reacting 🚀...'],
+};
+
+const TRAIL_COLORS: Record = {
+ channel: 'rgba(74, 144, 194,',
+ dm: 'rgba(99, 209, 139,',
+ thread: 'rgba(193, 103, 75,',
+ reaction: 'rgba(254, 188, 46,',
+};
+
+const LANDING_BY_KIND: Record = {
+ channel: ['#general: Starting task...', '#dev: PR ready', '#alerts: CPU spike', '#dev: Deployed'],
+ dm: ['DM: Check the logs?', 'DM: Incident resolved', 'DM: Can you review?', 'DM: Tests green'],
+ thread: ['Thread: re: deploy plan', 'Thread: re: auth fix', 'Thread: re: perf spike'],
+ reaction: ['👍 reacted', '✅ reacted', '🚀 reacted', '👀 reacted', '❤️ reacted', '🎉 reacted'],
+};
+
+const MSG_KINDS: MessageKind[] = ['channel', 'dm', 'thread', 'reaction'];
+
+const NODE_POOL: { name: string; provider: ModelProvider; model: string }[] = [
+ { name: 'Lead', provider: 'claude', model: 'Opus' },
+ { name: 'Planner', provider: 'gemini', model: '2.5 Pro' },
+ { name: 'Coder', provider: 'codex', model: 'Codex-1' },
+ { name: 'Reviewer', provider: 'claude', model: 'Sonnet' },
+ { name: 'Frontend', provider: 'copilot', model: 'GPT-4.1' },
+ { name: 'Backend', provider: 'opencode', model: 'Gemini' },
+ { name: 'Marketer', provider: 'gemini', model: '2.5 Flash' },
+ { name: 'Tester', provider: 'claude', model: 'Haiku' },
+];
+
+type ScriptEvent =
+ | { tick: number; type: 'spawn'; from: number; to: number }
+ | { tick: number; type: 'message'; from: number; to: number };
+
+const SCRIPT: ScriptEvent[] = [
+ { tick: 3, type: 'spawn', from: 0, to: 1 },
+ { tick: 6, type: 'spawn', from: 0, to: 2 },
+ { tick: 10, type: 'spawn', from: 0, to: 3 },
+ { tick: 14, type: 'message', from: 1, to: 2 },
+ { tick: 17, type: 'message', from: 2, to: 1 },
+ { tick: 21, type: 'spawn', from: 2, to: 4 },
+ { tick: 25, type: 'spawn', from: 2, to: 5 },
+ { tick: 29, type: 'spawn', from: 0, to: 6 },
+ { tick: 33, type: 'message', from: 4, to: 3 },
+ { tick: 35, type: 'message', from: 5, to: 3 },
+ { tick: 38, type: 'message', from: 3, to: 0 },
+ { tick: 42, type: 'spawn', from: 3, to: 7 },
+ { tick: 46, type: 'message', from: 0, to: 6 },
+ { tick: 49, type: 'message', from: 7, to: 2 },
+];
+
+const MAX_NODES = 8;
+const CARD_W = 160;
+const CARD_H = 88;
+
+const NODE_POSITIONS = (() => {
+ const cx = 0.38;
+ const cy = 0.38;
+ const positions: { x: number; y: number }[] = [{ x: cx, y: cy }];
+ const rings = [{ count: 7, radius: 0.32 }];
+
+ for (const ring of rings) {
+ for (let i = 0; i < ring.count; i++) {
+ const angle = (i / ring.count) * Math.PI * 2 - Math.PI * 0.1;
+ const jx = Math.sin(positions.length * 7.3) * 0.01;
+ const jy = Math.cos(positions.length * 5.1) * 0.008;
+ positions.push({
+ x: cx + Math.cos(angle) * ring.radius + jx,
+ y: cy + Math.sin(angle) * ring.radius * 0.85 + jy,
+ });
+ }
+ }
+
+ return positions;
+})();
+
+function buildConnections(count: number): [number, number][] {
+ const conns: [number, number][] = [];
+ const threshold = 0.38;
+ for (let i = 0; i < count; i++) {
+ for (let j = i + 1; j < count; j++) {
+ const dx = NODE_POSITIONS[i].x - NODE_POSITIONS[j].x;
+ const dy = NODE_POSITIONS[i].y - NODE_POSITIONS[j].y;
+ if (Math.sqrt(dx * dx + dy * dy) < threshold) {
+ conns.push([i, j]);
+ }
+ }
+ }
+ return conns;
+}
+
+function lerp(a: number, b: number, t: number) {
+ return a + (b - a) * t;
+}
+
+function easeInOut(t: number) {
+ return t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
+}
+
+function pick(arr: T[]): T {
+ return arr[Math.floor(Math.random() * arr.length)];
+}
+
+function pickKind(): MessageKind {
+ return MSG_KINDS[Math.floor(Math.random() * MSG_KINDS.length)];
+}
+
+function isReadyNode(node: AgentNode) {
+ return node.active && node.opacity >= 0.9;
+}
+
+function enqueueMessage(messages: Message[], message: Omit) {
+ messages.push({
+ ...message,
+ t: 0,
+ trail: [],
+ });
+}
+
+function setNodeRelaying(node: AgentNode, statusText: string) {
+ node.state = 'RELAYING';
+ node.statusText = statusText;
+ node.glowing = true;
+}
+
+function handleScriptEvents(tick: number, nodes: AgentNode[], messages: Message[]) {
+ for (const event of SCRIPT) {
+ if (event.tick !== tick) {
+ continue;
+ }
+
+ if (event.type === 'spawn') {
+ setNodeRelaying(nodes[event.from], `Spawning ${nodes[event.to].name}...`);
+ enqueueMessage(messages, {
+ from: event.from,
+ to: event.to,
+ speed: 0.008 + Math.random() * 0.004,
+ isSpawn: true,
+ kind: 'channel',
+ });
+ continue;
+ }
+
+ const sender = nodes[event.from];
+ if (!isReadyNode(sender)) {
+ continue;
+ }
+
+ const kind = pickKind();
+ setNodeRelaying(sender, pick(STATUS_BY_KIND[kind]));
+ enqueueMessage(messages, {
+ from: event.from,
+ to: event.to,
+ speed: 0.007 + Math.random() * 0.005,
+ kind,
+ });
+ }
+}
+
+function findReadyIndices(nodes: AgentNode[]) {
+ return nodes.map((node, index) => (isReadyNode(node) ? index : -1)).filter((index) => index >= 0);
+}
+
+function getActiveNeighbors(senderIdx: number, connections: [number, number][], nodes: AgentNode[]) {
+ return connections
+ .filter(([a, b]) => a === senderIdx || b === senderIdx)
+ .map(([a, b]) => (a === senderIdx ? b : a))
+ .filter((index) => isReadyNode(nodes[index]) && index !== senderIdx);
+}
+
+function pickRelayTargets(activeNeighbors: number[]) {
+ if (activeNeighbors.length >= 2 && Math.random() < 0.3) {
+ return [...activeNeighbors].sort(() => Math.random() - 0.5).slice(0, 2);
+ }
+
+ return [pick(activeNeighbors)];
+}
+
+function maybeQueueRandomRelay(
+ tick: number,
+ nodes: AgentNode[],
+ messages: Message[],
+ connections: [number, number][]
+) {
+ const readyIndices = findReadyIndices(nodes);
+ const scriptDone = tick > (SCRIPT.length > 0 ? SCRIPT[SCRIPT.length - 1].tick + 5 : 0);
+ if (!scriptDone || tick % 3 !== 0 || readyIndices.length < 2) {
+ return;
+ }
+
+ const senderIdx = pick(readyIndices);
+ const activeNeighbors = getActiveNeighbors(senderIdx, connections, nodes);
+ if (activeNeighbors.length === 0) {
+ return;
+ }
+
+ const kind = pickKind();
+ const sender = nodes[senderIdx];
+ setNodeRelaying(sender, pick(STATUS_BY_KIND[kind]));
+
+ for (const target of pickRelayTargets(activeNeighbors)) {
+ enqueueMessage(messages, {
+ from: senderIdx,
+ to: target,
+ speed: 0.007 + Math.random() * 0.005,
+ branches: Math.random() < 0.25 && readyIndices.length > 1 ? [pick(readyIndices)] : undefined,
+ kind,
+ });
+ }
+}
+
+function cycleNodeStates(nodes: AgentNode[], tick: number) {
+ for (const node of nodes) {
+ if (node.state === 'COMPLETE') {
+ node.state = 'AWAITING TASK';
+ node.statusText = pick(IDLE_TEXTS[node.provider]);
+ }
+
+ if (node.state === 'RELAYING' && tick % 3 === 0) {
+ node.state = 'COMPLETE';
+ node.statusText = 'Done';
+ }
+
+ if (node.state === 'PROCESSING' && Math.random() < 0.15) {
+ node.statusText = pick(WORKING_TEXTS[node.provider]);
+ }
+ }
+}
+
+function updateNodePositions(nodes: AgentNode[], now: number) {
+ for (const node of nodes) {
+ const t = now * node.driftSpeed + node.driftPhase;
+ node.x = node.baseX + Math.sin(t) * node.driftAmplitudeX + Math.cos(t * 0.7) * node.driftAmplitudeX * 0.5;
+ node.y =
+ node.baseY + Math.cos(t * 1.3) * node.driftAmplitudeY + Math.sin(t * 0.5) * node.driftAmplitudeY * 0.4;
+
+ node.opacity = node.active ? Math.min(node.opacity + 0.08, 1) : Math.max(node.opacity - 0.03, 0);
+ node.glowOpacity = node.glowing
+ ? Math.min(node.glowOpacity + 0.06, 1)
+ : Math.max(node.glowOpacity - 0.02, 0);
+
+ if (node.glowOpacity > 0.85) {
+ node.glowing = false;
+ }
+ }
+}
+
+type NodeCenter = { cx: number; cy: number; opacity: number };
+
+function buildCenters(nodes: AgentNode[], w: number, h: number): NodeCenter[] {
+ return nodes.map((node) => ({
+ cx: node.x * w + CARD_W / 2,
+ cy: node.y * h + CARD_H / 2,
+ opacity: node.opacity,
+ }));
+}
+
+function drawConnectionLines(
+ ctx: CanvasRenderingContext2D,
+ centers: NodeCenter[],
+ connections: [number, number][]
+) {
+ for (const [i, j] of connections) {
+ const ci = centers[i];
+ const cj = centers[j];
+ if (!ci || !cj) {
+ continue;
+ }
+
+ const lineOpacity = Math.min(ci.opacity, cj.opacity) * 0.1;
+ if (lineOpacity < 0.01) {
+ continue;
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(ci.cx, ci.cy);
+ ctx.lineTo(cj.cx, cj.cy);
+ ctx.strokeStyle = `rgba(45, 79, 62, ${lineOpacity})`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+}
+
+function drawMessageTrail(ctx: CanvasRenderingContext2D, message: Message) {
+ const color = TRAIL_COLORS[message.kind];
+ for (let trailIndex = message.trail.length - 1; trailIndex >= 0; trailIndex--) {
+ const point = message.trail[trailIndex];
+ point.age++;
+ const trailAlpha = Math.max(0, 0.3 - point.age * 0.003);
+
+ if (trailAlpha <= 0) {
+ message.trail.splice(trailIndex, 1);
+ continue;
+ }
+
+ const trailSize = Math.max(1, 4 - point.age * 0.03);
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, trailSize, 0, Math.PI * 2);
+ ctx.fillStyle = `${color} ${trailAlpha})`;
+ ctx.fill();
+ }
+}
+
+function drawMessagePulse(ctx: CanvasRenderingContext2D, x: number, y: number, kind: MessageKind) {
+ const color = TRAIL_COLORS[kind];
+ const grad = ctx.createRadialGradient(x, y, 0, x, y, 17);
+ grad.addColorStop(0, `${color} 0.58)`);
+ grad.addColorStop(0.45, `${color} 0.2)`);
+ grad.addColorStop(1, `${color} 0)`);
+
+ ctx.beginPath();
+ ctx.arc(x, y, 17, 0, Math.PI * 2);
+ ctx.fillStyle = grad;
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(x, y, 5, 0, Math.PI * 2);
+ ctx.fillStyle = `${color} 0.88)`;
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(x, y, 8, 0, Math.PI * 2);
+ ctx.strokeStyle = `${color} 0.35)`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+}
+
+function isDarkMode() {
+ return document.documentElement.getAttribute('data-theme') === 'dark';
+}
+
+function drawLandingToasts(ctx: CanvasRenderingContext2D, toasts: LandingToast[]) {
+ const dark = isDarkMode();
+ const toastColors: Record = dark
+ ? {
+ channel: { border: 'rgba(74, 144, 194, 0.35)', text: 'rgba(116, 184, 226, 0.95)' },
+ dm: { border: 'rgba(99, 209, 139, 0.35)', text: 'rgba(99, 209, 139, 0.95)' },
+ thread: { border: 'rgba(193, 103, 75, 0.35)', text: 'rgba(193, 103, 75, 0.95)' },
+ reaction: { border: 'rgba(254, 188, 46, 0.35)', text: 'rgba(254, 188, 46, 0.95)' },
+ }
+ : {
+ channel: { border: 'rgba(74, 144, 194, 0.3)', text: 'rgba(40, 100, 150, 0.95)' },
+ dm: { border: 'rgba(60, 170, 100, 0.3)', text: 'rgba(30, 120, 70, 0.95)' },
+ thread: { border: 'rgba(193, 103, 75, 0.3)', text: 'rgba(150, 70, 45, 0.95)' },
+ reaction: { border: 'rgba(220, 160, 30, 0.3)', text: 'rgba(160, 110, 10, 0.95)' },
+ };
+
+ for (let i = toasts.length - 1; i >= 0; i--) {
+ const toast = toasts[i];
+ toast.age++;
+ toast.y -= 0.3;
+
+ if (toast.age < 12) {
+ toast.alpha = toast.age / 12;
+ } else if (toast.age > toast.maxAge - 18) {
+ toast.alpha = (toast.maxAge - toast.age) / 18;
+ } else {
+ toast.alpha = 1;
+ }
+
+ if (toast.age >= toast.maxAge) {
+ toasts.splice(i, 1);
+ continue;
+ }
+
+ if (toast.alpha <= 0.01) {
+ continue;
+ }
+
+ ctx.save();
+ ctx.globalAlpha = toast.alpha * 0.88;
+ ctx.font = '600 11px "JetBrains Mono", monospace';
+
+ const textWidth = ctx.measureText(toast.text).width;
+ const pillW = textWidth + 20;
+ const pillH = 24;
+ const pillX = toast.x - pillW / 2;
+ const pillY = toast.y - pillH / 2;
+ const pillR = pillH / 2;
+ const colors = toastColors[toast.kind];
+
+ ctx.beginPath();
+ ctx.moveTo(pillX + pillR, pillY);
+ ctx.lineTo(pillX + pillW - pillR, pillY);
+ ctx.arcTo(pillX + pillW, pillY, pillX + pillW, pillY + pillR, pillR);
+ ctx.arcTo(pillX + pillW, pillY + pillH, pillX + pillW - pillR, pillY + pillH, pillR);
+ ctx.lineTo(pillX + pillR, pillY + pillH);
+ ctx.arcTo(pillX, pillY + pillH, pillX, pillY + pillR, pillR);
+ ctx.arcTo(pillX, pillY, pillX + pillR, pillY, pillR);
+ ctx.closePath();
+ ctx.fillStyle = dark ? 'rgba(15, 27, 41, 0.9)' : 'rgba(255, 255, 255, 0.92)';
+ ctx.fill();
+ ctx.strokeStyle = colors.border;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+
+ ctx.fillStyle = colors.text;
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.fillText(toast.text, toast.x, toast.y);
+ ctx.textAlign = 'left';
+ ctx.textBaseline = 'alphabetic';
+ ctx.restore();
+ }
+}
+
+function handleMessageArrival(
+ message: Message,
+ nodes: AgentNode[],
+ messages: Message[],
+ centers: NodeCenter[],
+ toasts: LandingToast[]
+) {
+ const receiver = nodes[message.to];
+ const center = centers[message.to];
+ if (!receiver || !center) {
+ return;
+ }
+
+ if (receiver.active) {
+ toasts.push({
+ x: center.cx,
+ y: center.cy - 30,
+ text: pick(LANDING_BY_KIND[message.kind]),
+ kind: message.kind,
+ age: 0,
+ maxAge: 100,
+ alpha: 0,
+ });
+ }
+
+ if (message.isSpawn && !receiver.active) {
+ receiver.active = true;
+ receiver.state = 'AWAITING TASK';
+ receiver.statusText = pick(IDLE_TEXTS[receiver.provider]);
+ receiver.glowing = true;
+ } else if (receiver.active) {
+ receiver.state = 'PROCESSING';
+ receiver.statusText = pick(WORKING_TEXTS[receiver.provider]);
+ receiver.glowing = true;
+ }
+
+ if (!message.branches || !receiver.active) {
+ return;
+ }
+
+ const branchTargets = message.branches.filter(
+ (branchTarget) =>
+ branchTarget !== message.to && branchTarget !== message.from && nodes[branchTarget]?.active
+ );
+
+ if (branchTargets.length === 0) {
+ return;
+ }
+
+ for (const branchTarget of branchTargets) {
+ enqueueMessage(messages, {
+ from: message.to,
+ to: branchTarget,
+ speed: 0.007 + Math.random() * 0.005,
+ kind: pickKind(),
+ });
+ }
+
+ receiver.state = 'RELAYING';
+ receiver.statusText = pick(STATUS_BY_KIND[pickKind()]);
+}
+
+function drawMessages(
+ ctx: CanvasRenderingContext2D,
+ nodes: AgentNode[],
+ messages: Message[],
+ centers: NodeCenter[],
+ toasts: LandingToast[]
+) {
+ for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex--) {
+ const message = messages[messageIndex];
+ message.t += message.speed;
+
+ const from = centers[message.from];
+ const to = centers[message.to];
+ if (!from || !to) {
+ messages.splice(messageIndex, 1);
+ continue;
+ }
+
+ const progress = easeInOut(Math.min(message.t, 1));
+ const px = lerp(from.cx, to.cx, progress);
+ const py = lerp(from.cy, to.cy, progress);
+
+ message.trail.push({ x: px, y: py, age: 0 });
+ drawMessageTrail(ctx, message);
+
+ if (message.t <= 1) {
+ drawMessagePulse(ctx, px, py, message.kind);
+ continue;
+ }
+
+ handleMessageArrival(message, nodes, messages, centers, toasts);
+ messages.splice(messageIndex, 1);
+ }
+}
+
+function drawGlowRings(ctx: CanvasRenderingContext2D, nodes: AgentNode[], centers: NodeCenter[]) {
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+ if (node.glowOpacity <= 0.01 || node.opacity <= 0.1) {
+ continue;
+ }
+
+ const center = centers[i];
+ const radius = 56;
+ const alpha = node.glowOpacity * node.opacity * 0.07;
+ const grad = ctx.createRadialGradient(center.cx, center.cy, radius * 0.6, center.cx, center.cy, radius);
+ grad.addColorStop(0, `rgba(45, 79, 62, ${alpha})`);
+ grad.addColorStop(1, 'rgba(45, 79, 62, 0)');
+ ctx.beginPath();
+ ctx.arc(center.cx, center.cy, radius, 0, Math.PI * 2);
+ ctx.fillStyle = grad;
+ ctx.fill();
+ }
+}
+
+function ClaudeLogo() {
+ return (
+
+
+
+ );
+}
+
+function CodexLogo() {
+ return (
+
+
+
+
+
+ );
+}
+
+function CopilotLogo() {
+ return (
+
+
+
+ );
+}
+
+function OpenCodeLogo() {
+ return (
+
+
+
+
+
+ );
+}
+
+function GeminiLogo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const LOGO_MAP: Record JSX.Element> = {
+ claude: ClaudeLogo,
+ codex: CodexLogo,
+ copilot: CopilotLogo,
+ opencode: OpenCodeLogo,
+ gemini: GeminiLogo,
+};
+
+export function MessageRelayAnimation() {
+ const containerRef = useRef(null);
+ const canvasRef = useRef(null);
+ const nodesRef = useRef([]);
+ const messagesRef = useRef([]);
+ const toastsRef = useRef([]);
+ const connectionsRef = useRef<[number, number][]>([]);
+ const rafRef = useRef(0);
+ const tickRef = useRef(0);
+ const timeRef = useRef(0);
+ const [renderNodes, setRenderNodes] = useState([]);
+
+ const initNodes = useCallback(() => {
+ const created: AgentNode[] = NODE_POOL.map((def, i) => ({
+ id: `node-${i}`,
+ name: def.name,
+ provider: def.provider,
+ model: def.model,
+ state: i === 0 ? 'PROCESSING' : 'IDLE',
+ statusText: i === 0 ? pick(WORKING_TEXTS[def.provider]) : '',
+ glowing: i === 0,
+ glowOpacity: 0,
+ baseX: NODE_POSITIONS[i].x,
+ baseY: NODE_POSITIONS[i].y,
+ x: NODE_POSITIONS[i].x,
+ y: NODE_POSITIONS[i].y,
+ driftPhase: Math.random() * Math.PI * 2,
+ driftSpeed: 0.0003 + Math.random() * 0.0004,
+ driftAmplitudeX: 0.008 + Math.random() * 0.01,
+ driftAmplitudeY: 0.006 + Math.random() * 0.008,
+ active: i === 0,
+ opacity: i === 0 ? 1 : 0,
+ }));
+
+ nodesRef.current = created;
+ connectionsRef.current = buildConnections(MAX_NODES);
+ messagesRef.current = [];
+ toastsRef.current = [];
+ setRenderNodes([...created]);
+ }, []);
+
+ useEffect(() => {
+ initNodes();
+ }, [initNodes]);
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ const tick = tickRef.current++;
+ const nodes = nodesRef.current;
+ const messages = messagesRef.current;
+
+ handleScriptEvents(tick, nodes, messages);
+ maybeQueueRandomRelay(tick, nodes, messages, connectionsRef.current);
+ cycleNodeStates(nodes, tick);
+
+ setRenderNodes([...nodes]);
+ }, 250);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ const container = containerRef.current;
+ if (!canvas || !container) {
+ return;
+ }
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) {
+ return;
+ }
+
+ const resize = () => {
+ const rect = container.getBoundingClientRect();
+ const dpr = window.devicePixelRatio || 1;
+ canvas.width = rect.width * dpr;
+ canvas.height = rect.height * dpr;
+ canvas.style.width = `${rect.width}px`;
+ canvas.style.height = `${rect.height}px`;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ };
+
+ resize();
+ window.addEventListener('resize', resize);
+
+ const draw = () => {
+ const rect = container.getBoundingClientRect();
+ const w = rect.width;
+ const h = rect.height;
+ const nodes = nodesRef.current;
+ const messages = messagesRef.current;
+ const toasts = toastsRef.current;
+ const now = timeRef.current++;
+
+ ctx.clearRect(0, 0, w, h);
+ updateNodePositions(nodes, now);
+ const centers = buildCenters(nodes, w, h);
+ drawConnectionLines(ctx, centers, connectionsRef.current);
+ drawMessages(ctx, nodes, messages, centers, toasts);
+ drawGlowRings(ctx, nodes, centers);
+ drawLandingToasts(ctx, toasts);
+
+ rafRef.current = requestAnimationFrame(draw);
+ };
+
+ rafRef.current = requestAnimationFrame(draw);
+
+ return () => {
+ window.removeEventListener('resize', resize);
+ cancelAnimationFrame(rafRef.current);
+ };
+ }, []);
+
+ return (
+
+
+ {renderNodes.map((node) => {
+ const Logo = LOGO_MAP[node.provider];
+ const isBusy = node.state === 'PROCESSING' || node.state === 'RELAYING';
+
+ return (
+
0
+ ? `0 0 ${18 * node.glowOpacity}px rgba(45, 79, 62, ${0.15 * node.glowOpacity}), 0 2px 10px rgba(0,0,0,0.08)`
+ : '0 2px 8px rgba(0,0,0,0.06)',
+ pointerEvents: node.opacity < 0.1 ? 'none' : undefined,
+ }}
+ >
+
+
+
+ {node.name}
+
+
{node.model}
+
+
+ {isBusy && }
+ {node.statusText}
+
+
+ );
+ })}
+
+ );
+}
diff --git a/web/components/NodeRelayAnimation.tsx b/web/components/NodeRelayAnimation.tsx
new file mode 100644
index 00000000..866fc939
--- /dev/null
+++ b/web/components/NodeRelayAnimation.tsx
@@ -0,0 +1,706 @@
+'use client';
+
+import { useEffect, useRef, useState, useCallback, type JSX } from 'react';
+
+import s from './node-relay.module.css';
+
+type ModelProvider = 'claude' | 'gemini' | 'codex' | 'copilot' | 'opencode';
+
+interface AgentNode {
+ id: string;
+ name: string;
+ provider: ModelProvider;
+ model: string;
+ state: 'IDLE' | 'AWAITING TASK' | 'PROCESSING' | 'RELAYING' | 'COMPLETE';
+ statusText: string;
+ glowing: boolean;
+ glowOpacity: number;
+ baseX: number;
+ baseY: number;
+ x: number;
+ y: number;
+ driftPhase: number;
+ driftSpeed: number;
+ driftAmplitudeX: number;
+ driftAmplitudeY: number;
+ active: boolean;
+ opacity: number;
+}
+
+interface Message {
+ from: number;
+ to: number;
+ t: number;
+ speed: number;
+ trail: { x: number; y: number; age: number }[];
+ branches?: number[];
+ /** If true, this message spawns (activates) the target node on arrival */
+ isSpawn?: boolean;
+}
+
+const IDLE_TEXTS: Record = {
+ claude: ['Waiting for task...', 'Ready'],
+ gemini: ['Standing by...', 'Idle'],
+ codex: ['Waiting...', 'Idle'],
+ copilot: ['Awaiting instructions...', 'Ready'],
+ opencode: ['Standing by...', 'Idle'],
+};
+
+const WORKING_TEXTS: Record = {
+ claude: ['* Thinking...', '* Analyzing codebase...', '* Reading files...', '* Writing code...'],
+ gemini: ['Processing query...', 'Searching context...', 'Generating response...'],
+ codex: ['Compiling...', 'Running sandbox...', 'Writing patch...'],
+ copilot: ['Generating suggestion...', 'Completing code...', 'Reasoning...'],
+ opencode: ['$ running task...', '$ reading files...', '$ writing patch...'],
+};
+
+const RELAYING_TEXTS: Record = {
+ claude: ['* Sending to relay...', '* Handing off...'],
+ gemini: ['Forwarding result...', 'Relaying context...'],
+ codex: ['Pushing changes...', 'Syncing...'],
+ copilot: ['Passing context...', 'Delegating...'],
+ opencode: ['$ relaying output...', '$ syncing...'],
+};
+
+const NODE_POOL: { name: string; provider: ModelProvider; model: string }[] = [
+ { name: 'Lead', provider: 'claude', model: 'Opus' }, // 0
+ { name: 'Planner', provider: 'gemini', model: '2.5 Pro' }, // 1
+ { name: 'Coder', provider: 'codex', model: 'Codex-1' }, // 2
+ { name: 'Reviewer', provider: 'claude', model: 'Sonnet' }, // 3
+ { name: 'Frontend', provider: 'copilot', model: 'GPT-4.1' }, // 4
+ { name: 'Backend', provider: 'opencode', model: 'Gemini' }, // 5
+ { name: 'Marketer', provider: 'gemini', model: '2.5 Flash' }, // 6
+ { name: 'Tester', provider: 'claude', model: 'Haiku' }, // 7
+];
+
+// Scripted spawn/message sequence: { tick, type, from, to }
+// tick = which state-machine tick to fire on
+type ScriptEvent =
+ | { tick: number; type: 'spawn'; from: number; to: number }
+ | { tick: number; type: 'message'; from: number; to: number };
+
+const SCRIPT: ScriptEvent[] = [
+ // Lead spawns Planner and Coder
+ { tick: 3, type: 'spawn', from: 0, to: 1 },
+ { tick: 6, type: 'spawn', from: 0, to: 2 },
+ // Lead spawns Reviewer
+ { tick: 10, type: 'spawn', from: 0, to: 3 },
+ // Planner talks with Coder
+ { tick: 14, type: 'message', from: 1, to: 2 },
+ { tick: 17, type: 'message', from: 2, to: 1 },
+ // Coder spawns Frontend and Backend
+ { tick: 21, type: 'spawn', from: 2, to: 4 },
+ { tick: 25, type: 'spawn', from: 2, to: 5 },
+ // Lead spawns Marketer
+ { tick: 29, type: 'spawn', from: 0, to: 6 },
+ // Chatter
+ { tick: 33, type: 'message', from: 4, to: 3 },
+ { tick: 35, type: 'message', from: 5, to: 3 },
+ { tick: 38, type: 'message', from: 3, to: 0 },
+ // Tester spawns last
+ { tick: 42, type: 'spawn', from: 3, to: 7 },
+ { tick: 46, type: 'message', from: 0, to: 6 },
+ { tick: 49, type: 'message', from: 7, to: 2 },
+];
+
+const MAX_NODES = 8;
+const CARD_W = 160;
+const CARD_H = 88;
+
+// Layout: 1 center + 7 in a single ring, evenly spaced
+const NODE_POSITIONS = (() => {
+ const cx = 0.38;
+ const cy = 0.38;
+
+ const positions: { x: number; y: number }[] = [{ x: cx, y: cy }]; // index 0 = center
+
+ const rings = [{ count: 7, radius: 0.32 }];
+
+ for (const ring of rings) {
+ for (let i = 0; i < ring.count; i++) {
+ const angle = (i / ring.count) * Math.PI * 2 - Math.PI * 0.1; // start slightly right of 0
+ const jx = Math.sin(positions.length * 7.3) * 0.01;
+ const jy = Math.cos(positions.length * 5.1) * 0.008;
+ positions.push({
+ x: cx + Math.cos(angle) * ring.radius + jx,
+ y: cy + Math.sin(angle) * ring.radius * 0.85 + jy, // squash Y slightly for wider containers
+ });
+ }
+ }
+
+ return positions;
+})();
+
+// Build connections: connect to nearest neighbors by distance
+function buildConnections(count: number): [number, number][] {
+ const conns: [number, number][] = [];
+ const threshold = 0.38;
+ for (let i = 0; i < count; i++) {
+ for (let j = i + 1; j < count; j++) {
+ const dx = NODE_POSITIONS[i].x - NODE_POSITIONS[j].x;
+ const dy = NODE_POSITIONS[i].y - NODE_POSITIONS[j].y;
+ if (Math.sqrt(dx * dx + dy * dy) < threshold) {
+ conns.push([i, j]);
+ }
+ }
+ }
+ return conns;
+}
+
+function lerp(a: number, b: number, t: number) {
+ return a + (b - a) * t;
+}
+
+function easeInOut(t: number) {
+ return t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
+}
+
+function pick(arr: T[]): T {
+ return arr[Math.floor(Math.random() * arr.length)];
+}
+
+function isReadyNode(node: AgentNode) {
+ return node.active && node.opacity >= 0.9;
+}
+
+function enqueueMessage(messages: Message[], message: Omit) {
+ messages.push({
+ ...message,
+ t: 0,
+ trail: [],
+ });
+}
+
+function setNodeRelaying(node: AgentNode, statusText: string) {
+ node.state = 'RELAYING';
+ node.statusText = statusText;
+ node.glowing = true;
+}
+
+function handleScriptEvents(tick: number, nodes: AgentNode[], messages: Message[]) {
+ for (const event of SCRIPT) {
+ if (event.tick !== tick) {
+ continue;
+ }
+
+ if (event.type === 'spawn') {
+ setNodeRelaying(nodes[event.from], `Spawning ${nodes[event.to].name}...`);
+ enqueueMessage(messages, {
+ from: event.from,
+ to: event.to,
+ speed: 0.02 + Math.random() * 0.008,
+ isSpawn: true,
+ });
+ continue;
+ }
+
+ const sender = nodes[event.from];
+ if (!isReadyNode(sender)) {
+ continue;
+ }
+
+ setNodeRelaying(sender, pick(RELAYING_TEXTS[sender.provider]));
+ enqueueMessage(messages, {
+ from: event.from,
+ to: event.to,
+ speed: 0.015 + Math.random() * 0.01,
+ });
+ }
+}
+
+function findReadyIndices(nodes: AgentNode[]) {
+ return nodes.map((node, index) => (isReadyNode(node) ? index : -1)).filter((index) => index >= 0);
+}
+
+function getActiveNeighbors(senderIdx: number, connections: [number, number][], nodes: AgentNode[]) {
+ return connections
+ .filter(([a, b]) => a === senderIdx || b === senderIdx)
+ .map(([a, b]) => (a === senderIdx ? b : a))
+ .filter((index) => isReadyNode(nodes[index]) && index !== senderIdx);
+}
+
+function pickRelayTargets(activeNeighbors: number[]) {
+ if (activeNeighbors.length >= 2 && Math.random() < 0.3) {
+ return [...activeNeighbors].sort(() => Math.random() - 0.5).slice(0, 2);
+ }
+
+ return [pick(activeNeighbors)];
+}
+
+function maybeQueueRandomRelay(
+ tick: number,
+ nodes: AgentNode[],
+ messages: Message[],
+ connections: [number, number][]
+) {
+ const readyIndices = findReadyIndices(nodes);
+ const scriptDone = tick > (SCRIPT.length > 0 ? SCRIPT[SCRIPT.length - 1].tick + 5 : 0);
+ if (!scriptDone || tick % 3 !== 0 || readyIndices.length < 2) {
+ return;
+ }
+
+ const senderIdx = pick(readyIndices);
+ const activeNeighbors = getActiveNeighbors(senderIdx, connections, nodes);
+ if (activeNeighbors.length === 0) {
+ return;
+ }
+
+ const sender = nodes[senderIdx];
+ setNodeRelaying(sender, pick(RELAYING_TEXTS[sender.provider]));
+
+ for (const target of pickRelayTargets(activeNeighbors)) {
+ enqueueMessage(messages, {
+ from: senderIdx,
+ to: target,
+ speed: 0.015 + Math.random() * 0.012,
+ branches: Math.random() < 0.25 && readyIndices.length > 1 ? [pick(readyIndices)] : undefined,
+ });
+ }
+}
+
+function cycleNodeStates(nodes: AgentNode[], tick: number) {
+ for (const node of nodes) {
+ if (node.state === 'COMPLETE') {
+ node.state = 'AWAITING TASK';
+ node.statusText = pick(IDLE_TEXTS[node.provider]);
+ }
+
+ if (node.state === 'RELAYING' && tick % 3 === 0) {
+ node.state = 'COMPLETE';
+ node.statusText = 'Done';
+ }
+
+ if (node.state === 'PROCESSING' && Math.random() < 0.15) {
+ node.statusText = pick(WORKING_TEXTS[node.provider]);
+ }
+ }
+}
+
+function updateNodePositions(nodes: AgentNode[], now: number) {
+ for (const node of nodes) {
+ const t = now * node.driftSpeed + node.driftPhase;
+ node.x = node.baseX + Math.sin(t) * node.driftAmplitudeX + Math.cos(t * 0.7) * node.driftAmplitudeX * 0.5;
+ node.y =
+ node.baseY + Math.cos(t * 1.3) * node.driftAmplitudeY + Math.sin(t * 0.5) * node.driftAmplitudeY * 0.4;
+
+ node.opacity = node.active ? Math.min(node.opacity + 0.08, 1) : Math.max(node.opacity - 0.03, 0);
+
+ node.glowOpacity = node.glowing
+ ? Math.min(node.glowOpacity + 0.06, 1)
+ : Math.max(node.glowOpacity - 0.02, 0);
+
+ if (node.glowOpacity > 0.85) {
+ node.glowing = false;
+ }
+ }
+}
+
+type NodeCenter = { cx: number; cy: number; opacity: number };
+
+function buildCenters(nodes: AgentNode[], w: number, h: number): NodeCenter[] {
+ return nodes.map((node) => ({
+ cx: node.x * w + CARD_W / 2,
+ cy: node.y * h + CARD_H / 2,
+ opacity: node.opacity,
+ }));
+}
+
+function drawConnectionLines(
+ ctx: CanvasRenderingContext2D,
+ centers: NodeCenter[],
+ connections: [number, number][]
+) {
+ for (const [i, j] of connections) {
+ const ci = centers[i];
+ const cj = centers[j];
+ if (!ci || !cj) {
+ continue;
+ }
+
+ const lineOpacity = Math.min(ci.opacity, cj.opacity) * 0.1;
+ if (lineOpacity < 0.01) {
+ continue;
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(ci.cx, ci.cy);
+ ctx.lineTo(cj.cx, cj.cy);
+ ctx.strokeStyle = `rgba(45, 79, 62, ${lineOpacity})`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+}
+
+function drawMessageTrail(ctx: CanvasRenderingContext2D, message: Message) {
+ for (let trailIndex = message.trail.length - 1; trailIndex >= 0; trailIndex--) {
+ const point = message.trail[trailIndex];
+ point.age++;
+ const trailAlpha = Math.max(0, 0.22 - point.age * 0.004);
+
+ if (trailAlpha <= 0) {
+ message.trail.splice(trailIndex, 1);
+ continue;
+ }
+
+ const trailSize = Math.max(0.5, 2.5 - point.age * 0.03);
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, trailSize, 0, Math.PI * 2);
+ ctx.fillStyle = `rgba(74, 144, 194, ${trailAlpha})`;
+ ctx.fill();
+ }
+}
+
+function drawMessagePulse(ctx: CanvasRenderingContext2D, x: number, y: number) {
+ const grad = ctx.createRadialGradient(x, y, 0, x, y, 14);
+ grad.addColorStop(0, 'rgba(74, 144, 194, 0.5)');
+ grad.addColorStop(0.4, 'rgba(74, 144, 194, 0.15)');
+ grad.addColorStop(1, 'rgba(74, 144, 194, 0)');
+ ctx.beginPath();
+ ctx.arc(x, y, 14, 0, Math.PI * 2);
+ ctx.fillStyle = grad;
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(x, y, 3.5, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(74, 144, 194, 0.8)';
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(x, y, 1.5, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(234, 230, 221, 0.9)';
+ ctx.fill();
+}
+
+function handleMessageArrival(message: Message, nodes: AgentNode[], messages: Message[]) {
+ const receiver = nodes[message.to];
+ if (!receiver) {
+ return;
+ }
+
+ if (message.isSpawn && !receiver.active) {
+ receiver.active = true;
+ receiver.state = 'AWAITING TASK';
+ receiver.statusText = pick(IDLE_TEXTS[receiver.provider]);
+ receiver.glowing = true;
+ } else if (receiver.active) {
+ receiver.state = 'PROCESSING';
+ receiver.statusText = pick(WORKING_TEXTS[receiver.provider]);
+ receiver.glowing = true;
+ }
+
+ if (!message.branches || !receiver.active) {
+ return;
+ }
+
+ const branchTargets = message.branches.filter(
+ (branchTarget) =>
+ branchTarget !== message.to && branchTarget !== message.from && nodes[branchTarget]?.active
+ );
+
+ if (branchTargets.length === 0) {
+ return;
+ }
+
+ for (const branchTarget of branchTargets) {
+ enqueueMessage(messages, {
+ from: message.to,
+ to: branchTarget,
+ speed: 0.014 + Math.random() * 0.01,
+ });
+ }
+
+ receiver.state = 'RELAYING';
+ receiver.statusText = pick(RELAYING_TEXTS[receiver.provider]);
+}
+
+function drawMessages(
+ ctx: CanvasRenderingContext2D,
+ nodes: AgentNode[],
+ messages: Message[],
+ centers: NodeCenter[]
+) {
+ for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex--) {
+ const message = messages[messageIndex];
+ message.t += message.speed;
+
+ const from = centers[message.from];
+ const to = centers[message.to];
+ if (!from || !to) {
+ messages.splice(messageIndex, 1);
+ continue;
+ }
+
+ const progress = easeInOut(Math.min(message.t, 1));
+ const px = lerp(from.cx, to.cx, progress);
+ const py = lerp(from.cy, to.cy, progress);
+
+ message.trail.push({ x: px, y: py, age: 0 });
+ drawMessageTrail(ctx, message);
+
+ if (message.t <= 1) {
+ drawMessagePulse(ctx, px, py);
+ continue;
+ }
+
+ handleMessageArrival(message, nodes, messages);
+ messages.splice(messageIndex, 1);
+ }
+}
+
+function drawGlowRings(ctx: CanvasRenderingContext2D, nodes: AgentNode[], centers: NodeCenter[]) {
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+ if (node.glowOpacity <= 0.01 || node.opacity <= 0.1) {
+ continue;
+ }
+
+ const center = centers[i];
+ const radius = 56;
+ const alpha = node.glowOpacity * node.opacity * 0.07;
+ const grad = ctx.createRadialGradient(center.cx, center.cy, radius * 0.6, center.cx, center.cy, radius);
+ grad.addColorStop(0, `rgba(45, 79, 62, ${alpha})`);
+ grad.addColorStop(1, 'rgba(45, 79, 62, 0)');
+ ctx.beginPath();
+ ctx.arc(center.cx, center.cy, radius, 0, Math.PI * 2);
+ ctx.fillStyle = grad;
+ ctx.fill();
+ }
+}
+
+// ── SVG logos from pear/AgentIcons ──
+
+function ClaudeLogo() {
+ return (
+
+
+
+ );
+}
+
+function CodexLogo() {
+ return (
+
+
+
+
+
+ );
+}
+
+function CopilotLogo() {
+ return (
+
+
+
+ );
+}
+
+function OpenCodeLogo() {
+ return (
+
+
+
+
+
+ );
+}
+
+function GeminiLogo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const LOGO_MAP: Record JSX.Element> = {
+ claude: ClaudeLogo,
+ codex: CodexLogo,
+ copilot: CopilotLogo,
+ opencode: OpenCodeLogo,
+ gemini: GeminiLogo,
+};
+
+export function NodeRelayAnimation() {
+ const containerRef = useRef(null);
+ const canvasRef = useRef(null);
+ const nodesRef = useRef([]);
+ const messagesRef = useRef([]);
+ const connectionsRef = useRef<[number, number][]>([]);
+ const rafRef = useRef(0);
+ const tickRef = useRef(0);
+ const timeRef = useRef(0);
+ const [renderNodes, setRenderNodes] = useState([]);
+
+ const initNodes = useCallback(() => {
+ // Start with only the center node (index 0 = Lead) active
+ const created: AgentNode[] = NODE_POOL.map((def, i) => ({
+ id: `node-${i}`,
+ name: def.name,
+ provider: def.provider,
+ model: def.model,
+ state: i === 0 ? 'PROCESSING' : 'IDLE',
+ statusText: i === 0 ? pick(WORKING_TEXTS[def.provider]) : '',
+ glowing: i === 0,
+ glowOpacity: 0,
+ baseX: NODE_POSITIONS[i].x,
+ baseY: NODE_POSITIONS[i].y,
+ x: NODE_POSITIONS[i].x,
+ y: NODE_POSITIONS[i].y,
+ driftPhase: Math.random() * Math.PI * 2,
+ driftSpeed: 0.0003 + Math.random() * 0.0004,
+ driftAmplitudeX: 0.008 + Math.random() * 0.01,
+ driftAmplitudeY: 0.006 + Math.random() * 0.008,
+ active: i === 0,
+ opacity: i === 0 ? 1 : 0,
+ }));
+ nodesRef.current = created;
+ connectionsRef.current = buildConnections(MAX_NODES);
+ messagesRef.current = [];
+ setRenderNodes([...created]);
+ }, []);
+
+ useEffect(() => {
+ initNodes();
+ }, [initNodes]);
+
+ // State machine
+ useEffect(() => {
+ const interval = setInterval(() => {
+ const tick = tickRef.current++;
+ const ns = nodesRef.current;
+ const ms = messagesRef.current;
+ handleScriptEvents(tick, ns, ms);
+ maybeQueueRandomRelay(tick, ns, ms, connectionsRef.current);
+ cycleNodeStates(ns, tick);
+
+ setRenderNodes([...ns]);
+ }, 250);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ // Canvas
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ const container = containerRef.current;
+ if (!canvas || !container) return;
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return;
+
+ const resize = () => {
+ const rect = container.getBoundingClientRect();
+ const dpr = window.devicePixelRatio || 1;
+ canvas.width = rect.width * dpr;
+ canvas.height = rect.height * dpr;
+ canvas.style.width = `${rect.width}px`;
+ canvas.style.height = `${rect.height}px`;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ };
+
+ resize();
+ window.addEventListener('resize', resize);
+
+ const draw = () => {
+ const rect = container.getBoundingClientRect();
+ const w = rect.width;
+ const h = rect.height;
+ const ns = nodesRef.current;
+ const ms = messagesRef.current;
+ const now = timeRef.current++;
+
+ ctx.clearRect(0, 0, w, h);
+ updateNodePositions(ns, now);
+ const centers = buildCenters(ns, w, h);
+ drawConnectionLines(ctx, centers, connectionsRef.current);
+ drawMessages(ctx, ns, ms, centers);
+ drawGlowRings(ctx, ns, centers);
+
+ rafRef.current = requestAnimationFrame(draw);
+ };
+
+ rafRef.current = requestAnimationFrame(draw);
+
+ return () => {
+ window.removeEventListener('resize', resize);
+ cancelAnimationFrame(rafRef.current);
+ };
+ }, []);
+
+ return (
+
+
+ {renderNodes.map((node) => {
+ const Logo = LOGO_MAP[node.provider];
+ const isBusy = node.state === 'PROCESSING' || node.state === 'RELAYING';
+ return (
+
0
+ ? `0 0 ${18 * node.glowOpacity}px rgba(45, 79, 62, ${0.15 * node.glowOpacity}), 0 2px 10px rgba(0,0,0,0.08)`
+ : '0 2px 8px rgba(0,0,0,0.06)',
+ pointerEvents: node.opacity < 0.1 ? 'none' : undefined,
+ }}
+ >
+
+
+
+ {node.name}
+
+
{node.model}
+
+
+ {isBusy && }
+ {node.statusText}
+
+
+ );
+ })}
+
+ );
+}
diff --git a/web/components/OpenClawLandingPage.tsx b/web/components/OpenClawLandingPage.tsx
new file mode 100644
index 00000000..d814833c
--- /dev/null
+++ b/web/components/OpenClawLandingPage.tsx
@@ -0,0 +1,123 @@
+import { CopyInstructionsButton } from './CopyInstructionsButton';
+import { absoluteUrl, SITE_HOST } from '../lib/site';
+import styles from '../app/openclaw/landing.module.css';
+
+const features = [
+ {
+ icon: '↔',
+ title: 'Real-time messaging',
+ description: 'Channels, DMs, and thread replies for collaborating claws.',
+ },
+ {
+ icon: '◌',
+ title: 'Hosted skill',
+ description: 'One URL operators can hand to any new claw.',
+ },
+ {
+ icon: '◍',
+ title: 'Hosted setup',
+ description: 'One link humans can hand to any new claw.',
+ },
+];
+
+const steps = [
+ {
+ number: '1',
+ title: 'Run setup',
+ detail: 'Create or join a workspace.',
+ code: 'npx -y @agent-relay/openclaw@latest setup --name my-claw',
+ },
+ {
+ number: '2',
+ title: 'Verify',
+ detail: 'Confirm the bridge is live.',
+ code: 'npx -y @agent-relay/openclaw@latest status',
+ },
+ {
+ number: '3',
+ title: 'Start talking',
+ detail: 'Send the first message.',
+ code: 'mcporter call agent-relay.post_message channel=general text="my-claw online"',
+ },
+];
+
+export function OpenClawLandingPage() {
+ const structuredData = {
+ '@context': 'https://schema.org',
+ '@type': 'WebPage',
+ name: 'Agent Relay for OpenClaw',
+ url: absoluteUrl('/openclaw'),
+ description: 'Connect OpenClaw to Agent Relay with shared channels, DMs, and hosted setup instructions.',
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ ◌
+ ◍
+ ◌
+
+ Agent Relay for OpenClaw
+ Connect Your Claws.
+
+ Give your claws a Slack so they can communicate, coordinate, and take action in real time with
+ channels, DMs, and emoji responses.
+
+
+
+ {SITE_HOST}/openclaw/skill
+
+
+ Send this link to your OpenClaw.
+
+
+
+ {features.map((feature) => (
+
+ {feature.icon}
+ {feature.title}
+ {feature.description}
+
+ ))}
+
+
+
+ How it works
+
+ {steps.map((step) => (
+
+
+
{step.number}
+
+
{step.title}
+
{step.detail}
+
+
+ {step.code}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/web/components/RelayAnimation.tsx b/web/components/RelayAnimation.tsx
new file mode 100644
index 00000000..68bcabba
--- /dev/null
+++ b/web/components/RelayAnimation.tsx
@@ -0,0 +1,227 @@
+'use client';
+
+import { useCallback, useEffect, useRef } from 'react';
+
+interface Node {
+ x: number;
+ y: number;
+ vx: number;
+ vy: number;
+ radius: number;
+ shade: number; // 0-1, blends between lighter and darker moss
+}
+
+interface Pulse {
+ fromIdx: number;
+ toIdx: number;
+ t: number;
+ speed: number;
+}
+
+const NODE_COUNT = 28;
+const CONNECTION_DISTANCE = 160;
+const PULSE_CHANCE = 0.008;
+
+// Moss / forest palette
+const BG_CENTER = '#ece8e0'; // warm oatmeal, slightly lighter center
+const BG_EDGE = '#e5e1d8'; // slightly darker edge
+const NODE_LIGHT = 'rgba(45, 79, 62, 0.7)'; // #2D4F3E at 70%
+const NODE_DARK = 'rgba(58, 100, 78, 0.85)'; // slightly brighter moss
+const GLOW_COLOR = 'rgba(45, 79, 62, 0.12)';
+const LINE_COLOR_R = 45;
+const LINE_COLOR_G = 79;
+const LINE_COLOR_B = 62;
+const PULSE_CENTER = 'rgba(45, 79, 62, 0.55)';
+const PULSE_MID = 'rgba(45, 79, 62, 0.18)';
+const PULSE_EDGE = 'rgba(45, 79, 62, 0)';
+
+function drawBackground(ctx: CanvasRenderingContext2D, w: number, h: number) {
+ const bgGrad = ctx.createRadialGradient(w * 0.5, h * 0.45, 0, w * 0.5, h * 0.45, w * 0.7);
+ bgGrad.addColorStop(0, BG_CENTER);
+ bgGrad.addColorStop(1, BG_EDGE);
+ ctx.fillStyle = bgGrad;
+ ctx.fillRect(0, 0, w, h);
+}
+
+function updateNodePositions(nodes: Node[], w: number, h: number) {
+ for (const node of nodes) {
+ node.x += node.vx;
+ node.y += node.vy;
+
+ if (node.x > w + 20) node.x = -20;
+ if (node.x < -20) node.x = w + 20;
+
+ if (node.y < 20 || node.y > h - 20) {
+ node.vy *= -1;
+ }
+ }
+}
+
+function drawConnections(ctx: CanvasRenderingContext2D, nodes: Node[], pulses: Pulse[]) {
+ for (let i = 0; i < nodes.length; i++) {
+ for (let j = i + 1; j < nodes.length; j++) {
+ const dx = nodes[i].x - nodes[j].x;
+ const dy = nodes[i].y - nodes[j].y;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+
+ if (dist >= CONNECTION_DISTANCE) {
+ continue;
+ }
+
+ const alpha = (1 - dist / CONNECTION_DISTANCE) * 0.15;
+ ctx.beginPath();
+ ctx.moveTo(nodes[i].x, nodes[i].y);
+ ctx.lineTo(nodes[j].x, nodes[j].y);
+ ctx.strokeStyle = `rgba(${LINE_COLOR_R}, ${LINE_COLOR_G}, ${LINE_COLOR_B}, ${alpha})`;
+ ctx.lineWidth = 0.8;
+ ctx.stroke();
+
+ if (Math.random() < PULSE_CHANCE) {
+ pulses.push({
+ fromIdx: i,
+ toIdx: j,
+ t: 0,
+ speed: 0.008 + Math.random() * 0.012,
+ });
+ }
+ }
+ }
+}
+
+function drawPulses(ctx: CanvasRenderingContext2D, nodes: Node[], pulses: Pulse[]) {
+ for (let p = pulses.length - 1; p >= 0; p--) {
+ const pulse = pulses[p];
+ pulse.t += pulse.speed;
+
+ if (pulse.t > 1) {
+ pulses.splice(p, 1);
+ continue;
+ }
+
+ const from = nodes[pulse.fromIdx];
+ const to = nodes[pulse.toIdx];
+ if (!from || !to) {
+ pulses.splice(p, 1);
+ continue;
+ }
+
+ const px = from.x + (to.x - from.x) * pulse.t;
+ const py = from.y + (to.y - from.y) * pulse.t;
+
+ const grad = ctx.createRadialGradient(px, py, 0, px, py, 7);
+ grad.addColorStop(0, PULSE_CENTER);
+ grad.addColorStop(0.5, PULSE_MID);
+ grad.addColorStop(1, PULSE_EDGE);
+ ctx.beginPath();
+ ctx.arc(px, py, 7, 0, Math.PI * 2);
+ ctx.fillStyle = grad;
+ ctx.fill();
+ }
+}
+
+function drawNodes(ctx: CanvasRenderingContext2D, nodes: Node[]) {
+ for (const node of nodes) {
+ const glowGrad = ctx.createRadialGradient(node.x, node.y, 0, node.x, node.y, node.radius * 5);
+ glowGrad.addColorStop(0, GLOW_COLOR);
+ glowGrad.addColorStop(1, 'rgba(45, 79, 62, 0)');
+ ctx.beginPath();
+ ctx.arc(node.x, node.y, node.radius * 5, 0, Math.PI * 2);
+ ctx.fillStyle = glowGrad;
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
+ ctx.fillStyle = node.shade > 0.5 ? NODE_DARK : NODE_LIGHT;
+ ctx.fill();
+ }
+}
+
+export default function RelayAnimation({ className }: { className?: string }) {
+ const canvasRef = useRef(null);
+ const nodesRef = useRef([]);
+ const pulsesRef = useRef([]);
+ const rafRef = useRef(0);
+ const sizeRef = useRef({ w: 0, h: 0 });
+
+ const initNodes = useCallback((w: number, h: number) => {
+ const nodes: Node[] = [];
+ for (let i = 0; i < NODE_COUNT; i++) {
+ nodes.push({
+ x: Math.random() * w,
+ y: Math.random() * h,
+ vx: (Math.random() - 0.3) * 0.6,
+ vy: (Math.random() - 0.5) * 0.25,
+ radius: 2.5 + Math.random() * 2,
+ shade: Math.random(),
+ });
+ }
+ nodesRef.current = nodes;
+ pulsesRef.current = [];
+ }, []);
+
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return;
+
+ const resize = () => {
+ const rect = canvas.parentElement?.getBoundingClientRect();
+ if (!rect) return;
+ const dpr = window.devicePixelRatio || 1;
+ const w = rect.width;
+ const h = rect.height;
+ canvas.width = w * dpr;
+ canvas.height = h * dpr;
+ canvas.style.width = `${w}px`;
+ canvas.style.height = `${h}px`;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+
+ if (sizeRef.current.w === 0 || nodesRef.current.length === 0) {
+ initNodes(w, h);
+ } else {
+ const sx = w / sizeRef.current.w;
+ const sy = h / sizeRef.current.h;
+ for (const node of nodesRef.current) {
+ node.x *= sx;
+ node.y *= sy;
+ }
+ }
+ sizeRef.current = { w, h };
+ };
+
+ resize();
+ window.addEventListener('resize', resize);
+
+ const draw = () => {
+ const { w, h } = sizeRef.current;
+ const nodes = nodesRef.current;
+ const pulses = pulsesRef.current;
+
+ ctx.clearRect(0, 0, w, h);
+ drawBackground(ctx, w, h);
+ updateNodePositions(nodes, w, h);
+ drawConnections(ctx, nodes, pulses);
+ drawPulses(ctx, nodes, pulses);
+ drawNodes(ctx, nodes);
+
+ rafRef.current = requestAnimationFrame(draw);
+ };
+
+ rafRef.current = requestAnimationFrame(draw);
+
+ return () => {
+ window.removeEventListener('resize', resize);
+ cancelAnimationFrame(rafRef.current);
+ };
+ }, [initNodes]);
+
+ return (
+
+ );
+}
diff --git a/web/components/SdkCodeExample.tsx b/web/components/SdkCodeExample.tsx
new file mode 100644
index 00000000..77797b9f
--- /dev/null
+++ b/web/components/SdkCodeExample.tsx
@@ -0,0 +1,114 @@
+'use client';
+
+import { useState } from 'react';
+
+import s from './sdk-code.module.css';
+
+const TS_CODE = `import { AgentRelay } from "@agent-relay/sdk";
+
+const relay = new AgentRelay({ channels: ["quantum-error-correction"] });
+
+await relay.spawnAgent({
+ name: "Research",
+ cli: "claude",
+ task: "Discuss quantum error correction approaches with Build.",
+});
+
+await relay.spawnAgent({
+ name: "Build",
+ cli: "codex",
+ task: "Debate implementation strategies with Research.",
+});
+
+const director = relay.human({ name: "Director" });
+await director.sendMessage({
+ to: "quantum-error-correction",
+ text: "Discuss and report back with solutions you both agree on.",
+});`;
+
+const PY_CODE = `from agent_relay import AgentRelay
+
+relay = AgentRelay(channels=["quantum-error-correction"])
+
+await relay.claude.spawn(
+ name="Research",
+ task="Discuss quantum error correction approaches with Build.",
+)
+
+await relay.codex.spawn(
+ name="Build",
+ task="Debate implementation strategies with Research.",
+)
+
+director = relay.human(name="Director")
+await director.send_message(
+ to="quantum-error-correction",
+ text="Discuss and report back with solutions you both agree on.",
+)`;
+
+type Tab = 'typescript' | 'python';
+
+function highlight(code: string, lang: Tab) {
+ // Simple syntax highlighting via spans
+ const keywords = lang === 'typescript' ? /\b(import|from|const|await|new)\b/g : /\b(from|import|await)\b/g;
+
+ const methods = /\.(\w+)\(/g;
+ const types = lang === 'typescript' ? /\b(AgentRelay)\b/g : /\b(AgentRelay)\b/g;
+
+ // Process in stages to avoid overlapping
+ const escaped = code.replace(/&/g, '&').replace(//g, '>');
+
+ // Replace strings first (capture them)
+ const parts: string[] = [];
+ let lastIdx = 0;
+ const stringMatches = [...escaped.matchAll(/(["'`])(?:(?!\1).)*\1/g)];
+
+ if (stringMatches.length === 0) {
+ return highlightNonString(escaped, keywords, methods, types);
+ }
+
+ for (const m of stringMatches) {
+ const before = escaped.slice(lastIdx, m.index);
+ parts.push(highlightNonString(before, keywords, methods, types));
+ parts.push(`${m[0]} `);
+ lastIdx = m.index! + m[0].length;
+ }
+ parts.push(highlightNonString(escaped.slice(lastIdx), keywords, methods, types));
+
+ return parts.join('');
+}
+
+function highlightNonString(text: string, keywords: RegExp, methods: RegExp, types: RegExp): string {
+ return text
+ .replace(types, `$1 `)
+ .replace(keywords, `$& `)
+ .replace(methods, `.$1 (`);
+}
+
+export function SdkCodeExample() {
+ const [tab, setTab] = useState('typescript');
+
+ const code = tab === 'typescript' ? TS_CODE : PY_CODE;
+
+ return (
+
+
+ setTab('typescript')}
+ >
+ TypeScript
+
+ setTab('python')}
+ >
+ Python
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/SiteFooter.tsx b/web/components/SiteFooter.tsx
new file mode 100644
index 00000000..b6734a7e
--- /dev/null
+++ b/web/components/SiteFooter.tsx
@@ -0,0 +1,175 @@
+import Link from 'next/link';
+
+import { SITE_EMAIL } from '../lib/site';
+import { LogoIcon, LogoWordmark } from './SiteNav';
+import s from './site-footer.module.css';
+
+export function SiteFooter() {
+ return (
+
+
+
+
+
+
+
+
The future is multi-agent.
+
+
+
+
+
+
Documentation
+
+ Introduction
+
+
+ Quickstart
+
+
+ Messaging
+
+
+ Delivery
+
+
+ Actions
+
+
+ CLI
+
+
+ TypeScript SDK
+
+
+
+
+
+
+
+
+
+
© {new Date().getFullYear()} Agent Relay. All rights reserved.
+
+
+
+
+ );
+}
diff --git a/web/components/SiteNav.tsx b/web/components/SiteNav.tsx
new file mode 100644
index 00000000..c0c8e18e
--- /dev/null
+++ b/web/components/SiteNav.tsx
@@ -0,0 +1,159 @@
+'use client';
+
+import type React from 'react';
+import { useEffect, useState } from 'react';
+import Link from 'next/link';
+import { usePathname } from 'next/navigation';
+
+import s from './site-nav.module.css';
+
+export function LogoIcon() {
+ return (
+
+
+
+
+ );
+}
+
+export function LogoWordmark() {
+ return (
+
+
+
+ );
+}
+
+export function SiteNav({
+ center,
+ actions,
+ mobileMenuContent,
+ hideMobileDocsLink,
+}: {
+ center?: React.ReactNode;
+ actions?: React.ReactNode;
+ mobileMenuContent?: React.ReactNode;
+ hideMobileDocsLink?: boolean;
+} = {}) {
+ const pathname = usePathname();
+ const [scrolled, setScrolled] = useState(false);
+ const [menuOpen, setMenuOpen] = useState(false);
+
+ useEffect(() => {
+ const onScroll = () => setScrolled(window.scrollY > 20);
+ onScroll();
+ window.addEventListener('scroll', onScroll, { passive: true });
+ return () => window.removeEventListener('scroll', onScroll);
+ }, []);
+
+ useEffect(() => {
+ setMenuOpen(false);
+ }, [pathname]);
+
+ return (
+
+ );
+}
diff --git a/web/components/SkillPage.tsx b/web/components/SkillPage.tsx
new file mode 100644
index 00000000..82a5955c
--- /dev/null
+++ b/web/components/SkillPage.tsx
@@ -0,0 +1,19 @@
+type SkillPageProps = {
+ eyebrow?: string;
+ title: string;
+ lead: string;
+ markdown: string;
+};
+
+export function SkillPage({ eyebrow = 'Hosted Skill', title, lead, markdown }: SkillPageProps) {
+ return (
+
+
+ {eyebrow}
+ {title}
+ {lead}
+
+ {markdown}
+
+ );
+}
diff --git a/web/components/WaitlistForm.tsx b/web/components/WaitlistForm.tsx
new file mode 100644
index 00000000..19ed0f45
--- /dev/null
+++ b/web/components/WaitlistForm.tsx
@@ -0,0 +1,84 @@
+'use client';
+
+import { useState, type FormEvent } from 'react';
+
+import s from './waitlist.module.css';
+
+const WAITLIST_ENDPOINT =
+ process.env.NEXT_PUBLIC_WAITLIST_API_URL?.trim() || 'https://agentrelay.com/cloud/api/waitlist';
+
+export function WaitlistForm() {
+ const [email, setEmail] = useState('');
+ const [state, setState] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
+ const [submittedEmail, setSubmittedEmail] = useState('');
+
+ async function handleSubmit(e: FormEvent) {
+ e.preventDefault();
+ if (!email.trim()) return;
+
+ setState('loading');
+
+ try {
+ const res = await fetch(WAITLIST_ENDPOINT, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ email: email.trim(), source: 'relay-web' }),
+ });
+
+ if (res.ok) {
+ setSubmittedEmail(email.trim());
+ setState('success');
+ setEmail('');
+ } else {
+ setState('error');
+ }
+ } catch {
+ setState('error');
+ }
+ }
+
+ if (state === 'success') {
+ return (
+
+
+
You're on the list!
+
+ We'll send updates about new features, SDK releases, and early access to{' '}
+ {submittedEmail}
+
+
+ );
+ }
+
+ return (
+
+ );
+}
diff --git a/web/components/agents/AgentArt.tsx b/web/components/agents/AgentArt.tsx
new file mode 100644
index 00000000..93b4538e
--- /dev/null
+++ b/web/components/agents/AgentArt.tsx
@@ -0,0 +1,73 @@
+import type { CSSProperties } from 'react';
+
+import { agentAsset, type Agent } from '../../lib/agents';
+
+type Variant = 'card' | 'card-sm' | 'banner';
+
+const ASSET_FOR: Record = {
+ card: 'card',
+ 'card-sm': 'card-sm',
+ banner: 'banner',
+};
+
+const FALLBACK_FONT_SIZE: Record = {
+ banner: 'clamp(3rem, 9vw, 6rem)',
+ card: 'clamp(2.4rem, 12vw, 4rem)',
+ 'card-sm': 'clamp(2.4rem, 12vw, 4rem)',
+};
+
+function monogram(name: string): string {
+ const words = name.split(/\s+/).filter(Boolean);
+ if (words.length === 1) return words[0].slice(0, 2).toUpperCase();
+ return (words[0][0] + words[words.length - 1][0]).toUpperCase();
+}
+
+/**
+ * Renders an agent's committed artwork as a plain (next/image is avoided
+ * because the SST/OpenNext optimizer 500s without sharp). Agents without
+ * committed PNGs get a deterministic brand-gradient + monogram so static
+ * renders, OG, and the sitemap stay stable.
+ */
+export function AgentArt({
+ agent,
+ variant,
+ alt,
+ loading = 'lazy',
+}: {
+ agent: Agent;
+ variant: Variant;
+ alt?: string;
+ /** Use 'eager' for above-the-fold art (e.g. the detail hero banner). */
+ loading?: 'lazy' | 'eager';
+}) {
+ if (agent.hasCustomArt) {
+ return (
+
+ );
+ }
+
+ const style: CSSProperties = {
+ position: 'absolute',
+ inset: 0,
+ background: `linear-gradient(135deg, ${agent.accent}, color-mix(in srgb, ${agent.accent} 50%, #0b0e14))`,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ fontFamily: 'var(--font-heading), sans-serif',
+ fontWeight: 700,
+ letterSpacing: '-0.04em',
+ fontSize: FALLBACK_FONT_SIZE[variant],
+ color: 'rgba(255,255,255,0.96)',
+ textShadow: '0 2px 16px rgba(0,0,0,0.28)',
+ };
+
+ return (
+
+ {monogram(agent.name)}
+
+ );
+}
diff --git a/web/components/agents/BuildYourOwn.tsx b/web/components/agents/BuildYourOwn.tsx
new file mode 100644
index 00000000..61f9f84d
--- /dev/null
+++ b/web/components/agents/BuildYourOwn.tsx
@@ -0,0 +1,173 @@
+'use client';
+
+import { Fragment, useState } from 'react';
+import { Check, Copy, Eye, EyeOff } from 'lucide-react';
+
+import { FadeIn } from '../FadeIn';
+import s from '../../app/agents/agents.module.css';
+
+const BUILD_AGENT_PROMPT = `Build me a new AgentWorkforce agent and prepare it to deploy on Agent Relay.
+
+Use the AgentWorkforce agents repo (https://github.com/AgentWorkforce/agents) as the structural reference, and the "creating-cloud-persona" skill for how to structure it.
+
+The agent should: .
+
+Steps:
+1. Read an existing agent in the repo (e.g. review/ or vendor-monitor/) to learn the persona.json + agent.ts shape.
+2. Scaffold a new / folder:
+ - persona.json: id, intent, tags, description, cloud: true, the integrations the job needs (scoped paths only), harness/model, and the inputs a user must configure.
+ - agent.ts: declare the trigger or cron schedule with defineAgent(...) and implement the behavior, branching on event.source/event.type.
+3. Wire only the integrations the job actually uses, and expose each user-configurable value as an input with a picker where one applies.
+4. Keep it minimal, match the repo's patterns, typecheck/build, and tell me exactly how to deploy it on Agent Relay.`;
+
+const PREVIEW = `${BUILD_AGENT_PROMPT.replace(/\s+/g, ' ').slice(0, 96)}...`;
+
+const INSTALL_CMDS = [
+ 'npx skills add https://github.com/agentworkforce/skills --skill creating-cloud-persona',
+ 'npx prpm install @agent-relay/creating-cloud-persona --as codex,claude',
+];
+
+async function copyText(text: string) {
+ if (navigator.clipboard?.writeText) {
+ try {
+ await navigator.clipboard.writeText(text);
+ return;
+ } catch {
+ // Fall back for embedded browsers that expose clipboard without write access.
+ }
+ }
+ const textarea = document.createElement('textarea');
+ textarea.value = text;
+ textarea.setAttribute('readonly', '');
+ textarea.style.position = 'fixed';
+ textarea.style.left = '-9999px';
+ document.body.appendChild(textarea);
+ textarea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textarea);
+}
+
+export function BuildYourOwn() {
+ const [copied, setCopied] = useState(false);
+ const [copiedCmd, setCopiedCmd] = useState(null);
+ const [showPrompt, setShowPrompt] = useState(false);
+
+ async function handleCopy() {
+ await copyText(BUILD_AGENT_PROMPT);
+ setCopied(true);
+ window.setTimeout(() => setCopied(false), 1800);
+ }
+
+ async function handleCopyCmd(cmd: string) {
+ await copyText(cmd);
+ setCopiedCmd(cmd);
+ window.setTimeout(() => setCopiedCmd(null), 1800);
+ }
+
+ return (
+
+
+
+
+
+
+ BUILD YOUR OWN
+
+
Can't find the one you need? Build it.
+
+ An agent is just a persona.json and an agent.ts handler. Paste this
+ prompt into your coding agent — it reads the open-source examples, follows the persona skills,
+ and scaffolds a deployable agent for you.
+
+
+
+
+
+
+
1 · Download the persona skill
+ {INSTALL_CMDS.map((cmd, i) => (
+
+ {i > 0 && or }
+
+ {cmd}
+ handleCopyCmd(cmd)}
+ aria-label="Copy install command"
+ >
+ {copiedCmd === cmd ? : }
+
+
+
+ ))}
+
+
+
+
+ 2 · Paste this prompt into your coding agent
+
+
{PREVIEW}
+
+ {showPrompt && (
+
+ Prompt
+ {BUILD_AGENT_PROMPT}
+
+ )}
+
+
+ setShowPrompt((value) => !value)}
+ >
+ {showPrompt ? : }
+ {showPrompt ? 'Hide prompt' : 'Show prompt'}
+
+
+ {copied ? (
+ <>
+ Copied
+ >
+ ) : (
+ <>
+ Copy prompt
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/agents/ForkAgentButton.tsx b/web/components/agents/ForkAgentButton.tsx
new file mode 100644
index 00000000..16ce3bd3
--- /dev/null
+++ b/web/components/agents/ForkAgentButton.tsx
@@ -0,0 +1,50 @@
+import { forkUrl, launchUrl, type Agent } from '../../lib/agents';
+
+const GitHubMark = () => (
+
+
+
+);
+
+/**
+ * CTA row for an agent: primary "Launch agent" (one-click deploy on Agent Relay
+ * Cloud via https://agentrelay.com/cloud/deploy?persona=…) and a secondary
+ * "Fork on GitHub" link (https://github.com//fork). Uses the global .btn
+ * classes via the className props the caller passes.
+ */
+export function ForkAgentButton({
+ agent,
+ primaryClassName,
+ secondaryClassName,
+ primaryLabel = 'Launch agent',
+}: {
+ agent: Agent;
+ primaryClassName: string;
+ secondaryClassName: string;
+ primaryLabel?: string;
+}) {
+ return (
+ <>
+
+ {primaryLabel}
+ →
+
+
+
+ Fork on GitHub
+
+ >
+ );
+}
diff --git a/web/components/blog/BlogTableOfContents.tsx b/web/components/blog/BlogTableOfContents.tsx
new file mode 100644
index 00000000..9dcbf293
--- /dev/null
+++ b/web/components/blog/BlogTableOfContents.tsx
@@ -0,0 +1,56 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+import type { TocItem } from '../../lib/blog';
+import styles from './blog.module.css';
+
+export function BlogTableOfContents({ items }: { items: TocItem[] }) {
+ const [activeId, setActiveId] = useState(items[0]?.id ?? '');
+
+ useEffect(() => {
+ const headings = items.map((item) => document.getElementById(item.id)).filter(Boolean) as HTMLElement[];
+
+ if (headings.length === 0) {
+ return;
+ }
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ for (const entry of entries) {
+ if (entry.isIntersecting) {
+ setActiveId(entry.target.id);
+ }
+ }
+ },
+ { rootMargin: '-96px 0px -68% 0px', threshold: 0 }
+ );
+
+ for (const heading of headings) {
+ observer.observe(heading);
+ }
+
+ return () => observer.disconnect();
+ }, [items]);
+
+ if (items.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/web/components/blog/blog.module.css b/web/components/blog/blog.module.css
new file mode 100644
index 00000000..fa36d1f5
--- /dev/null
+++ b/web/components/blog/blog.module.css
@@ -0,0 +1,1043 @@
+/* Blog */
+
+.blogPage {
+ --blog-nav-overlay-offset: 68px;
+ --blog-band-bg:
+ radial-gradient(circle at 18% 24%, rgba(116, 184, 226, 0.16), transparent 32%),
+ linear-gradient(180deg, #10273a 0%, #17364c 52%, #0d2638 100%);
+ --blog-shell-bg: #08111a;
+ --blog-post-hero-bg: #0b1c2a;
+ min-height: 100vh;
+ background: var(--blog-band-bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+}
+
+.blogMain {
+ padding-bottom: 0;
+}
+
+.postShell {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 0 40px 5.5rem;
+}
+
+/* Index */
+
+.blogHero,
+.postHero {
+ position: relative;
+ overflow: hidden;
+ margin-top: calc(-1 * var(--blog-nav-overlay-offset));
+ padding-top: var(--blog-nav-overlay-offset);
+ background: var(--blog-post-hero-bg);
+ border-bottom: 1px solid rgba(116, 184, 226, 0.12);
+}
+
+.postHero {
+ background: var(--blog-band-bg);
+ border-bottom: 0;
+}
+
+.blogHero {
+ border-bottom: 0;
+}
+
+.blogHero::before,
+.postHero::before {
+ content: '';
+ position: absolute;
+ inset: auto 6% -35% auto;
+ width: min(760px, 68vw);
+ height: 460px;
+ pointer-events: none;
+ background: radial-gradient(ellipse at center, rgba(193, 103, 75, 0.16), transparent 68%);
+ opacity: 0.8;
+}
+
+.blogHero::before,
+.postHero::before {
+ content: none;
+}
+
+.blogHeroInner,
+.postHeroInner {
+ position: relative;
+ z-index: 1;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 112px 40px 86px;
+}
+
+.eyebrow,
+.sectionKicker,
+.postHeroCategory {
+ margin: 0;
+ font-size: 0.76rem;
+ font-weight: 700;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--primary);
+}
+
+.blogTitle,
+.postHeroTitle {
+ max-width: 13ch;
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(3rem, 8vw, 5.4rem);
+ font-weight: 500;
+ letter-spacing: -0.03em;
+ line-height: 1.03;
+}
+
+.blogSubtitle,
+.postHeroDek {
+ max-width: 66ch;
+ margin: 28px 0 0;
+ color: var(--fg-muted);
+ font-size: clamp(1rem, 1.8vw, 1.18rem);
+ line-height: 1.72;
+}
+
+.blogSection {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 3.5rem 40px 0;
+}
+
+.archiveSection {
+ padding-bottom: 5.5rem;
+ background: var(--blog-post-hero-bg);
+ box-shadow: 0 0 0 100vmax var(--blog-post-hero-bg);
+ clip-path: inset(0 -100vmax);
+}
+
+.sectionHeader {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 28px;
+ margin-bottom: 1.5rem;
+}
+
+.sectionTitle {
+ margin: 0.35rem 0 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.45rem, 3vw, 2.25rem);
+ font-weight: 600;
+ letter-spacing: -0.03em;
+ line-height: 1.15;
+}
+
+.sectionTitleRow {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+}
+
+.sectionDescription {
+ max-width: 42ch;
+ margin: 0;
+ color: var(--fg-muted);
+ font-size: 0.98rem;
+ line-height: 1.65;
+}
+
+.featuredCard {
+ display: block;
+ padding: clamp(1.4rem, 3vw, 2rem);
+ border: 1px solid rgba(156, 207, 255, 0.16);
+ border-radius: 8px;
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.018)), rgba(17, 30, 47, 0.76);
+ backdrop-filter: blur(20px) saturate(1.35);
+ -webkit-backdrop-filter: blur(20px) saturate(1.35);
+ color: inherit;
+ text-decoration: none;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.08),
+ 0 20px 54px rgba(2, 9, 18, 0.26);
+ transition:
+ border-color 0.2s ease,
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
+}
+
+.featuredCard:hover {
+ border-color: rgba(156, 207, 255, 0.28);
+ transform: translateY(-2px);
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.065), rgba(255, 255, 255, 0.024)), rgba(17, 30, 47, 0.84);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
+ 0 24px 62px rgba(2, 9, 18, 0.3);
+}
+
+.featuredContent {
+ display: grid;
+ gap: 16px;
+ max-width: 840px;
+}
+
+.postMeta {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 8px;
+ color: var(--fg-faint);
+ font-size: 0.78rem;
+}
+
+.postCategory {
+ color: var(--primary);
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.postDot,
+.postListDot {
+ color: var(--fg-faint);
+ opacity: 0.5;
+}
+
+.featuredTitle {
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.7rem, 4vw, 3.2rem);
+ font-weight: 550;
+ letter-spacing: -0.03em;
+ line-height: 1.08;
+}
+
+.featuredDesc {
+ max-width: 68ch;
+ margin: 0;
+ color: var(--fg-muted);
+ font-size: 1rem;
+ line-height: 1.7;
+}
+
+.featuredAuthor,
+.authorCard {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.authorAvatar {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ flex: 0 0 auto;
+ width: 42px;
+ height: 42px;
+ border-radius: 50%;
+ background:
+ linear-gradient(135deg, rgba(116, 184, 226, 0.22), rgba(193, 103, 75, 0.18)), rgba(12, 28, 42, 0.95);
+ border: 1px solid rgba(116, 184, 226, 0.24);
+ color: var(--fg);
+ font-size: 0.78rem;
+ font-weight: 800;
+ letter-spacing: 0.06em;
+}
+
+.authorAvatarPhoto {
+ overflow: hidden;
+ border-radius: 999px;
+ background: #ffffff;
+ border-color: transparent;
+ clip-path: circle(50%);
+}
+
+.authorAvatarPhoto img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ border-radius: inherit;
+ object-fit: cover;
+ object-position: center;
+}
+
+.authorInfo {
+ display: flex;
+ min-width: 0;
+ flex-direction: column;
+ gap: 1px;
+}
+
+.authorPanel .authorAvatar {
+ width: 48px;
+ height: 48px;
+ font-size: 0.84rem;
+}
+
+.authorLabel,
+.sidebarTitle {
+ margin: 0;
+ color: var(--fg-faint);
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+}
+
+.authorName {
+ color: var(--fg);
+ font-size: 0.94rem;
+ font-weight: 600;
+ line-height: 1.08;
+}
+
+.authorRole {
+ color: var(--fg-faint);
+ font-size: 0.76rem;
+ line-height: 1.08;
+}
+
+.authorSocialLinks {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-top: 6px;
+}
+
+.authorSocialLinks a {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 17px;
+ height: 17px;
+ color: var(--fg-faint);
+ text-decoration: none;
+ transition: color 0.15s ease;
+}
+
+.authorSocialLinks a:hover {
+ color: var(--primary);
+}
+
+.authorSocialLinks svg {
+ width: 14px;
+ height: 14px;
+}
+
+.postList {
+ border-top: 1px solid rgba(116, 184, 226, 0.14);
+}
+
+.postListRow {
+ border-bottom: 1px solid rgba(116, 184, 226, 0.14);
+}
+
+.postListRowLink {
+ display: grid;
+ gap: 8px;
+ padding: 22px 0;
+ color: inherit;
+ text-decoration: none;
+ transition:
+ opacity 0.15s ease,
+ color 0.15s ease;
+}
+
+.postListRowLink:hover {
+ opacity: 0.78;
+}
+
+.postListTopline {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 24px;
+}
+
+.postListTitle {
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.08rem, 2vw, 1.42rem);
+ font-weight: 550;
+ letter-spacing: -0.02em;
+ line-height: 1.22;
+ transition: color 0.15s ease;
+}
+
+.postListRowLink:hover .postListTitle {
+ color: #d2e1ea;
+}
+
+.postListDescription {
+ max-width: 76ch;
+ margin: 0;
+ color: var(--fg-muted);
+ font-size: 0.95rem;
+ line-height: 1.58;
+}
+
+.postListMeta {
+ display: inline-grid;
+ grid-template-columns: 22px minmax(0, auto);
+ align-items: center;
+ justify-content: flex-end;
+ justify-self: end;
+ column-gap: 8px;
+ color: var(--fg-muted);
+ font-size: 0.86rem;
+ text-align: left;
+}
+
+.postListMetaText {
+ display: grid;
+ gap: 2px;
+ min-width: 0;
+}
+
+.postListAuthor,
+.postListTime {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+}
+
+.postListAvatar {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ flex: 0 0 auto;
+ width: 22px;
+ height: 22px;
+ border-radius: 999px;
+ background:
+ linear-gradient(135deg, rgba(116, 184, 226, 0.22), rgba(193, 103, 75, 0.18)), rgba(12, 28, 42, 0.95);
+ color: var(--fg);
+ font-size: 0.6rem;
+ font-weight: 800;
+ letter-spacing: 0.04em;
+}
+
+.postListAuthor,
+.postListDate,
+.postListRead {
+ white-space: nowrap;
+}
+
+/* Single post */
+
+.postHeroInner {
+ padding-top: 104px;
+ padding-bottom: 64px;
+}
+
+.postHeroCategory {
+ display: block;
+ margin-bottom: 28px;
+ color: var(--primary);
+}
+
+.postHeroTitle {
+ max-width: 14ch;
+ font-size: clamp(2.25rem, 5.2vw, 3.65rem);
+ line-height: 1.04;
+}
+
+.postHeroDek {
+ margin-top: 18px;
+ font-size: clamp(0.98rem, 1.5vw, 1.08rem);
+ line-height: 1.55;
+}
+
+.blogWaveDivider,
+.postWaveDivider {
+ position: relative;
+ height: 72px;
+ margin-top: -1px;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+.blogWaveDivider {
+ background: var(--blog-post-hero-bg);
+}
+
+.postWaveDivider {
+ background: var(--blog-shell-bg);
+}
+
+.blogWaveDivider svg,
+.postWaveDivider svg {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+
+.blogWaveDivider path:first-child {
+ fill: var(--blog-post-hero-bg);
+}
+
+.postWaveDivider path:first-child {
+ fill: #0d2638;
+}
+
+.blogWaveDivider path:not(:first-child),
+.postWaveDivider path:not(:first-child) {
+ stroke: rgba(116, 184, 226, 0.42);
+ stroke-width: 1.2;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+}
+
+.blogWaveDivider path:nth-child(3),
+.postWaveDivider path:nth-child(3) {
+ opacity: 0.54;
+}
+
+.postShell {
+ max-width: none;
+ margin: 0;
+ padding: 0 0 5.5rem;
+ background: var(--blog-shell-bg);
+}
+
+.postLayout {
+ display: grid;
+ grid-template-columns: 280px minmax(0, 760px);
+ gap: clamp(48px, 8vw, 96px);
+ align-items: start;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2.75rem 40px 0;
+}
+
+.postMain {
+ min-width: 0;
+}
+
+.postSidebar {
+ min-width: 0;
+ align-self: stretch;
+}
+
+.sidebarSticky {
+ display: grid;
+ gap: 28px;
+ align-content: start;
+ height: 100%;
+}
+
+.authorPanel,
+.tocPanel {
+ display: grid;
+ gap: 16px;
+}
+
+.tocPanel {
+ padding-left: 0;
+}
+
+.postSidebar .tocPanel {
+ position: sticky;
+ top: 96px;
+ align-self: start;
+}
+
+.postSidebarMeta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px 12px;
+ color: var(--fg-muted);
+ font-size: 0.78rem;
+ line-height: 1.2;
+}
+
+.postSidebarMeta span {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ white-space: nowrap;
+}
+
+.postSidebarMeta svg {
+ width: 13px;
+ height: 13px;
+ color: var(--fg-faint);
+}
+
+.sidebarNav {
+ display: block;
+ border-left: 1px solid rgba(116, 184, 226, 0.14);
+}
+
+.tocList {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.tocLink {
+ display: block;
+ padding: 7px 0 7px 16px;
+ border-left: 2px solid transparent;
+ color: var(--fg-muted);
+ font-size: 0.9rem;
+ line-height: 1.45;
+ text-decoration: none;
+ transform: translateX(-1px);
+ transition:
+ border-color 0.15s ease,
+ color 0.15s ease;
+}
+
+.tocIndent {
+ padding-left: 28px;
+}
+
+.tocActive,
+.tocLink:hover {
+ border-left-color: var(--primary);
+ color: var(--fg);
+}
+
+.mobileToc {
+ display: none;
+ margin-bottom: 2rem;
+ padding: 1rem 0 1.1rem;
+ border-top: 1px solid rgba(116, 184, 226, 0.14);
+ border-bottom: 1px solid rgba(116, 184, 226, 0.14);
+}
+
+.mobileAuthor {
+ display: none;
+ margin-bottom: 1.3rem;
+ padding-bottom: 1.25rem;
+ border-bottom: 1px solid rgba(116, 184, 226, 0.14);
+}
+
+/* Article */
+
+.article {
+ color: var(--fg);
+ font-size: clamp(1.02rem, 1.25vw, 1.16rem);
+ line-height: 1.84;
+}
+
+.article h2,
+.article h3 {
+ scroll-margin-top: 120px;
+}
+
+.article h2 {
+ margin: 3.6rem 0 1rem;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.8rem, 3.4vw, 2.45rem);
+ font-weight: 650;
+ letter-spacing: -0.03em;
+ line-height: 1.12;
+}
+
+.article h3 {
+ margin: 2.4rem 0 0.7rem;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.28rem, 2.2vw, 1.62rem);
+ font-weight: 650;
+ letter-spacing: -0.02em;
+ line-height: 1.22;
+}
+
+.article p,
+.article ul,
+.article ol,
+.article blockquote,
+.article table,
+.article pre,
+.article figure {
+ margin: 1rem 0;
+}
+
+.article p {
+ max-width: 68ch;
+}
+
+.article a {
+ color: var(--primary);
+ text-decoration: underline;
+ text-decoration-color: var(--link-underline);
+ text-underline-offset: 3px;
+}
+
+.article a:hover {
+ text-decoration-color: var(--primary);
+}
+
+.article ul,
+.article ol {
+ list-style-position: outside;
+ padding-left: 1.55rem;
+}
+
+.article ul {
+ list-style-type: disc;
+}
+
+.article ol {
+ list-style-type: decimal;
+}
+
+.article li {
+ margin: 0.4rem 0;
+ padding-left: 0.15rem;
+}
+
+.article li::marker {
+ color: var(--primary);
+}
+
+.article ul li::marker {
+ font-size: 0.92em;
+}
+
+.article ol li::marker {
+ font-weight: 700;
+}
+
+.article strong {
+ color: var(--fg);
+ font-weight: 700;
+}
+
+.article em {
+ color: var(--fg-muted);
+}
+
+.article blockquote {
+ padding: 0.95rem 1.1rem;
+ border-left: 3px solid var(--primary);
+ border-radius: 0 8px 8px 0;
+ background: rgba(116, 184, 226, 0.08);
+ color: var(--fg-muted);
+}
+
+.article table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.94rem;
+}
+
+.article th,
+.article td {
+ padding: 0.78rem 0.85rem;
+ border-bottom: 1px solid rgba(116, 184, 226, 0.14);
+ text-align: left;
+}
+
+.article th {
+ color: var(--primary);
+ font-weight: 700;
+}
+
+.article code {
+ border-radius: 0.3rem;
+ background: var(--inline-code-bg);
+ color: var(--inline-code-fg);
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.85em;
+ padding: 0.14em 0.35em;
+}
+
+.article pre {
+ overflow-x: auto;
+ padding: 1rem 1.25rem;
+ border: 1px solid rgba(116, 184, 226, 0.16);
+ border-radius: 8px;
+ background: var(--code-bg);
+ line-height: 1.55;
+}
+
+.article pre code {
+ background: none;
+ color: var(--code-fg);
+ font-size: 0.88rem;
+ padding: 0;
+}
+
+.article img,
+.article figure {
+ max-width: 100%;
+}
+
+.article figcaption {
+ margin-top: 0.5rem;
+ color: var(--fg-faint);
+ font-size: 0.86rem;
+}
+
+.articleFooter {
+ overflow: hidden;
+ background: var(--blog-band-bg);
+}
+
+.articleFooterWave {
+ height: 76px;
+ overflow: hidden;
+ background: var(--blog-band-bg);
+ pointer-events: none;
+}
+
+.articleFooterWave svg {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+
+.articleFooterWave path:first-child {
+ fill: var(--blog-shell-bg);
+}
+
+.articleFooterWave path:not(:first-child) {
+ stroke: rgba(116, 184, 226, 0.42);
+ stroke-width: 1.2;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+}
+
+.articleFooterWave path:nth-child(3) {
+ opacity: 0.54;
+}
+
+.articleFooterInner {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 1.55rem 40px 5rem;
+}
+
+.articleFooterHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 18px;
+ max-width: 980px;
+ margin-bottom: 1.15rem;
+}
+
+.listTitle {
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: clamp(1.28rem, 3vw, 1.8rem);
+ font-weight: 600;
+ letter-spacing: -0.03em;
+}
+
+.postGrid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 4px;
+ max-width: 980px;
+}
+
+.postCard {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ align-items: start;
+ gap: 16px;
+ padding: 0.76rem 0;
+ border-bottom: 0;
+ color: inherit;
+ text-decoration: none;
+ transition:
+ opacity 0.15s ease,
+ color 0.15s ease;
+}
+
+.postCardText {
+ display: grid;
+ gap: 4px;
+ min-width: 0;
+}
+
+.postCardTitle {
+ margin: 0;
+ color: var(--fg);
+ font-family: var(--font-heading), sans-serif;
+ font-size: 0.98rem;
+ font-weight: 600;
+ letter-spacing: -0.02em;
+ line-height: 1.22;
+}
+
+.postCardByline,
+.postCardDate {
+ color: var(--fg-faint);
+ font-size: 0.75rem;
+ line-height: 1.25;
+}
+
+.postCardByline {
+ display: block;
+ font-weight: 600;
+}
+
+.postCardDate {
+ white-space: nowrap;
+}
+
+.postCard:hover {
+ opacity: 0.72;
+}
+
+.postCard:hover .postCardTitle {
+ color: #d2e1ea;
+}
+
+.blogBottom {
+ display: flex;
+ justify-content: flex-start;
+ margin-top: 2.6rem;
+}
+
+.rssIconLink {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.5rem;
+ height: 2.5rem;
+ border: 1px solid rgba(116, 184, 226, 0.22);
+ border-radius: 999px;
+ background: rgba(12, 28, 42, 0.74);
+ color: var(--fg-muted);
+ text-decoration: none;
+ transition:
+ border-color 0.15s ease,
+ color 0.15s ease,
+ transform 0.15s ease;
+}
+
+.rssIconLink:hover {
+ border-color: rgba(116, 184, 226, 0.42);
+ color: var(--primary);
+ transform: translateY(-1px);
+}
+
+.rssIconLink svg {
+ width: 1.1rem;
+ height: 1.1rem;
+}
+
+@media (max-width: 1100px) {
+ .postLayout {
+ grid-template-columns: 240px minmax(0, 1fr);
+ gap: 48px;
+ }
+}
+
+@media (max-width: 960px) {
+ .blogHeroInner,
+ .postHeroInner {
+ padding: 92px 24px 66px;
+ }
+
+ .blogSection {
+ padding-right: 24px;
+ padding-left: 24px;
+ }
+
+ .sectionHeader {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .postLayout {
+ grid-template-columns: 1fr;
+ gap: 0;
+ padding: 2.4rem 24px 0;
+ }
+
+ .articleFooterInner {
+ padding-right: 24px;
+ padding-left: 24px;
+ }
+
+ .postSidebar {
+ display: none;
+ }
+
+ .mobileToc {
+ display: block;
+ }
+
+ .mobileAuthor {
+ display: block;
+ }
+
+ .article {
+ font-size: 1.02rem;
+ }
+}
+
+@media (max-width: 640px) {
+ .blogHeroInner,
+ .postHeroInner {
+ padding: 78px 20px 56px;
+ }
+
+ .blogSection,
+ .postLayout,
+ .articleFooterInner {
+ padding-right: 20px;
+ padding-left: 20px;
+ }
+
+ .blogTitle,
+ .postHeroTitle {
+ max-width: none;
+ font-size: clamp(2.55rem, 14vw, 4rem);
+ }
+
+ .blogSubtitle,
+ .postHeroDek {
+ margin-top: 22px;
+ }
+
+ .postListTopline {
+ grid-template-columns: 1fr;
+ gap: 8px;
+ }
+
+ .postListMeta {
+ justify-content: flex-start;
+ justify-self: start;
+ }
+
+ .featuredCard {
+ padding: 1.15rem;
+ }
+
+ .postGrid,
+ .postCard {
+ grid-template-columns: 1fr;
+ }
+
+ .postCard {
+ gap: 6px;
+ padding: 0.82rem 0;
+ }
+
+ .postCardDate {
+ white-space: normal;
+ }
+
+ .articleFooterWave {
+ height: 58px;
+ }
+}
diff --git a/web/components/docs/BannerLink.tsx b/web/components/docs/BannerLink.tsx
new file mode 100644
index 00000000..a20047c2
--- /dev/null
+++ b/web/components/docs/BannerLink.tsx
@@ -0,0 +1,51 @@
+import type { ReactNode } from 'react';
+import Link from 'next/link';
+import { ArrowRight, BookOpen, PlayCircle, Rocket } from 'lucide-react';
+
+import styles from './docs.module.css';
+
+function isInternalHref(href: string): boolean {
+ return href.startsWith('/') && !href.startsWith('//');
+}
+
+type BannerIcon = 'play' | 'rocket' | 'docs';
+
+interface BannerLinkProps {
+ href: string;
+ children: ReactNode;
+ icon?: BannerIcon;
+}
+
+const iconMap = {
+ play: PlayCircle,
+ rocket: Rocket,
+ docs: BookOpen,
+} satisfies Record;
+
+export function BannerLink({ href, children, icon = 'docs' }: BannerLinkProps) {
+ const Icon = iconMap[icon];
+
+ const inner = (
+ <>
+
+
+
+ {children}
+
+ >
+ );
+
+ if (isInternalHref(href)) {
+ return (
+
+ {inner}
+
+ );
+ }
+
+ return (
+
+ {inner}
+
+ );
+}
diff --git a/web/components/docs/Card.tsx b/web/components/docs/Card.tsx
new file mode 100644
index 00000000..75d38f5f
--- /dev/null
+++ b/web/components/docs/Card.tsx
@@ -0,0 +1,45 @@
+import type { ReactNode } from 'react';
+import Link from 'next/link';
+import { ArrowRight } from 'lucide-react';
+
+import styles from './docs.module.css';
+
+function isInternalHref(href: string): boolean {
+ return href.startsWith('/') && !href.startsWith('//');
+}
+
+interface CardProps {
+ title: string;
+ icon?: string;
+ href?: string;
+ children?: ReactNode;
+}
+
+export function Card({ title, href, children }: CardProps) {
+ const inner = (
+ <>
+
+ {children && {children}
}
+ >
+ );
+
+ if (href) {
+ if (isInternalHref(href)) {
+ return (
+
+ {inner}
+
+ );
+ }
+ return (
+
+ {inner}
+
+ );
+ }
+
+ return {inner}
;
+}
diff --git a/web/components/docs/CardGroup.tsx b/web/components/docs/CardGroup.tsx
new file mode 100644
index 00000000..8838cbcf
--- /dev/null
+++ b/web/components/docs/CardGroup.tsx
@@ -0,0 +1,16 @@
+import type { ReactNode } from 'react';
+
+import styles from './docs.module.css';
+
+interface CardGroupProps {
+ cols?: number;
+ children: ReactNode;
+}
+
+export function CardGroup({ cols = 2, children }: CardGroupProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/web/components/docs/CodeGroup.tsx b/web/components/docs/CodeGroup.tsx
new file mode 100644
index 00000000..138eb79f
--- /dev/null
+++ b/web/components/docs/CodeGroup.tsx
@@ -0,0 +1,108 @@
+'use client';
+
+import { useEffect, useState, type ReactNode, type ReactElement, Children, isValidElement } from 'react';
+
+import {
+ extractCodeFenceToken,
+ humanizeCodeFenceLanguage,
+ parseCodeFenceMetaToken,
+} from '../../lib/code-fence-meta';
+import { normalizeDocsLanguageLabel, useDocsLanguage } from './DocsLanguageContext';
+import styles from './docs.module.css';
+
+interface CodeGroupProps {
+ children: ReactNode;
+}
+
+interface CodeBlockProps {
+ children?: ReactNode;
+ className?: string;
+ 'data-label'?: string;
+}
+
+function findCodeElement(node: ReactNode): ReactElement | null {
+ if (!isValidElement(node)) {
+ return null;
+ }
+
+ if (node.type === 'code') {
+ return node;
+ }
+
+ for (const child of Children.toArray(node.props.children)) {
+ const found = findCodeElement(child);
+ if (found) {
+ return found;
+ }
+ }
+
+ return null;
+}
+
+function getLabel(block: ReactElement, index: number): string {
+ if (block.props['data-label']) {
+ return block.props['data-label'] as string;
+ }
+ const code = findCodeElement(block);
+ if (!code) return `Tab ${index + 1}`;
+ const token = extractCodeFenceToken(code.props.className || '');
+ if (!token) {
+ return `Tab ${index + 1}`;
+ }
+
+ const meta = parseCodeFenceMetaToken(token);
+ return meta.label ?? humanizeCodeFenceLanguage(meta.language);
+}
+
+export function CodeGroup({ children }: CodeGroupProps) {
+ const { language, setLanguage } = useDocsLanguage();
+ const rawBlocks = Children.toArray(children).filter((child): child is ReactElement =>
+ isValidElement(child)
+ );
+
+ if (rawBlocks.length <= 1) {
+ return <>{children}>;
+ }
+
+ const pairs = rawBlocks.map((block, i) => ({ block, label: getLabel(block, i) }));
+ const labels = pairs.map((p) => p.label);
+ const blocks = pairs.map((p) => p.block);
+ const preferredIndex = labels.findIndex((label) => normalizeDocsLanguageLabel(label) === language);
+ const isLanguageOnlyGroup = labels.every((label) => normalizeDocsLanguageLabel(label) !== null);
+ const [active, setActive] = useState(preferredIndex === -1 ? 0 : preferredIndex);
+
+ useEffect(() => {
+ if (preferredIndex !== -1) {
+ setActive(preferredIndex);
+ }
+ }, [preferredIndex]);
+
+ return (
+
+
+ {labels.map((label, i) => (
+ {
+ const nextLanguage = normalizeDocsLanguageLabel(label);
+ if (nextLanguage) {
+ setLanguage(nextLanguage);
+ }
+ setActive(i);
+ }}
+ >
+ {label}
+
+ ))}
+
+
{blocks[active]}
+
+ );
+}
diff --git a/web/components/docs/CopyCodeButton.tsx b/web/components/docs/CopyCodeButton.tsx
new file mode 100644
index 00000000..bccb1419
--- /dev/null
+++ b/web/components/docs/CopyCodeButton.tsx
@@ -0,0 +1,53 @@
+'use client';
+
+import { useState } from 'react';
+
+import s from './docs.module.css';
+
+export function CopyCodeButton({ code, inline = false }: { code: string; inline?: boolean }) {
+ const [copied, setCopied] = useState(false);
+
+ async function handleCopy() {
+ await navigator.clipboard.writeText(code);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ }
+
+ return (
+
+ {copied ? (
+
+
+
+ ) : (
+
+
+
+
+ )}
+
+ );
+}
diff --git a/web/components/docs/DocsLanguageContext.tsx b/web/components/docs/DocsLanguageContext.tsx
new file mode 100644
index 00000000..b50a36db
--- /dev/null
+++ b/web/components/docs/DocsLanguageContext.tsx
@@ -0,0 +1,68 @@
+'use client';
+
+import {
+ default as React,
+ createContext,
+ startTransition,
+ useContext,
+ useEffect,
+ useState,
+ type ReactNode,
+} from 'react';
+
+export type DocsLanguage = 'typescript' | 'python';
+
+type DocsLanguageContextValue = {
+ language: DocsLanguage;
+ setLanguage: (language: DocsLanguage) => void;
+};
+
+const STORAGE_KEY = 'agent-relay-docs-language';
+
+const DocsLanguageContext = createContext(null);
+
+export function normalizeDocsLanguageLabel(label: string): DocsLanguage | null {
+ const normalized = label.trim().toLowerCase();
+
+ if (normalized === 'typescript' || normalized === 'ts' || normalized === 'tsx') {
+ return 'typescript';
+ }
+
+ if (normalized === 'python' || normalized === 'py') {
+ return 'python';
+ }
+
+ return null;
+}
+
+export function DocsLanguageProvider({ children }: { children: ReactNode }) {
+ const [language, setLanguageState] = useState('typescript');
+
+ useEffect(() => {
+ const stored = window.localStorage.getItem(STORAGE_KEY);
+ if (stored === 'typescript' || stored === 'python') {
+ setLanguageState(stored);
+ }
+ }, []);
+
+ function setLanguage(nextLanguage: DocsLanguage) {
+ window.localStorage.setItem(STORAGE_KEY, nextLanguage);
+ startTransition(() => {
+ setLanguageState(nextLanguage);
+ });
+ }
+
+ return (
+ {children}
+ );
+}
+
+export function useDocsLanguage() {
+ const context = useContext(DocsLanguageContext);
+
+ if (!context) {
+ throw new Error('useDocsLanguage must be used within DocsLanguageProvider');
+ }
+
+ return context;
+}
diff --git a/web/components/docs/DocsNav.tsx b/web/components/docs/DocsNav.tsx
new file mode 100644
index 00000000..6113ea18
--- /dev/null
+++ b/web/components/docs/DocsNav.tsx
@@ -0,0 +1,169 @@
+'use client';
+
+import type { ComponentType } from 'react';
+import { useEffect, useRef } from 'react';
+import Link from 'next/link';
+import { usePathname } from 'next/navigation';
+import {
+ Activity,
+ BookOpen,
+ Bot,
+ Cloud,
+ Clock3,
+ Compass,
+ FolderOpen,
+ Hash,
+ Mail,
+ Network,
+ Plug,
+ PlayCircle,
+ Power,
+ Rocket,
+ Send,
+ Shield,
+ Smile,
+ Terminal,
+ Users,
+ Zap,
+} from 'lucide-react';
+import { BsChatRightText } from 'react-icons/bs';
+import { FaReact } from 'react-icons/fa';
+import { GrSwift } from 'react-icons/gr';
+import { PiBroadcastFill, PiLockKeyDuotone } from 'react-icons/pi';
+import { RiLayout5Line } from 'react-icons/ri';
+import { SiClaude, SiPython, SiTypescript } from 'react-icons/si';
+
+import { docsNav, legacyDocsNav } from '../../lib/docs-nav';
+import { getDocsVersionForPath, legacyDocsBasePath, v8DocsBasePath } from '../../lib/docs-versions';
+import { DocsVersionSelect } from './DocsVersionSelect';
+import styles from './docs.module.css';
+
+type NavIcon = ComponentType<{ className?: string; 'aria-hidden'?: boolean | 'true' | 'false' }>;
+
+const navIcons: Record = {
+ introduction: Compass,
+ quickstart: Rocket,
+ workspaces: Users,
+ messaging: Mail,
+ delivery: Network,
+ actions: Zap,
+ webhooks: Plug,
+ 'spawning-an-agent': Bot,
+ 'sending-messages': Send,
+ 'event-handlers': Activity,
+ channels: Hash,
+ dms: Mail,
+ threads: BsChatRightText,
+ 'emoji-reactions': Smile,
+ 'file-sharing': FolderOpen,
+ authentication: Shield,
+ permissions: PiLockKeyDuotone,
+ scheduling: Clock3,
+ cloud: Cloud,
+ workforce: Users,
+ 'proactive-agents': Zap,
+ harnesses: Bot,
+ 'session-capabilities': Shield,
+ runtime: Power,
+ 'agent-relay-mcp': Plug,
+ 'relay-dashboard': RiLayout5Line,
+ observer: PiBroadcastFill,
+ 'cli-overview': Terminal,
+ 'cli-broker-lifecycle': Power,
+ 'cli-agent-management': Bot,
+ 'cli-messaging': Send,
+ 'cli-cloud-commands': Cloud,
+ 'cli-on-the-relay': Plug,
+ 'reference-cli': BookOpen,
+ 'reference-broker-api': Network,
+ 'typescript-sdk': SiTypescript,
+ 'python-sdk': SiPython,
+ 'react-sdk': FaReact,
+ 'swift-sdk': GrSwift,
+ 'plugin-claude-code': SiClaude,
+ 'typescript-examples': PlayCircle,
+ migration: BookOpen,
+};
+
+export function DocsNav({ variant = 'sidebar' }: { variant?: 'sidebar' | 'mobileMenu' } = {}) {
+ const pathname = usePathname();
+ const navRef = useRef(null);
+ const isSidebar = variant === 'sidebar';
+ const docsVersion = getDocsVersionForPath(pathname ?? '/docs');
+ const navGroups = docsVersion === 'v7.1.1' ? legacyDocsNav : docsNav;
+ const docsBasePath = docsVersion === 'v8' ? v8DocsBasePath : legacyDocsBasePath;
+
+ useEffect(() => {
+ if (!isSidebar) return;
+
+ const nav = navRef.current;
+ const container = nav?.parentElement;
+ const docsBody = container?.parentElement;
+
+ if (!nav || !container || !docsBody) return;
+
+ const restingTop = docsBody.getBoundingClientRect().top;
+
+ const syncWheel = (event: WheelEvent) => {
+ if (event.deltaY === 0) return;
+
+ const target = event.target;
+ if (target instanceof Node && container.contains(target)) {
+ return;
+ }
+
+ const scrollMax = container.scrollHeight - container.clientHeight;
+ if (scrollMax <= 0) return;
+
+ const docsBodyRect = docsBody.getBoundingClientRect();
+ const atBoundary =
+ event.deltaY > 0 ? docsBodyRect.bottom <= window.innerHeight + 1 : docsBodyRect.top >= restingTop - 1;
+
+ if (!atBoundary) return;
+
+ const nextScrollTop = Math.max(0, Math.min(scrollMax, container.scrollTop + event.deltaY));
+ if (Math.abs(nextScrollTop - container.scrollTop) < 0.5) return;
+
+ container.scrollTop = nextScrollTop;
+ };
+
+ window.addEventListener('wheel', syncWheel, { passive: true });
+
+ return () => {
+ window.removeEventListener('wheel', syncWheel);
+ };
+ }, [isSidebar]);
+
+ return (
+
+ {!isSidebar && }
+ {navGroups.map((group) => (
+
+
{group.title}
+
+ {group.items.map((item) => {
+ const href = `${docsBasePath}/${item.slug}`;
+ const isActive =
+ pathname === href ||
+ (item.slug === 'introduction' &&
+ (pathname === '/docs' || pathname === legacyDocsBasePath || pathname === v8DocsBasePath));
+ const Icon = navIcons[item.slug] ?? BookOpen;
+ return (
+
+
+ {Icon && }
+ {item.title}
+
+
+ );
+ })}
+
+
+ ))}
+
+ );
+}
diff --git a/web/components/docs/DocsPageActions.tsx b/web/components/docs/DocsPageActions.tsx
new file mode 100644
index 00000000..66771c97
--- /dev/null
+++ b/web/components/docs/DocsPageActions.tsx
@@ -0,0 +1,244 @@
+'use client';
+
+import type React from 'react';
+import { useEffect, useId, useRef, useState } from 'react';
+import { Check, ChevronDown, ChevronUp, Copy, ExternalLink, Link2 } from 'lucide-react';
+import { SiClaude, SiOpenai } from 'react-icons/si';
+
+import styles from './docs.module.css';
+
+type DocsPageActionsProps = {
+ title: string;
+ pageUrl: string;
+ markdownPath: string;
+ markdownUrl: string;
+};
+
+type ActionItem = {
+ title: string;
+ href: string;
+ icon: React.ComponentType<{ className?: string }>;
+};
+
+function buildPrompt(title: string, pageUrl: string, markdownUrl: string): string {
+ return [
+ `I am reading the Agent Relay docs page "${title}".`,
+ 'Use this page as the source of truth and answer questions about it.',
+ '',
+ `Docs page: ${pageUrl}`,
+ `Markdown: ${markdownUrl}`,
+ ].join('\n');
+}
+
+async function copyToClipboard(text: string) {
+ if (navigator.clipboard?.writeText) {
+ try {
+ await navigator.clipboard.writeText(text);
+ return;
+ } catch {
+ // Fall through to a DOM-based copy path for browsers that block async clipboard writes.
+ }
+ }
+
+ const textarea = document.createElement('textarea');
+ textarea.value = text;
+ textarea.setAttribute('readonly', '');
+ textarea.style.position = 'fixed';
+ textarea.style.top = '0';
+ textarea.style.left = '0';
+ textarea.style.opacity = '0';
+ textarea.style.pointerEvents = 'none';
+ document.body.appendChild(textarea);
+ textarea.focus();
+ textarea.select();
+ textarea.setSelectionRange(0, textarea.value.length);
+
+ const copied = document.execCommand('copy');
+ document.body.removeChild(textarea);
+
+ if (!copied) {
+ throw new Error('Clipboard copy failed');
+ }
+}
+
+export function DocsPageActions({ title, pageUrl, markdownPath, markdownUrl }: DocsPageActionsProps) {
+ const [open, setOpen] = useState(false);
+ const [copiedAction, setCopiedAction] = useState<'markdown' | 'markdown-link' | null>(null);
+ const containerRef = useRef(null);
+ const menuId = useId();
+
+ useEffect(() => {
+ if (!open) return;
+
+ const handlePointerDown = (event: PointerEvent) => {
+ if (!containerRef.current?.contains(event.target as Node)) {
+ setOpen(false);
+ }
+ };
+
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === 'Escape') {
+ setOpen(false);
+ }
+ };
+
+ document.addEventListener('pointerdown', handlePointerDown);
+ document.addEventListener('keydown', handleKeyDown);
+ return () => {
+ document.removeEventListener('pointerdown', handlePointerDown);
+ document.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [open]);
+
+ useEffect(() => {
+ if (!copiedAction) return;
+
+ const timeoutId = window.setTimeout(() => setCopiedAction(null), 1800);
+ return () => window.clearTimeout(timeoutId);
+ }, [copiedAction]);
+
+ async function handleCopyMarkdown() {
+ try {
+ const response = await fetch(markdownPath, {
+ headers: { Accept: 'text/markdown' },
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to load markdown: ${response.status}`);
+ }
+
+ const markdown = (await response.text()).replace(/\r\n/g, '\n').trimEnd();
+ await copyToClipboard(`${markdown}\n`);
+ setCopiedAction('markdown');
+ setOpen(false);
+ } catch {
+ window.open(markdownPath, '_blank', 'noopener,noreferrer');
+ setOpen(false);
+ }
+ }
+
+ async function handleCopyMarkdownLink() {
+ try {
+ await copyToClipboard(markdownUrl);
+ setCopiedAction('markdown-link');
+ setOpen(false);
+ } catch {
+ window.open(markdownUrl, '_blank', 'noopener,noreferrer');
+ setOpen(false);
+ }
+ }
+
+ const prompt = encodeURIComponent(buildPrompt(title, pageUrl, markdownUrl));
+ const actionItems: ActionItem[] = [
+ {
+ title: 'Open in ChatGPT',
+ href: `https://chatgpt.com/?q=${prompt}`,
+ icon: SiOpenai,
+ },
+ {
+ title: 'Open in Claude',
+ href: `https://claude.ai/new?q=${prompt}`,
+ icon: SiClaude,
+ },
+ ];
+
+ return (
+
+
+
+
+ {copiedAction === 'markdown' ? 'Copied' : 'Copy Markdown'}
+
+ setOpen((value) => !value)}
+ aria-expanded={open}
+ aria-controls={menuId}
+ aria-haspopup="menu"
+ aria-label={open ? 'Close page actions' : 'Open page actions'}
+ >
+ {open ? (
+
+ ) : (
+
+ )}
+
+
+
+ {open && (
+
+ )}
+
+ );
+}
diff --git a/web/components/docs/DocsSearch.tsx b/web/components/docs/DocsSearch.tsx
new file mode 100644
index 00000000..9dbb385c
--- /dev/null
+++ b/web/components/docs/DocsSearch.tsx
@@ -0,0 +1,173 @@
+'use client';
+
+import { useCallback, useEffect, useRef, useState } from 'react';
+import { createPortal } from 'react-dom';
+import Link from 'next/link';
+import { useRouter } from 'next/navigation';
+
+import type { SearchEntry } from '../../lib/docs';
+import s from './docs-search.module.css';
+
+interface DocsSearchProps {
+ index: SearchEntry[];
+}
+
+function search(query: string, index: SearchEntry[]): SearchEntry[] {
+ if (!query.trim()) return [];
+ const q = query.toLowerCase();
+ const terms = q.split(/\s+/).filter(Boolean);
+
+ return index
+ .map((entry) => {
+ const haystack =
+ `${entry.title} ${entry.description} ${entry.headings.join(' ')} ${entry.body}`.toLowerCase();
+ let score = 0;
+ for (const term of terms) {
+ if (entry.title.toLowerCase().includes(term)) score += 10;
+ if (entry.description.toLowerCase().includes(term)) score += 5;
+ if (entry.headings.some((h) => h.toLowerCase().includes(term))) score += 3;
+ if (haystack.includes(term)) score += 1;
+ }
+ return { entry, score };
+ })
+ .filter((r) => r.score > 0)
+ .sort((a, b) => b.score - a.score)
+ .slice(0, 8)
+ .map((r) => r.entry);
+}
+
+export function DocsSearch({ index }: DocsSearchProps) {
+ const [open, setOpen] = useState(false);
+ const [query, setQuery] = useState('');
+ const [activeIdx, setActiveIdx] = useState(0);
+ const inputRef = useRef(null);
+ const router = useRouter();
+ const results = search(query, index);
+
+ const close = useCallback(() => {
+ setOpen(false);
+ setQuery('');
+ setActiveIdx(0);
+ }, []);
+
+ const navigate = useCallback(
+ (slug: string) => {
+ close();
+ router.push(`/docs/${slug}`);
+ },
+ [close, router]
+ );
+
+ // Reset active index when results change
+ useEffect(() => {
+ setActiveIdx(0);
+ }, [query]);
+
+ // Cmd+K / Ctrl+K shortcut
+ useEffect(() => {
+ function onKeyDown(e: KeyboardEvent) {
+ if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
+ e.preventDefault();
+ setOpen((prev) => !prev);
+ }
+ if (e.key === 'Escape') close();
+ }
+ window.addEventListener('keydown', onKeyDown);
+ return () => window.removeEventListener('keydown', onKeyDown);
+ }, [close]);
+
+ // Focus input when modal opens
+ useEffect(() => {
+ if (open) setTimeout(() => inputRef.current?.focus(), 50);
+ }, [open]);
+
+ function handleKeyDown(e: React.KeyboardEvent) {
+ if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ setActiveIdx((prev) => (prev + 1) % Math.max(results.length, 1));
+ } else if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ setActiveIdx((prev) => (prev - 1 + results.length) % Math.max(results.length, 1));
+ } else if (e.key === 'Enter' && results[activeIdx]) {
+ e.preventDefault();
+ navigate(results[activeIdx].slug);
+ }
+ }
+
+ return (
+ <>
+ setOpen(true)}>
+
+
+
+
+ Search docs...
+ ⌘K
+
+
+ {open &&
+ createPortal(
+
+
e.stopPropagation()} onKeyDown={handleKeyDown}>
+
+
+
+
+
+ setQuery(e.target.value)}
+ />
+
+ Esc
+
+
+
+ {query.trim() && (
+
+ {results.length === 0 ? (
+
No results for “{query}”
+ ) : (
+ results.map((entry, i) => (
+
close()}
+ onMouseEnter={() => setActiveIdx(i)}
+ >
+
{entry.title}
+ {entry.description &&
{entry.description} }
+
+ ))
+ )}
+
+ )}
+
+
,
+ document.body
+ )}
+ >
+ );
+}
diff --git a/web/components/docs/DocsVersionSelect.tsx b/web/components/docs/DocsVersionSelect.tsx
new file mode 100644
index 00000000..04bf29bf
--- /dev/null
+++ b/web/components/docs/DocsVersionSelect.tsx
@@ -0,0 +1,41 @@
+'use client';
+
+import { useId } from 'react';
+import { usePathname, useRouter } from 'next/navigation';
+import { ChevronDown } from 'lucide-react';
+
+import { docsVersions, getDocsVersionForPath, getDocsVersionHref } from '../../lib/docs-versions';
+import styles from './docs.module.css';
+
+export function DocsVersionSelect() {
+ const id = useId();
+ const pathname = usePathname() ?? '/docs';
+ const router = useRouter();
+ const activeVersion = getDocsVersionForPath(pathname);
+
+ return (
+
+
+ Version
+
+
+ {
+ router.push(getDocsVersionHref(event.target.value as typeof activeVersion, pathname));
+ }}
+ >
+ {docsVersions.map((version) => (
+
+ {version.label}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/web/components/docs/HighlightedCode.tsx b/web/components/docs/HighlightedCode.tsx
new file mode 100644
index 00000000..df1ff36c
--- /dev/null
+++ b/web/components/docs/HighlightedCode.tsx
@@ -0,0 +1,125 @@
+import React, { type ReactElement, Children, isValidElement } from 'react';
+import { SiPython, SiTypescript } from 'react-icons/si';
+
+import {
+ extractCodeFenceToken,
+ getCodeFenceBadgeLabel,
+ parseCodeFenceMetaToken,
+} from '../../lib/code-fence-meta';
+import { highlightCode } from '../../lib/syntax';
+import { CopyCodeButton } from './CopyCodeButton';
+import styles from './docs.module.css';
+
+const codeBadgeIcons: Record> = {
+ py: SiPython,
+ python: SiPython,
+ ts: SiTypescript,
+ tsx: SiTypescript,
+ typescript: SiTypescript,
+};
+
+function parseInlineStyle(styleText?: string): React.CSSProperties | undefined {
+ if (!styleText) {
+ return undefined;
+ }
+
+ const style: Record = {};
+
+ for (const declaration of styleText.split(';')) {
+ const separator = declaration.indexOf(':');
+ if (separator === -1) {
+ continue;
+ }
+
+ const property = declaration.slice(0, separator).trim();
+ const value = declaration.slice(separator + 1).trim();
+ if (!property || !value) {
+ continue;
+ }
+
+ if (property.startsWith('--')) {
+ style[property] = value;
+ continue;
+ }
+
+ const camelCased = property.replace(/-([a-z])/g, (_, character: string) => character.toUpperCase());
+ style[camelCased] = value;
+ }
+
+ return style as React.CSSProperties;
+}
+
+/**
+ * Server component that wraps ... with Shiki highlighting and a copy button.
+ */
+export async function HighlightedPre({ children, ...props }: React.ComponentPropsWithoutRef<'pre'>) {
+ const codeChild = Children.toArray(children).find(
+ (c): c is ReactElement<{ children?: React.ReactNode; className?: string }> =>
+ isValidElement(c) && c.type === 'code'
+ );
+
+ if (!codeChild) {
+ return {children} ;
+ }
+
+ const raw = typeof codeChild.props.children === 'string' ? codeChild.props.children : '';
+ if (!raw) {
+ return {children} ;
+ }
+
+ const className = codeChild.props.className || '';
+ const token = extractCodeFenceToken(className);
+
+ if (!token) {
+ return (
+
+ );
+ }
+
+ const meta = parseCodeFenceMetaToken(token);
+ const normalizedLanguage = meta.language.toLowerCase();
+ const highlighted = await highlightCode(raw.trimEnd(), normalizedLanguage);
+ const shikiStyle = parseInlineStyle(highlighted.preStyle);
+ const preClassName = [props.className, highlighted.preClassName].filter(Boolean).join(' ');
+ const wrapperClassName = meta.filename
+ ? `${styles.codeWrapper} ${styles.codeWrapperWithHeader}`
+ : styles.codeWrapper;
+ const normalizedLanguageClass = `language-${normalizedLanguage}`;
+ const codeClassName = Array.from(
+ new Set(className.split(/\s+/).filter(Boolean).concat(normalizedLanguageClass))
+ ).join(' ');
+ const badgeKey = meta.filename?.split('/').pop()?.split('.').pop()?.toLowerCase() || normalizedLanguage;
+ const BadgeIcon = codeBadgeIcons[badgeKey];
+
+ return (
+
+ {meta.filename ? (
+
+
+
+ {BadgeIcon ? (
+
+ ) : (
+ getCodeFenceBadgeLabel(meta.language, meta.filename)
+ )}
+
+ {meta.filename}
+
+
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+}
diff --git a/web/components/docs/LegacySpawnOptionsTable.tsx b/web/components/docs/LegacySpawnOptionsTable.tsx
new file mode 100644
index 00000000..fc44006b
--- /dev/null
+++ b/web/components/docs/LegacySpawnOptionsTable.tsx
@@ -0,0 +1,190 @@
+'use client';
+
+import { Fragment } from 'react';
+
+import { useDocsLanguage } from './DocsLanguageContext';
+
+type SpawnOptionsTableVariant = 'relay-startup' | 'common' | 'advanced';
+type SpawnOptionsLanguage = 'typescript' | 'python';
+type SpawnOptionDescription = string | Partial>;
+
+type SpawnOptionRow = {
+ typescript?: string[];
+ python?: string[];
+ description: SpawnOptionDescription;
+};
+
+const RELAY_STARTUP_ROWS: SpawnOptionRow[] = [
+ {
+ typescript: ['binaryPath'],
+ python: ['binary_path'],
+ description: 'Path to the agent-relay-broker binary. Auto-resolved if omitted.',
+ },
+ {
+ typescript: ['binaryArgs'],
+ python: ['binary_args'],
+ description: {
+ typescript: 'Extra args passed to `broker init` (for example `{ persist: true }`).',
+ python: 'Extra args passed to `broker init` (for example `["--persist"]`).',
+ },
+ },
+ {
+ typescript: ['brokerName'],
+ python: ['broker_name'],
+ description: 'Broker name. Defaults to the current working directory basename.',
+ },
+ {
+ typescript: ['channels'],
+ python: ['channels'],
+ description: 'Default channels for spawned agents.',
+ },
+ {
+ typescript: ['cwd'],
+ python: ['cwd'],
+ description: 'Working directory for the broker process.',
+ },
+ {
+ typescript: ['env'],
+ python: ['env'],
+ description: 'Environment variables for the broker process.',
+ },
+ {
+ typescript: ['onStderr'],
+ python: ['on_stderr'],
+ description: 'Forward broker stderr lines to this callback.',
+ },
+ {
+ typescript: ['startupTimeoutMs'],
+ python: ['startup_timeout_ms'],
+ description: 'Timeout in ms to wait for the broker to become ready. Defaults to `15000`.',
+ },
+ {
+ typescript: ['requestTimeoutMs'],
+ python: ['request_timeout_ms'],
+ description: 'Timeout in ms for HTTP requests to the broker. Defaults to `30000`.',
+ },
+];
+
+const COMMON_ROWS: SpawnOptionRow[] = [
+ {
+ typescript: ['name'],
+ python: ['name'],
+ description: {
+ typescript: 'Stable identity other agents can message. Defaults from `cli`.',
+ python: 'Stable identity other agents can message',
+ },
+ },
+ { typescript: ['cli'], description: 'CLI or named harness to spawn' },
+ {
+ typescript: ['runtime'],
+ description: 'Runtime category. Defaults to `pty`; set `headless` for app-server sessions',
+ },
+ { typescript: ['model'], python: ['model'], description: 'Model string or enum for the selected CLI' },
+ { typescript: ['task'], python: ['task'], description: 'Initial prompt for autonomous startup' },
+ { typescript: ['channels'], python: ['channels'], description: 'Rooms the agent joins on spawn' },
+ { typescript: ['args'], python: ['args'], description: 'Extra CLI arguments' },
+ { typescript: ['cwd'], python: ['cwd'], description: 'Per-agent working directory override' },
+ {
+ typescript: ['skipRelayPrompt'],
+ python: ['skip_relay_prompt'],
+ description: 'Skip MCP/protocol prompt injection when relay messaging is not needed',
+ },
+ {
+ typescript: ['onStart'],
+ python: ['on_start'],
+ description: 'Run code before spawn',
+ },
+ {
+ typescript: ['onSuccess'],
+ python: ['on_success'],
+ description: 'Run code after a successful spawn',
+ },
+ {
+ typescript: ['onError'],
+ python: ['on_error'],
+ description: 'Run code if spawn fails',
+ },
+];
+
+const ADVANCED_ROWS: SpawnOptionRow[] = [
+ {
+ typescript: ['team'],
+ python: ['team'],
+ description: 'Attach the agent to a team-aware workflow or relay-managed grouping',
+ },
+ {
+ typescript: ['shadowOf'],
+ python: ['shadow_of'],
+ description: 'Spawn a shadow worker linked to another agent',
+ },
+ {
+ typescript: ['shadowMode'],
+ python: ['shadow_mode'],
+ description: 'Configure shadow behavior',
+ },
+ {
+ typescript: ['idleThresholdSecs'],
+ python: ['idle_threshold_secs'],
+ description: 'Control when idle lifecycle events fire',
+ },
+ {
+ typescript: ['restartPolicy'],
+ python: ['restart_policy'],
+ description: 'Configure broker-managed restart behavior',
+ },
+];
+
+function getRows(variant: SpawnOptionsTableVariant): SpawnOptionRow[] {
+ if (variant === 'relay-startup') {
+ return RELAY_STARTUP_ROWS;
+ }
+
+ return variant === 'advanced' ? ADVANCED_ROWS : COMMON_ROWS;
+}
+
+function getOptionNames(row: SpawnOptionRow, language: SpawnOptionsLanguage): string[] {
+ return (language === 'python' ? row.python : row.typescript) ?? [];
+}
+
+function getDescription(row: SpawnOptionRow, language: SpawnOptionsLanguage): string {
+ if (typeof row.description === 'string') {
+ return row.description;
+ }
+
+ return row.description[language] ?? row.description.typescript ?? row.description.python ?? '';
+}
+
+export function LegacySpawnOptionsTable({ variant }: { variant: SpawnOptionsTableVariant }) {
+ const { language } = useDocsLanguage();
+ const rows = getRows(variant).filter((row) => getOptionNames(row, language).length > 0);
+
+ return (
+
+
+
+ Option
+ What it does
+
+
+
+ {rows.map((row) => {
+ const optionNames = getOptionNames(row, language);
+ const description = getDescription(row, language);
+ return (
+
+
+ {optionNames.map((name, index) => (
+
+ {index > 0 ? ', ' : null}
+ {name}
+
+ ))}
+
+ {description}
+
+ );
+ })}
+
+
+ );
+}
diff --git a/web/components/docs/Note.tsx b/web/components/docs/Note.tsx
new file mode 100644
index 00000000..98575545
--- /dev/null
+++ b/web/components/docs/Note.tsx
@@ -0,0 +1,11 @@
+import type { ReactNode } from 'react';
+
+import styles from './docs.module.css';
+
+interface NoteProps {
+ children: ReactNode;
+}
+
+export function Note({ children }: NoteProps) {
+ return {children}
;
+}
diff --git a/web/components/docs/TableOfContents.tsx b/web/components/docs/TableOfContents.tsx
new file mode 100644
index 00000000..a9b1e2f1
--- /dev/null
+++ b/web/components/docs/TableOfContents.tsx
@@ -0,0 +1,71 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+import type { TocItem } from '../../lib/docs';
+import { useDocsLanguage } from './DocsLanguageContext';
+import { DocsVersionSelect } from './DocsVersionSelect';
+import styles from './docs.module.css';
+
+export function TableOfContents({ items }: { items: TocItem[] }) {
+ const [activeId, setActiveId] = useState('');
+ const { language, setLanguage } = useDocsLanguage();
+
+ useEffect(() => {
+ const headings = items.map((item) => document.getElementById(item.id)).filter(Boolean) as HTMLElement[];
+
+ if (headings.length === 0) return;
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ for (const entry of entries) {
+ if (entry.isIntersecting) {
+ setActiveId(entry.target.id);
+ }
+ }
+ },
+ { rootMargin: '-80px 0px -60% 0px', threshold: 0 }
+ );
+
+ for (const el of headings) observer.observe(el);
+ return () => observer.disconnect();
+ }, [items]);
+
+ return (
+
+
+ Language
+
+ setLanguage(event.target.value as 'typescript' | 'python')}
+ >
+ TypeScript
+ Python
+
+
+
+
+
+ {items.length > 0 && (
+
+ On this page
+
+
+ )}
+
+ );
+}
diff --git a/web/components/docs/Warning.tsx b/web/components/docs/Warning.tsx
new file mode 100644
index 00000000..1fe23479
--- /dev/null
+++ b/web/components/docs/Warning.tsx
@@ -0,0 +1,11 @@
+import type { ReactNode } from 'react';
+
+import styles from './docs.module.css';
+
+interface WarningProps {
+ children: ReactNode;
+}
+
+export function Warning({ children }: WarningProps) {
+ return {children}
;
+}
diff --git a/web/components/docs/docs-search.module.css b/web/components/docs/docs-search.module.css
new file mode 100644
index 00000000..3e9ed407
--- /dev/null
+++ b/web/components/docs/docs-search.module.css
@@ -0,0 +1,168 @@
+/* ── Search trigger button ── */
+
+.trigger {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ width: 100%;
+ padding: 7px 14px;
+ border-radius: 100px;
+ border: 1px solid var(--nav-border);
+ background: var(--nav-surface);
+ color: var(--nav-muted);
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.8rem;
+ cursor: pointer;
+ transition:
+ border-color 0.2s,
+ background 0.2s;
+}
+
+.trigger:hover {
+ border-color: var(--nav-border-strong);
+ background: var(--nav-surface-hover);
+}
+
+.triggerText {
+ flex: 1;
+ text-align: left;
+}
+
+.triggerKbd {
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.68rem;
+ padding: 2px 6px;
+ border-radius: 4px;
+ border: 1px solid var(--nav-border);
+ background: color-mix(in srgb, var(--nav-surface) 85%, transparent);
+ color: var(--nav-subtle);
+}
+
+/* ── Modal overlay ── */
+
+.overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 200;
+ background: var(--overlay);
+ backdrop-filter: blur(4px);
+ -webkit-backdrop-filter: blur(4px);
+ display: flex;
+ align-items: flex-start;
+ justify-content: center;
+ padding-top: 12vh;
+ animation: fadeIn 0.15s ease;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+.modal {
+ width: 100%;
+ max-width: 560px;
+ border-radius: 14px;
+ background: var(--bg);
+ border: 1px solid var(--line);
+ box-shadow: var(--shadow-strong);
+ overflow: hidden;
+ animation: slideIn 0.15s ease;
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateY(-8px) scale(0.98);
+ }
+ to {
+ opacity: 1;
+ transform: none;
+ }
+}
+
+.inputRow {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 14px 16px;
+ border-bottom: 1px solid var(--line);
+ color: var(--fg-faint);
+}
+
+.input {
+ flex: 1;
+ border: none;
+ background: transparent;
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.95rem;
+ outline: none;
+}
+
+.input::placeholder {
+ color: var(--fg-faint);
+}
+
+.escKbd {
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.65rem;
+ padding: 3px 7px;
+ border-radius: 4px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg-faint);
+ cursor: pointer;
+}
+
+/* ── Results ── */
+
+.results {
+ max-height: 50vh;
+ overflow-y: auto;
+ padding: 6px;
+}
+
+.result {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ padding: 10px 12px;
+ border-radius: 8px;
+ text-decoration: none;
+ color: inherit;
+ transition: background 0.1s;
+ cursor: pointer;
+}
+
+.resultActive {
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+}
+
+.result:hover {
+ background: color-mix(in srgb, var(--primary) 6%, transparent);
+}
+
+.resultTitle {
+ font-size: 0.88rem;
+ font-weight: 600;
+ color: var(--fg);
+}
+
+.resultDesc {
+ font-size: 0.78rem;
+ color: var(--fg-faint);
+ line-height: 1.4;
+}
+
+.empty {
+ padding: 20px 16px;
+ text-align: center;
+ font-size: 0.88rem;
+ color: var(--fg-faint);
+ margin: 0;
+}
diff --git a/web/components/docs/docs.module.css b/web/components/docs/docs.module.css
new file mode 100644
index 00000000..cd237eab
--- /dev/null
+++ b/web/components/docs/docs.module.css
@@ -0,0 +1,1050 @@
+/* ── Docs — theme-aware ── */
+
+.docsPage {
+ min-height: 100vh;
+ background: var(--bg);
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ transition:
+ background 0.3s ease,
+ color 0.3s ease;
+}
+
+/* Code-block surface for the docs scope (dark only). Flows into ,
+ .shiki, code groups, and the tab header via the --code-bg token. */
+:global(html[data-theme='dark']) .docsPage {
+ --code-bg: #0b1c2a;
+}
+
+.docsBody {
+ --docs-rail-top-padding: 1.5rem;
+ --docs-content-top-padding: 2.75rem;
+ display: grid;
+ grid-template-columns: 260px 1fr;
+ max-width: 1280px;
+ margin: 0 auto;
+ min-height: calc(100vh - 70px);
+}
+
+.sidebarCol {
+ position: sticky;
+ top: 88px;
+ height: calc(100vh - 88px);
+ overflow-y: auto;
+ padding: var(--docs-rail-top-padding) 1.25rem 2rem;
+ border-right: 1px solid var(--line);
+}
+
+.sidebar {
+ /* DocsNav wrapper — no longer sticky itself since sidebarCol handles it */
+}
+
+.mobileSidebar {
+ display: flex;
+ flex-direction: column;
+ gap: 0.35rem;
+ margin: 0 0 0.35rem;
+ padding: 0 0 0.85rem;
+ border-bottom: 1px solid var(--nav-border);
+}
+
+.mobileSidebar .navGroup {
+ margin-bottom: 0.8rem;
+}
+
+.mobileSidebar .navGroup:last-child {
+ margin-bottom: 0;
+}
+
+.mobileSidebar .navGroupTitle {
+ color: var(--nav-subtle);
+ margin-bottom: 0.35rem;
+ padding-left: 0.9rem;
+}
+
+.mobileSidebar .navLink {
+ padding: 0.65rem 0.9rem;
+ border-radius: 0.6rem;
+ color: var(--nav-muted);
+}
+
+.mobileSidebar .navLink:hover {
+ background: var(--nav-surface);
+ color: var(--nav-fg);
+}
+
+.mobileSidebar .navLinkActive {
+ background: color-mix(in srgb, var(--primary) 12%, transparent);
+ color: var(--primary-hover);
+}
+
+.mobileSidebar .navIcon {
+ opacity: 0.9;
+}
+
+.mobileSidebar .versionControl {
+ margin: 0.45rem 0 0.95rem;
+ padding: 0.95rem 0.9rem 0;
+ border-top: 1px solid var(--nav-border);
+}
+
+.sidebarLogo {
+ display: none;
+}
+
+.content {
+ min-width: 0;
+ padding: var(--docs-content-top-padding) 3rem 5rem;
+}
+
+/* ── Article + TOC wrapper ── */
+
+.articleWrapper {
+ display: grid;
+ grid-template-columns: 1fr 200px;
+ align-items: start;
+ gap: 48px;
+ max-width: 56rem;
+}
+
+.tocSidebar {
+ position: sticky;
+ top: 100px;
+ align-self: start;
+ height: fit-content;
+ min-width: 0;
+ margin-top: calc(var(--docs-rail-top-padding) - var(--docs-content-top-padding));
+}
+
+.toc {
+ max-height: calc(100vh - 100px);
+ overflow-y: auto;
+}
+
+.tocControl {
+ margin-bottom: 0.95rem;
+}
+
+.tocSelectWrap {
+ position: relative;
+ display: block;
+}
+
+.tocSelectWrap::after {
+ content: '';
+ position: absolute;
+ top: 50%;
+ right: 0.65rem;
+ width: 0.4rem;
+ height: 0.4rem;
+ border-right: 1.5px solid var(--fg-muted);
+ border-bottom: 1.5px solid var(--fg-muted);
+ transform: translateY(-65%) rotate(45deg);
+ pointer-events: none;
+}
+
+.tocSelect {
+ width: 100%;
+ height: 2rem;
+ appearance: none;
+ -webkit-appearance: none;
+ padding: 0.35rem 1.7rem 0.35rem 0.65rem;
+ border: 1px solid var(--line);
+ border-radius: 999px;
+ background: transparent;
+ color: var(--fg-muted);
+ font: inherit;
+ font-size: 0.76rem;
+ font-weight: 600;
+ line-height: 1.2;
+ box-shadow: none;
+}
+
+.tocSelect:focus-visible {
+ outline: none;
+ border-color: color-mix(in srgb, var(--primary) 28%, var(--line));
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 18%, transparent);
+}
+
+.tocNav {
+ margin-top: 1.3rem;
+}
+
+.tocTitle {
+ font-size: 0.68rem;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--fg-faint);
+ margin: 0 0 8px;
+}
+
+.tocList {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.tocLink {
+ display: block;
+ padding: 3px 0;
+ font-size: 0.72rem;
+ line-height: 1.4;
+ color: var(--fg-faint);
+ text-decoration: none;
+ border-left: 2px solid transparent;
+ padding-left: 10px;
+ transition:
+ color 0.15s,
+ border-color 0.15s;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.tocLink:hover {
+ color: var(--primary);
+}
+
+.tocIndent {
+ padding-left: 20px;
+}
+
+.tocActive {
+ color: var(--primary);
+ border-left-color: var(--primary);
+}
+
+.versionControl {
+ margin: 0 0 1.25rem;
+}
+
+.versionLabel {
+ display: block;
+ font-size: 0.68rem;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--fg-faint);
+ margin: 0 0 8px;
+}
+
+.versionSelectWrap {
+ position: relative;
+ display: block;
+}
+
+.versionSelect {
+ width: 100%;
+ height: 2rem;
+ appearance: none;
+ -webkit-appearance: none;
+ border: 1px solid var(--line);
+ border-radius: 999px;
+ background: transparent;
+ color: var(--fg-muted);
+ font: inherit;
+ font-size: 0.76rem;
+ font-weight: 600;
+ line-height: 1.2;
+ padding: 0.35rem 1.7rem 0.35rem 0.65rem;
+ cursor: pointer;
+}
+
+.versionSelect:hover {
+ color: var(--fg-muted);
+}
+
+.versionSelect:focus-visible {
+ outline: none;
+ border-color: color-mix(in srgb, var(--primary) 28%, var(--line));
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 18%, transparent);
+}
+
+.versionChevron {
+ position: absolute;
+ top: 50%;
+ right: 0.65rem;
+ width: 0.8rem;
+ height: 0.8rem;
+ color: var(--fg-muted);
+ pointer-events: none;
+ transform: translateY(-50%);
+}
+
+.navGroup {
+ margin-bottom: 1.25rem;
+}
+
+.navGroupTitle {
+ font-size: 0.7rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--fg-faint);
+ margin-bottom: 0.4rem;
+ padding-left: 0.5rem;
+}
+
+.navList {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.navLink {
+ display: flex;
+ align-items: center;
+ gap: 0.55rem;
+ padding: 0.34rem 0.5rem;
+ border-radius: 0.4rem;
+ font-size: 0.88rem;
+ color: var(--fg-muted);
+ text-decoration: none;
+ transition:
+ background 0.15s,
+ color 0.15s;
+}
+
+.navIcon {
+ width: 0.92rem;
+ height: 0.92rem;
+ flex-shrink: 0;
+ color: currentColor;
+ opacity: 0.8;
+}
+
+.navLabel {
+ min-width: 0;
+}
+
+.navLink:hover {
+ background: color-mix(in srgb, var(--primary) 6%, transparent);
+ color: var(--primary);
+}
+
+.navLinkActive {
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
+ color: var(--primary);
+ font-weight: 600;
+}
+
+/* ── Article ── */
+
+.article {
+ line-height: 1.7;
+ color: var(--fg);
+ min-width: 0;
+ overflow: visible;
+}
+
+.articleHeader {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 1.5rem;
+ margin-bottom: 0.45rem;
+}
+
+.articleHeading {
+ min-width: 0;
+ flex: 1;
+}
+
+.articleBody {
+ min-width: 0;
+ overflow: hidden;
+}
+
+.article h1 {
+ font-size: clamp(1.8rem, 4vw, 2.4rem);
+ letter-spacing: -0.04em;
+ font-weight: 800;
+ line-height: 0.98;
+ color: var(--fg);
+ margin: 0 0 0.35rem;
+}
+
+.articleDescription {
+ color: var(--fg-muted);
+ font-size: 1.05rem;
+ margin: 0 0 1.5rem;
+}
+
+.pageActions {
+ position: relative;
+ flex-shrink: 0;
+ z-index: 3;
+}
+
+.pageActionButton {
+ display: inline-flex;
+ align-items: stretch;
+ border: 1px solid var(--line);
+ border-radius: 0.85rem;
+ background: color-mix(in srgb, var(--card-bg) 98%, transparent);
+ box-shadow: 0 3px 10px color-mix(in srgb, var(--fg) 4%, transparent);
+ overflow: hidden;
+}
+
+.pageActionPrimary,
+.pageActionToggle {
+ border: 0;
+ background: transparent;
+ color: var(--fg);
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ font: inherit;
+ font-size: 0.8rem;
+ cursor: pointer;
+ transition:
+ background 0.15s ease,
+ color 0.15s ease;
+}
+
+.pageActionPrimary {
+ padding: 0.34rem 0.62rem;
+ font-weight: 600;
+}
+
+.pageActionToggle {
+ padding: 0.34rem 0.5rem;
+ border-left: 1px solid var(--line);
+}
+
+.pageActionPrimary:hover,
+.pageActionToggle:hover {
+ background: color-mix(in srgb, var(--primary) 8%, var(--card-bg));
+ color: var(--primary);
+}
+
+.pageActionPrimary:focus-visible,
+.pageActionToggle:focus-visible,
+.pageActionItem:focus-visible {
+ outline: 2px solid color-mix(in srgb, var(--primary) 60%, white 40%);
+ outline-offset: 2px;
+}
+
+.pageActionPrimaryIcon,
+.pageActionChevron,
+.pageActionItemIcon,
+.pageActionExternal {
+ width: 0.9rem;
+ height: 0.9rem;
+ flex-shrink: 0;
+}
+
+.pageActionMenu {
+ position: absolute;
+ top: calc(100% + 0.75rem);
+ right: 0;
+ width: min(17.5rem, calc(100vw - 2rem));
+ padding: 0.35rem;
+ border: 1px solid var(--line);
+ border-radius: 1rem;
+ background: color-mix(in srgb, var(--card-bg) 96%, white 4%);
+ box-shadow: 0 12px 28px color-mix(in srgb, var(--fg) 10%, transparent);
+ backdrop-filter: blur(16px);
+}
+
+.pageActionItem {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.34rem 0.45rem;
+ border: 0;
+ border-radius: 0.65rem;
+ background: transparent;
+ color: inherit;
+ text-decoration: none;
+ text-align: left;
+ font: inherit;
+ cursor: pointer;
+ transition: background 0.15s ease;
+}
+
+.pageActionItem:hover {
+ background: color-mix(in srgb, var(--primary) 7%, transparent);
+}
+
+.pageActionIconFrame {
+ width: 1.35rem;
+ height: 1.35rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ border: 1px solid var(--line);
+ border-radius: 0.45rem;
+ background: color-mix(in srgb, var(--bg) 90%, var(--card-bg));
+ color: var(--fg-muted);
+}
+
+.pageActionCopyBody {
+ flex: 1;
+ min-width: 0;
+}
+
+.pageActionItemTitle {
+ display: flex;
+ align-items: center;
+ gap: 0.3rem;
+ width: 100%;
+ font-size: 0.8rem;
+ font-weight: 600;
+ line-height: 1.15;
+ color: var(--fg);
+}
+
+.pageActionExternal {
+ margin-left: auto;
+ opacity: 0.7;
+}
+
+.article h2 {
+ font-size: 1.35rem;
+ letter-spacing: -0.02em;
+ font-weight: 700;
+ color: var(--fg);
+ margin: 2.5rem 0 0.75rem;
+ padding-bottom: 0.45rem;
+ border-bottom: none;
+ /* Fading gradient hairline, matching the landing-page section rules. */
+ background-image: linear-gradient(
+ 90deg,
+ color-mix(in srgb, var(--primary) 42%, transparent),
+ color-mix(in srgb, var(--primary) 12%, transparent) 55%,
+ transparent
+ );
+ background-repeat: no-repeat;
+ background-position: 0 100%;
+ background-size: 100% 1px;
+}
+
+.article h3 {
+ font-size: 1.1rem;
+ font-weight: 600;
+ margin: 1.8rem 0 0.5rem;
+}
+
+.article hr {
+ height: 1px;
+ margin: 2.75rem 0;
+ border: 0;
+ background: linear-gradient(
+ 90deg,
+ transparent,
+ color-mix(in srgb, var(--primary) 32%, transparent) 50%,
+ transparent
+ );
+}
+.article p {
+ margin: 0.75rem 0;
+}
+
+.article a {
+ color: var(--primary);
+ text-decoration: underline;
+ text-decoration-color: var(--link-underline);
+ text-underline-offset: 2px;
+ transition: text-decoration-color 0.15s;
+}
+.article a:hover {
+ text-decoration-color: var(--primary);
+}
+
+.article .pageActionItem,
+.article .pageActionItem:hover {
+ color: inherit;
+ text-decoration: none;
+}
+
+.article ul,
+.article ol {
+ margin: 0.75rem 0;
+ padding-left: 1.5rem;
+}
+.article li {
+ margin: 0.3rem 0;
+}
+.article strong {
+ color: var(--fg);
+ font-weight: 600;
+}
+
+.article table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1rem 0;
+ font-size: 0.9rem;
+}
+.article th,
+.article td {
+ text-align: left;
+ padding: 0.5rem 0.75rem;
+ border-bottom: 1px solid var(--line);
+}
+.article th {
+ font-weight: 600;
+ color: var(--primary);
+}
+.article td {
+ color: var(--fg-muted);
+}
+
+.article code {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.85em;
+ background: var(--inline-code-bg);
+ color: var(--inline-code-fg);
+ padding: 0.15em 0.35em;
+ border-radius: 0.3rem;
+}
+
+.codeWrapper {
+ position: relative;
+ margin: 1rem 0;
+}
+
+.codeWrapperWithHeader {
+ overflow: hidden;
+ border-radius: 0.75rem;
+}
+
+.codeWrapper pre {
+ margin: 0;
+}
+
+.codeWrapperWithHeader pre {
+ border-top: none;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.codeBlockHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.85rem;
+ padding: 0.38rem 0.65rem;
+ border: 1px solid var(--line);
+ border-bottom: none;
+ border-radius: 0.75rem 0.75rem 0 0;
+ background: color-mix(in srgb, var(--surface-strong) 38%, var(--code-bg));
+}
+
+.codeBlockHeaderMeta {
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.codeBlockBadge {
+ width: 1.4rem;
+ height: 1.4rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ border-radius: 0.28rem;
+ background: color-mix(in srgb, var(--bg) 22%, var(--surface-strong));
+ color: var(--fg-muted);
+ font-size: 0.62rem;
+ font-weight: 800;
+ letter-spacing: 0.01em;
+}
+
+.codeBlockBadgeIcon {
+ width: 0.82rem;
+ height: 0.82rem;
+}
+
+.codeBlockFilename {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ color: var(--fg-muted);
+ font-size: 0.84rem;
+ font-weight: 600;
+}
+
+.copyBtn {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ z-index: 2;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border-radius: 6px;
+ border: 1px solid var(--line);
+ background: var(--card-bg);
+ color: var(--fg-faint);
+ cursor: pointer;
+ opacity: 1;
+ transition:
+ opacity 0.15s,
+ color 0.15s,
+ background 0.15s;
+}
+
+.copyBtnInline {
+ position: static;
+ top: auto;
+ right: auto;
+ z-index: auto;
+ flex-shrink: 0;
+ width: 28px;
+ height: 28px;
+ border-color: transparent;
+ background: transparent;
+}
+
+.copyBtn:hover {
+ color: var(--primary);
+ background: var(--bg);
+}
+
+.copyBtnInline:hover {
+ color: var(--fg);
+ background: color-mix(in srgb, var(--surface) 65%, transparent);
+}
+
+.article pre {
+ margin: 0;
+ padding: 1rem 1.25rem;
+ border-radius: 0.75rem;
+ background: var(--code-bg);
+ border: 1px solid var(--line);
+ overflow-x: auto;
+ line-height: 1.5;
+}
+
+.article pre code {
+ background: none;
+ padding: 0;
+ font-size: 0.85rem;
+ color: var(--code-fg);
+}
+
+/* ── CodeGroup ── */
+
+.codeGroup {
+ margin: 1rem 0;
+ border: 1px solid var(--line);
+ border-radius: 0.75rem;
+ overflow: hidden;
+ background: var(--code-bg);
+}
+
+.codeGroupTabs {
+ display: flex;
+ background: var(--nav-bg);
+ border-bottom: 1px solid var(--nav-border);
+}
+
+.codeGroupTabsHidden {
+ display: none;
+}
+
+.codeGroupTab {
+ padding: 0.5rem 1rem;
+ font-size: 0.8rem;
+ font-weight: 600;
+ font-family: inherit;
+ color: var(--nav-fg);
+ opacity: 0.55;
+ background: none;
+ border: none;
+ border-bottom: 2px solid transparent;
+ cursor: pointer;
+ transition:
+ opacity 0.15s,
+ border-color 0.15s;
+}
+.codeGroupTab:hover {
+ opacity: 0.8;
+}
+.codeGroupTabActive {
+ opacity: 1;
+ border-bottom-color: var(--nav-fg);
+}
+.codeGroupPanel {
+ background: var(--code-bg);
+}
+
+.codeGroupPanel .codeWrapper {
+ margin: 0;
+}
+
+.codeGroupPanel .codeWrapperWithHeader {
+ border-radius: 0;
+}
+
+.codeGroupPanel .codeBlockHeader {
+ border: none;
+ border-bottom: 1px solid var(--line);
+ border-radius: 0;
+}
+
+.codeGroupPanel pre {
+ margin: 0;
+ border: none;
+ border-radius: 0;
+ background: transparent;
+}
+
+/* ── Banner Link ── */
+
+.bannerLink {
+ width: 100%;
+ min-height: 48px;
+ display: flex;
+ align-items: center;
+ gap: 0.9rem;
+ margin: 1.35rem 0 1.55rem;
+ padding: 0rem 0.95rem;
+ border: 1.5px solid #234969;
+ border-radius: 999px;
+ background: #2d6a9c;
+ color: var(--primary-fg);
+ text-decoration: none;
+ box-shadow: none;
+ transition:
+ background 0.2s ease,
+ border-color 0.2s ease,
+ transform 0.15s ease,
+ color 0.15s ease;
+}
+
+.article .bannerLink,
+.article .bannerLink:hover {
+ color: var(--primary-fg);
+ text-decoration: none;
+}
+
+.bannerLink:hover {
+ background: #4a90c2;
+ border-color: #234969;
+ transform: translateY(-1px);
+ box-shadow: none;
+}
+
+.bannerLinkIconWrap {
+ width: 2rem;
+ height: 2rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--primary-fg) 12%, transparent);
+ color: inherit;
+}
+
+.bannerLinkIcon {
+ width: 1rem;
+ height: 1rem;
+}
+
+.bannerLinkText {
+ flex: 1;
+ min-width: 0;
+ font-size: 0.92rem;
+ font-weight: 600;
+ letter-spacing: 0.01em;
+ line-height: 1.35;
+}
+
+.bannerLinkArrow {
+ width: 1rem;
+ height: 1rem;
+ flex-shrink: 0;
+ color: inherit;
+ opacity: 0.86;
+}
+
+/* ── Cards ── */
+
+.cardGroup {
+ display: grid;
+ gap: 0.95rem;
+ margin: 1.25rem 0;
+}
+
+/* Flat, refined dark panels — a faint top sheen and a hairline cool-blue
+ border, matching the event-feed surfaces. Colour is reserved for tiny
+ semantic accents, not the card body. */
+.card {
+ position: relative;
+ display: block;
+ min-height: 100%;
+ padding: 1rem 1.15rem 0.95rem;
+ border: 1px solid var(--card-border);
+ border-radius: 0.7rem;
+ background: linear-gradient(180deg, color-mix(in srgb, var(--card-bg) 94%, white 6%), var(--card-bg));
+ text-decoration: none;
+ color: inherit;
+ box-shadow:
+ inset 0 1px 0 color-mix(in srgb, white 5%, transparent),
+ 0 1px 3px color-mix(in srgb, black 16%, transparent);
+ transition:
+ border-color 0.15s ease,
+ box-shadow 0.15s ease,
+ transform 0.15s ease;
+}
+
+.card:hover {
+ border-color: var(--card-hover-border);
+ box-shadow:
+ inset 0 1px 0 color-mix(in srgb, white 8%, transparent),
+ 0 4px 14px color-mix(in srgb, black 22%, transparent);
+ transform: translateY(-1px);
+}
+
+.article .card,
+.article .card:hover {
+ color: inherit;
+ text-decoration: none;
+}
+
+.cardHeader {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 0.85rem;
+ margin-bottom: 0.5rem;
+}
+
+.cardTitle {
+ font-size: 1.02rem;
+ font-weight: 700;
+ color: var(--fg);
+ line-height: 1.25;
+ margin: 0 !important;
+}
+
+.cardLinkIcon {
+ width: 0.95rem;
+ height: 0.95rem;
+ flex-shrink: 0;
+ color: var(--fg-faint);
+ transition:
+ transform 0.15s ease,
+ color 0.15s ease;
+}
+
+.card:hover .cardLinkIcon {
+ color: var(--primary);
+ transform: translateX(2px);
+}
+
+.cardBody {
+ position: relative;
+ z-index: 1;
+ font-size: 0.92rem;
+ color: var(--fg-muted);
+ margin: 0;
+ line-height: 1.55;
+}
+.cardBody p {
+ margin: 0;
+}
+
+/* ── Note ── */
+
+.note {
+ margin: 1rem 0;
+ padding: 0.85rem 1rem;
+ border-left: 3px solid var(--primary);
+ border-radius: 0 0.5rem 0.5rem 0;
+ background: var(--note-bg);
+ font-size: 0.92rem;
+ color: var(--fg-muted);
+}
+.note p {
+ margin: 0.3rem 0;
+}
+.note p:first-child {
+ margin-top: 0;
+}
+.note p:last-child {
+ margin-bottom: 0;
+}
+
+.warning {
+ margin: 1rem 0;
+ padding: 0.85rem 1rem;
+ border-left: 3px solid var(--warning, #e5a00d);
+ border-radius: 0 0.5rem 0.5rem 0;
+ background: color-mix(in srgb, var(--warning, #e5a00d) 8%, var(--bg));
+ font-size: 0.92rem;
+ color: var(--fg-muted);
+}
+.warning p {
+ margin: 0.3rem 0;
+}
+.warning p:first-child {
+ margin-top: 0;
+}
+.warning p:last-child {
+ margin-bottom: 0;
+}
+
+/* ── Responsive ── */
+
+@media (max-width: 1100px) {
+ .articleWrapper {
+ grid-template-columns: 1fr;
+ }
+ .tocSidebar {
+ display: none;
+ }
+ .articleHeader {
+ flex-direction: column;
+ align-items: stretch;
+ }
+ .pageActions {
+ align-self: flex-start;
+ }
+}
+
+@media (max-width: 900px) {
+ .docsBody {
+ grid-template-columns: 1fr;
+ }
+ .sidebarCol {
+ display: none;
+ }
+ .content {
+ padding: 1.5rem 1rem 3rem;
+ }
+ .bannerLink {
+ padding: 0.9rem 0.95rem;
+ gap: 0.75rem;
+ }
+ .cardGroup {
+ grid-template-columns: 1fr !important;
+ }
+ .pageActionButton {
+ width: 100%;
+ }
+ .pageActionPrimary {
+ flex: 1;
+ justify-content: center;
+ }
+ .pageActionMenu {
+ left: 0;
+ right: auto;
+ width: min(22rem, calc(100vw - 2rem));
+ }
+}
diff --git a/web/components/github-stars.module.css b/web/components/github-stars.module.css
new file mode 100644
index 00000000..288e6c41
--- /dev/null
+++ b/web/components/github-stars.module.css
@@ -0,0 +1,57 @@
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ padding: 6px 14px;
+ background: var(--nav-surface);
+ color: var(--nav-fg);
+ border: 1px solid var(--nav-border);
+ border-radius: 100px;
+ text-decoration: none;
+ font-size: 0.82rem;
+ font-weight: 500;
+ animation: throb 3s ease-in-out infinite;
+ transition:
+ background 0.25s,
+ border-color 0.25s,
+ color 0.25s;
+}
+
+.meta {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+}
+
+.badge:hover {
+ background: var(--nav-surface-hover);
+ border-color: var(--nav-border-strong);
+ color: var(--nav-link-hover);
+ animation: none;
+ transform: scale(1.04);
+}
+
+@keyframes throb {
+ 0%,
+ 100% {
+ border-color: var(--nav-border);
+ transform: scale(1);
+ box-shadow: 0 0 0 0 transparent;
+ }
+ 50% {
+ border-color: var(--nav-border-strong);
+ transform: scale(1.04);
+ box-shadow: 0 0 12px color-mix(in srgb, var(--nav-fg) 10%, transparent);
+ }
+}
+
+.divider {
+ width: 1px;
+ height: 14px;
+ background: currentColor;
+ opacity: 0.3;
+}
+
+.count {
+ font-variant-numeric: tabular-nums;
+}
diff --git a/web/components/home/A2AFeature.tsx b/web/components/home/A2AFeature.tsx
new file mode 100644
index 00000000..2320ef12
--- /dev/null
+++ b/web/components/home/A2AFeature.tsx
@@ -0,0 +1,94 @@
+import { FadeIn } from '../FadeIn';
+import s from '../../app/landing.module.css';
+
+/** Syntax-highlight kinds mapped to the editor's CSS module classes. */
+type TokenKind = 'muted' | 'fn' | 'var' | 'kw' | 'str';
+
+const TOKEN_CLASS: Record = {
+ muted: s.codeMuted,
+ fn: s.codeFunction,
+ var: s.codeVariable,
+ kw: s.codeKeyword,
+ str: s.codeString,
+};
+
+/**
+ * An A2A Agent Card, expressed as ordered tokens. Plain segments (including
+ * whitespace/newlines) carry no kind; highlighted segments name the span color.
+ * Rendered into a `pre` whose `white-space: pre-wrap` preserves the spacing here.
+ */
+const AGENT_CARD_TOKENS: ReadonlyArray<{ text: string; kind?: TokenKind }> = [
+ { text: '// Any A2A client can discover and call this agent ', kind: 'muted' },
+ { text: '\n{\n ' },
+ { text: '"name"', kind: 'var' },
+ { text: ': ' },
+ { text: '"Scout"', kind: 'str' },
+ { text: ',\n ' },
+ { text: '"protocolVersion"', kind: 'var' },
+ { text: ': ' },
+ { text: '"0.3.0"', kind: 'str' },
+ { text: ',\n ' },
+ { text: '"url"', kind: 'var' },
+ { text: ': ' },
+ { text: '"https://relay.dev/a2a/scout"', kind: 'str' },
+ { text: ',\n ' },
+ { text: '"capabilities"', kind: 'var' },
+ { text: ': { ' },
+ { text: '"streaming"', kind: 'var' },
+ { text: ': ' },
+ { text: 'true', kind: 'kw' },
+ { text: ' },\n ' },
+ { text: '"skills"', kind: 'var' },
+ { text: ': [{ ' },
+ { text: '"id"', kind: 'var' },
+ { text: ': ' },
+ { text: '"triage"', kind: 'str' },
+ { text: ', ' },
+ { text: '"tags"', kind: 'var' },
+ { text: ': [' },
+ { text: '"routing"', kind: 'str' },
+ { text: '] }]\n}' },
+];
+
+export function A2AFeature() {
+ return (
+
+
+
+
+
+
+
+ agent-card.json
+
+
+
+ {AGENT_CARD_TOKENS.map(({ text, kind }, i) =>
+ kind ? (
+
+ {text}
+
+ ) : (
+ {text}
+ )
+ )}
+
+
+
+
+
+
Speaks A2A out of the box
+
+ Every Relay agent publishes an A2A Agent Card, so any A2A client can discover and call it.
+
+ Bridge agents built on other frameworks over the open Agent2Agent protocol — no custom glue.
+
+
+ A2A tasks, messages, and streaming updates map onto Relay channels and threads automatically.
+
+ Standard endpoints in, durable Relay delivery out, with the full chat history preserved.
+
+
+
+ );
+}
diff --git a/web/components/home/AgentToolsFeature.tsx b/web/components/home/AgentToolsFeature.tsx
new file mode 100644
index 00000000..d56487a1
--- /dev/null
+++ b/web/components/home/AgentToolsFeature.tsx
@@ -0,0 +1,117 @@
+import MCP from '@lobehub/icons/es/MCP';
+import { SquareTerminal } from 'lucide-react';
+
+import { FadeIn } from '../FadeIn';
+import s from '../../app/landing.module.css';
+
+/** Syntax-highlight kinds mapped to the editor's CSS module classes. */
+type TokenKind = 'muted' | 'fn' | 'var' | 'kw' | 'str';
+
+const TOKEN_CLASS: Record = {
+ muted: s.codeMuted,
+ fn: s.codeFunction,
+ var: s.codeVariable,
+ kw: s.codeKeyword,
+ str: s.codeString,
+};
+
+/**
+ * The orchestrator.ts snippet, expressed as ordered tokens. Plain segments
+ * (including whitespace/newlines) carry no kind; highlighted segments name the
+ * span color. Rendered into a `pre` whose `white-space: pre-wrap` preserves the
+ * literal spacing here. Mirrors the SDK's `registerAction` API from the README.
+ */
+const EDITOR_TOKENS: ReadonlyArray<{ text: string; kind?: TokenKind }> = [
+ { text: '// Expose a tool to agents via MCP + CLI ', kind: 'muted' },
+ { text: '\nrelay.' },
+ { text: 'registerAction', kind: 'fn' },
+ { text: '({\n name: ' },
+ { text: '"submit-vote"', kind: 'str' },
+ { text: ',\n input: z.' },
+ { text: 'object', kind: 'fn' },
+ { text: '({ vote: z.' },
+ { text: 'enum', kind: 'fn' },
+ { text: '([' },
+ { text: '"yes"', kind: 'str' },
+ { text: ', ' },
+ { text: '"no"', kind: 'str' },
+ { text: ']) }),\n handler: ' },
+ { text: 'async', kind: 'kw' },
+ { text: ' ({ agent, input }) => {\n ' },
+ { text: 'await', kind: 'kw' },
+ { text: ' ' },
+ { text: 'recordVote', kind: 'fn' },
+ { text: '(agent.' },
+ { text: 'name', kind: 'var' },
+ { text: ', input.' },
+ { text: 'vote', kind: 'var' },
+ { text: ');\n },\n});\n\n' },
+ { text: '// Run a callback when it completes ', kind: 'muted' },
+ { text: '\nrelay.' },
+ { text: 'addListener', kind: 'fn' },
+ { text: '(relay.' },
+ { text: 'action', kind: 'fn' },
+ { text: '(' },
+ { text: '"submit-vote"', kind: 'str' },
+ { text: ').' },
+ { text: 'completed', kind: 'fn' },
+ { text: '(), tally);' },
+];
+
+export function AgentToolsFeature() {
+ return (
+
+
+
Agent Tools for Structured Work
+
+ Register actions for agents and callbacks for results via the SDK
+ Expose CLI and MCP tools so agents can communicate progress back to the SDK.
+
+ Require approvals, validate inputs, and return structured results instead of free-form guesses.
+
+ Keep action updates attached to the right channel, thread, and workflow state.
+
+
+
+
+
+ MCP
+ The Agent Relay MCP exposes tool calls you define via the SDK that you can define callbacks for.
+
+
+
+
+
+ CLI
+ The Agent Relay CLI exposes actions you define via the SDK as terminal commands the agent can
+ use and you can define callbacks for.
+
+
+
+
+
+
+
+
+
+
+ orchestrator.ts
+
+
+
+ {EDITOR_TOKENS.map(({ text, kind }, i) =>
+ kind ? (
+
+ {text}
+
+ ) : (
+ {text}
+ )
+ )}
+
+
+
+
+
+ );
+}
diff --git a/web/components/home/ContextCapabilities.tsx b/web/components/home/ContextCapabilities.tsx
new file mode 100644
index 00000000..525bed05
--- /dev/null
+++ b/web/components/home/ContextCapabilities.tsx
@@ -0,0 +1,90 @@
+import { FadeIn } from '../FadeIn';
+import { RealtimeEventFeed } from '../../app/RealtimeEventFeed';
+import { SearchPreviewAnimation } from '../../app/SearchPreviewAnimation';
+import s from '../../app/landing.module.css';
+import { WaveDivider } from './icons';
+
+const WEBHOOK_SNIPPET = `curl -X POST \\
+ https://api.agentrelay.com/v1/webhooks \\
+ -H "Content-Type: application/json" \\
+ -d '{"channel":"#alerts","text":"Deploy finished"}'`;
+
+/**
+ * The "build the right context" band: real-time events, webhooks, and search.
+ * Rendered inside {@link DeliveryFeature} so it stays grouped with the delivery
+ * story, matching the original DOM nesting.
+ */
+export function ContextCapabilities() {
+ return (
+
+
+
+
+ Easy to build the right context
+
+
+ Agents are only as good as the context you give them. Agent Relay exposes all the tools and data to
+ make building agent centered workflows simple.
+
+
+
+
+
+
+
Real-time events
+
+ WebSocket stream for live events. Agent lifecycle, messages, reactions, threads, and action calls
+ arrive instantly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ terminal
+
+
+
+ $
+ {WEBHOOK_SNIPPET}
+
+
+
+
+
+
Webhooks
+
+ Create a webhook, get a URL, POST to it from GitHub Actions, Sentry, PagerDuty, or any service.
+ Messages appear in your channel instantly.
+
+
+
+
+
+
+
+
Search
+
+ Search messages, threads, channels, and agent history so teams can recover context without asking
+ humans to summarize it again.
+
+
+
+
+ );
+}
diff --git a/web/components/home/DeliveryFeature.tsx b/web/components/home/DeliveryFeature.tsx
new file mode 100644
index 00000000..fe8a0b5b
--- /dev/null
+++ b/web/components/home/DeliveryFeature.tsx
@@ -0,0 +1,97 @@
+import { AgentToolLogo, type AgentTool } from '../AgentToolLogos';
+import { FadeIn } from '../FadeIn';
+import { DurableDeliveryTimeline } from '../../app/DurableDeliveryTimeline';
+import s from '../../app/landing.module.css';
+import { ContextCapabilities } from './ContextCapabilities';
+import { OpenClawLogo } from './icons';
+
+/** `openclaw` has no entry in {@link AgentTool}, so it renders a bespoke mark. */
+type DeliveryProvider = AgentTool | 'openclaw';
+
+interface DeliveryRow {
+ dot: string;
+ provider: DeliveryProvider;
+ name: string;
+ /** [delivered, pending, retry, fail] counts shown in the dashboard. */
+ stats: readonly [number, number, number, number];
+}
+
+const DELIVERY_ROWS: readonly DeliveryRow[] = [
+ { dot: '#28c840', provider: 'claude', name: 'Scout', stats: [58, 0, 0, 0] },
+ { dot: '#9CA3AF', provider: 'codex', name: 'Designer', stats: [51, 0, 1, 0] },
+ { dot: '#28c840', provider: 'openclaw', name: 'QA', stats: [46, 1, 0, 0] },
+ { dot: '#28c840', provider: 'claude', name: 'Planner', stats: [42, 2, 0, 0] },
+ { dot: '#28c840', provider: 'gemini', name: 'Builder', stats: [37, 0, 1, 0] },
+ { dot: '#febc2e', provider: 'codex', name: 'Reviewer', stats: [29, 3, 2, 1] },
+ { dot: '#9CA3AF', provider: 'copilot', name: 'Ops', stats: [18, 4, 0, 0] },
+];
+
+function DeliveryRowIcon({ provider }: { provider: DeliveryProvider }) {
+ if (provider === 'openclaw') {
+ return ;
+ }
+ return (
+
+ );
+}
+
+export function DeliveryFeature() {
+ return (
+
+
+
+
+
+
+
+ agent
+ msg
+ pending
+ retry
+ fail
+
+
+ {DELIVERY_ROWS.map((row) => (
+
+
+
+
+ {row.name}
+
+
+ {row.stats.map((stat, i) => (
+
+ {stat}
+
+ ))}
+
+
+ ))}
+
+
+
+
+
The hard parts of delivery, handled
+
+ Durable delivery so channel history and offline catch-up survive restarts.
+
+ Receipts, retry queues, and backoff keep handoffs moving until every target agent acknowledges.
+
+
+ Stateful coordination stays close to active channels for fast reads, writes, and thread updates.
+
+
+ A global edge network places channels near agents while keeping ordering and membership
+ consistent.
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/home/Deploy.tsx b/web/components/home/Deploy.tsx
new file mode 100644
index 00000000..db83415f
--- /dev/null
+++ b/web/components/home/Deploy.tsx
@@ -0,0 +1,64 @@
+import type { ComponentType } from 'react';
+
+import { FadeIn } from '../FadeIn';
+import s from '../../app/landing.module.css';
+import { CloudIcon, MonitorIcon } from './icons';
+
+interface DeployOption {
+ href: string;
+ label: string;
+ Icon: ComponentType;
+ title: string;
+ text: string;
+}
+
+const DEPLOY_OPTIONS: DeployOption[] = [
+ {
+ href: 'https://github.com/agentworkforce/relay/blob/main/docs/self-hosting/README.md',
+ label: 'Read the Agent Relay self-hosting README on GitHub',
+ Icon: MonitorIcon,
+ title: 'Self host',
+ text: 'For teams that need complete control.',
+ },
+ {
+ href: 'https://agentrelay.com/cloud',
+ label: 'Open Agent Relay hosted cloud',
+ Icon: CloudIcon,
+ title: 'Hosted cloud',
+ text: 'For teams that just want to build.',
+ },
+];
+
+export function Deploy() {
+ return (
+
+
+ Open source from day one
+
+ Use the open-source engine in your own infrastructure, or let us run it for you with a generous free
+ tier.
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/home/Hero.tsx b/web/components/home/Hero.tsx
new file mode 100644
index 00000000..6e326fc7
--- /dev/null
+++ b/web/components/home/Hero.tsx
@@ -0,0 +1,45 @@
+import Link from 'next/link';
+
+import { HeroGraph } from './HeroGraph';
+import s from '../../app/landing.module.css';
+import { GitHubIcon, HeroBackdrop } from './icons';
+
+export function Hero() {
+ return (
+
+
+
+
+
Let your agents talk
+
+
+ Give Claude, Codex or any other agent DMs, channels and a searchable chat history. Build your
+ multi-agent system without worrying about the glue.
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/home/HeroGraph.tsx b/web/components/home/HeroGraph.tsx
new file mode 100644
index 00000000..d0a27e63
--- /dev/null
+++ b/web/components/home/HeroGraph.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+import { MessageRelayAnimation } from '../MessageRelayAnimation';
+
+// Matches the mobile breakpoint in landing.module.css where .heroRight is hidden.
+const MOBILE_QUERY = '(max-width: 600px)';
+
+type MediaQueryListWithLegacyListeners = MediaQueryList & {
+ addListener?: (listener: () => void) => void;
+ removeListener?: (listener: () => void) => void;
+};
+
+function subscribeToMediaQuery(mql: MediaQueryList, listener: () => void) {
+ if (typeof mql.addEventListener === 'function') {
+ mql.addEventListener('change', listener);
+ return () => mql.removeEventListener('change', listener);
+ }
+
+ const legacyMql = mql as MediaQueryListWithLegacyListeners;
+ legacyMql.addListener?.(listener);
+ return () => legacyMql.removeListener?.(listener);
+}
+
+/**
+ * Renders the hero's animated node graph only above the mobile breakpoint.
+ *
+ * The graph is a canvas animation with a continuous requestAnimationFrame loop,
+ * so on mobile we avoid mounting it entirely (rather than just hiding it with
+ * CSS) to keep the page lightweight on phones.
+ */
+export function HeroGraph() {
+ const [isDesktop, setIsDesktop] = useState(false);
+
+ useEffect(() => {
+ const mql = window.matchMedia(MOBILE_QUERY);
+ const update = () => setIsDesktop(!mql.matches);
+
+ update();
+ return subscribeToMediaQuery(mql, update);
+ }, []);
+
+ if (!isDesktop) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/web/components/home/HowItWorks.tsx b/web/components/home/HowItWorks.tsx
new file mode 100644
index 00000000..264b813a
--- /dev/null
+++ b/web/components/home/HowItWorks.tsx
@@ -0,0 +1,148 @@
+import type { ReactNode } from 'react';
+
+import ClaudeCode from '@lobehub/icons/es/ClaudeCode';
+import Codex from '@lobehub/icons/es/Codex';
+import Cursor from '@lobehub/icons/es/Cursor';
+import Github from '@lobehub/icons/es/Github';
+import Grok from '@lobehub/icons/es/Grok';
+import HermesAgent from '@lobehub/icons/es/HermesAgent';
+import OpenClaw from '@lobehub/icons/es/OpenClaw';
+import OpenCode from '@lobehub/icons/es/OpenCode';
+
+import { FadeIn } from '../FadeIn';
+import s from '../../app/landing.module.css';
+import { PythonLogo, TypeScriptLogo } from './icons';
+
+const ICON_SIZE = 24;
+
+interface LogoItem {
+ key: string;
+ label: string;
+ node: ReactNode;
+}
+
+/** CLI coding agents that Relay drives over a PTY. */
+const CLI_LOGOS: readonly LogoItem[] = [
+ { key: 'claude-code', label: 'Claude Code', node: },
+ { key: 'codex', label: 'Codex', node: },
+ { key: 'opencode', label: 'OpenCode', node: },
+ { key: 'cursor', label: 'Cursor', node: },
+ { key: 'grok', label: 'Grok', node: },
+];
+
+/** Custom agents you build against the SDK and language bindings. */
+const CUSTOM_LOGOS: readonly LogoItem[] = [
+ { key: 'hermes', label: 'Hermes', node: },
+ { key: 'openclaw', label: 'OpenClaw', node: },
+ { key: 'typescript', label: 'TypeScript', node: },
+ { key: 'python', label: 'Python', node: },
+ { key: 'github', label: 'GitHub', node: },
+];
+
+/**
+ * Pyramid layout: the Agent Relay hub sits at the top; the two agent groups
+ * sit below it. A fixed-height connector zone between them carries the
+ * orthogonal pipes (relay → bus → each group) and the animated messages.
+ * Coordinates are percentages of that connector zone, so the pipes and dots
+ * line up at every width. Three message dots ({@link MESSAGE_CLASSES}) travel
+ * the pipes up to the relay and back down to another agent (CLI → custom,
+ * custom → CLI, CLI → CLI).
+ */
+// Apex coords (% of the relay+connector zone): relay bottom 50%, bus 75%,
+// card tops 100%. The pipes connect the relay box down to each agent group.
+const WIRES: readonly string[] = [
+ '50,50 50,75', // relay down to the bus
+ '25,75 75,75', // bus across
+ '25,75 25,100', // bus down to CLI agents
+ '75,75 75,100', // bus down to custom agents
+];
+
+const MESSAGE_CLASSES = [s.howMsg1, s.howMsg2, s.howMsg3] as const;
+
+function AgentGroup({
+ label,
+ logos,
+ caption,
+}: {
+ label: string;
+ logos: readonly LogoItem[];
+ caption: string;
+}) {
+ return (
+
+
{label}
+
+ {logos.map((logo) => (
+
+ {logo.node}
+
+ ))}
+
+
{caption}
+
+ );
+}
+
+export function HowItWorks() {
+ return (
+
+
+
+ Works with Every Agent
+
+
+ It's not a harness, and it's not a framework. Our PTY based driver can power any CLI agent or you
+ can drop in our SDK for your custom orchestrator.
+
+
+
+
+ {/* Apex zone = relay box (top) + connector (below). Wires and message
+ dots span the whole zone so a dot can travel up behind the relay
+ box and pop back out. */}
+
+
+ {WIRES.map((points, i) => (
+
+ ))}
+
+
+ {MESSAGE_CLASSES.map((cls, i) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/home/MessagingFeature.tsx b/web/components/home/MessagingFeature.tsx
new file mode 100644
index 00000000..ca20831b
--- /dev/null
+++ b/web/components/home/MessagingFeature.tsx
@@ -0,0 +1,32 @@
+import { ChannelMessagesPreview } from '../ChannelMessagesPreview';
+import { FadeIn } from '../FadeIn';
+import s from '../../app/landing.module.css';
+import { ScribbleUnderline } from './icons';
+
+export function MessagingFeature() {
+ return (
+
+
+
+
+
+ The
+
+ {' '}
+ real-time messaging SDK
+
+
+ Channels and messages to coordinate work in shared spaces.
+ Threads and reactions to keep decisions attached to the right context.
+ DMs and @mentions to route handoffs to the right agent.
+ Searchable history so agents can recover decisions without asking humans.
+
+
+
+ );
+}
diff --git a/web/components/home/QuickStart.tsx b/web/components/home/QuickStart.tsx
new file mode 100644
index 00000000..e9372309
--- /dev/null
+++ b/web/components/home/QuickStart.tsx
@@ -0,0 +1,66 @@
+import Link from 'next/link';
+
+import { AGENT_TOOL_LABELS, AGENT_TOOLS, AgentToolLogo } from '../AgentToolLogos';
+import { AgentSetupPrompt, InstallCommand } from '../InstallCommand';
+import s from '../../app/landing.module.css';
+
+export function QuickStart() {
+ return (
+
+
+
+
+
+
+ Quick start
+
+
+
+ {AGENT_TOOLS.map((provider) => (
+
+
+
+ ))}
+
+ Works with the harnesses you already love or integrate your own.
+
+
+
+
+
+ Human or agent, sometimes it's just easier to start building with stuff to figure out if
+ it's useful. Fortunately, we've made that really easy for both.
+
+
+
+
+
+
+
+
+ Hand this to an agent
+
+ The hosted skill explains how to start a workspace, choose orchestrator or participant mode, and
+ get workers communicating on Relay.
+
+
+
+ Open skill
+
+
+
+
+ );
+}
diff --git a/web/components/home/Waitlist.tsx b/web/components/home/Waitlist.tsx
new file mode 100644
index 00000000..d60ba1cb
--- /dev/null
+++ b/web/components/home/Waitlist.tsx
@@ -0,0 +1,22 @@
+import { WaitlistForm } from '../WaitlistForm';
+import s from '../../app/landing.module.css';
+
+export function Waitlist() {
+ return (
+
+
+
+
+ Be the first to know
+
+
+ Join the waitlist for early access when we release new products.
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/home/WorksWithEveryAgent.tsx b/web/components/home/WorksWithEveryAgent.tsx
new file mode 100644
index 00000000..339f921c
--- /dev/null
+++ b/web/components/home/WorksWithEveryAgent.tsx
@@ -0,0 +1,28 @@
+import { AGENT_TOOL_LABELS, AGENT_TOOLS, AgentToolLogo } from '../AgentToolLogos';
+import { FadeIn } from '../FadeIn';
+import s from '../../app/landing.module.css';
+
+export function WorksWithEveryAgent() {
+ return (
+
+
+
+ Works with every agent
+
+ It's not a harness, and it's not a framework. You can plug in directly with our first class
+ adapters or you can define your own.
+
+
+
+ {AGENT_TOOLS.map((provider) => (
+
+
+
{AGENT_TOOL_LABELS[provider]}
+
+ ))}
+
+ or any other agent that you hook up
+
+
+ );
+}
diff --git a/web/components/home/icons.tsx b/web/components/home/icons.tsx
new file mode 100644
index 00000000..7c3893d1
--- /dev/null
+++ b/web/components/home/icons.tsx
@@ -0,0 +1,313 @@
+import s from '../../app/landing.module.css';
+
+/**
+ * Decorative and brand SVGs used across the landing page.
+ *
+ * These stay inline (rather than living in `/public/*.svg`) on purpose:
+ * the dividers and underline inherit their stroke from CSS via `currentColor`,
+ * and the hero/decorative marks sit above the fold where an extra network
+ * request would cause a visible flash. Componentizing them keeps the markup
+ * DRY and the section components readable without an external request.
+ */
+
+export function GitHubIcon({ size = 18 }: { size?: number }) {
+ return (
+
+
+
+ );
+}
+
+/** Self-host / monitor glyph for the deploy cards. */
+export function MonitorIcon() {
+ return (
+
+
+
+
+
+ );
+}
+
+/** Hosted-cloud glyph for the deploy cards. */
+export function CloudIcon() {
+ return (
+
+
+
+ );
+}
+
+export function OpenClawLogo({ className }: { className: string }) {
+ return (
+
+
+
+
+
+
+ );
+}
+
+/** Official two-tone Python mark for the custom-agent bindings card. */
+export function PythonLogo({ className }: { className: string }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+/** TypeScript mark — blue rounded tile with the canonical "TS" lockup. */
+export function TypeScriptLogo({ className }: { className: string }) {
+ return (
+
+
+
+ TS
+
+
+ );
+}
+
+/** Hand-drawn underline that sits under a single highlighted word in a heading. */
+export function ScribbleUnderline() {
+ return (
+
+
+
+
+ );
+}
+
+/**
+ * Full-bleed wavy line that separates landing sections.
+ * `feature` is the thin two-stroke rule between feature blocks; `capability`
+ * is the taller three-stroke band that opens the context-capabilities group.
+ */
+export function WaveDivider({
+ variant,
+ className,
+}: {
+ variant: 'feature' | 'capability' | 'a2a';
+ /** Extra class merged onto the wrapper — used to reposition via grid `order`. */
+ className?: string;
+}) {
+ if (variant === 'a2a') {
+ // A livelier take on the feature wave: a gradient strand that runs blue →
+ // pear-green → light-blue, a soft glow, an extra ghost strand, and a gentle
+ // drift (see `.a2aSeparatorWaves` in landing.module.css). Tilts the opposite
+ // way from the plain separators so it reads as deliberately different.
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ if (variant === 'capability') {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+}
+
+/**
+ * Decorative communication-graph and contour backdrop behind the hero.
+ * Inline so it paints with the first server-rendered HTML — no extra request
+ * and no above-the-fold flash.
+ */
+export function HeroBackdrop() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/components/home/index.ts b/web/components/home/index.ts
new file mode 100644
index 00000000..f9fb2ebc
--- /dev/null
+++ b/web/components/home/index.ts
@@ -0,0 +1,12 @@
+export { Hero } from './Hero';
+export { HowItWorks } from './HowItWorks';
+export { MessagingFeature } from './MessagingFeature';
+export { WorksWithEveryAgent } from './WorksWithEveryAgent';
+export { DeliveryFeature } from './DeliveryFeature';
+export { ContextCapabilities } from './ContextCapabilities';
+export { AgentToolsFeature } from './AgentToolsFeature';
+export { A2AFeature } from './A2AFeature';
+export { Deploy } from './Deploy';
+export { QuickStart } from './QuickStart';
+export { Waitlist } from './Waitlist';
+export { WaveDivider } from './icons';
diff --git a/web/components/node-relay.module.css b/web/components/node-relay.module.css
new file mode 100644
index 00000000..63be58a0
--- /dev/null
+++ b/web/components/node-relay.module.css
@@ -0,0 +1,186 @@
+.container {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ min-height: 420px;
+ background: transparent;
+ overflow: hidden;
+}
+
+.canvas {
+ position: absolute;
+ inset: 0;
+ z-index: 0;
+ pointer-events: none;
+}
+
+/* ══════════════════════════════════════
+ Terminal Card
+ ══════════════════════════════════════ */
+
+.card {
+ position: absolute;
+ z-index: 1;
+ width: 160px;
+ height: 88px;
+ padding: 0;
+ background: var(--agent-card-bg);
+ backdrop-filter: blur(10px);
+ border: 1px solid var(--agent-card-border);
+ border-radius: 8px;
+ overflow: hidden;
+ transition:
+ box-shadow 0.4s ease,
+ opacity 0.2s ease-out,
+ transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
+ border-color 0.3s ease,
+ background 0.3s ease;
+}
+
+.cardActive {
+ border-color: var(--agent-card-border-active);
+}
+
+.cardHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 5px 8px;
+ background: var(--agent-card-header);
+ border-bottom: 1px solid var(--agent-card-border);
+ gap: 4px;
+ transition:
+ background 0.3s ease,
+ border-color 0.3s ease;
+}
+
+.cardIdentity {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.providerLogo {
+ width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+}
+
+.cardName {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.62rem;
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ color: var(--agent-card-title);
+ white-space: nowrap;
+ transition: color 0.3s ease;
+}
+
+.cardModel {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.5rem;
+ font-weight: 400;
+ letter-spacing: 0.02em;
+ color: var(--agent-card-muted);
+ white-space: nowrap;
+ transition: color 0.3s ease;
+}
+
+.cardStatus {
+ display: flex;
+ align-items: flex-start;
+ gap: 5px;
+ padding: 8px 8px 10px;
+ min-height: 36px;
+}
+
+.statusDot {
+ width: 4px;
+ height: 4px;
+ margin-top: 3px;
+ border-radius: 50%;
+ background: var(--agent-card-status);
+ flex-shrink: 0;
+ animation: statusPulse 1s ease-in-out infinite;
+ box-shadow: 0 0 4px var(--agent-card-glow);
+}
+
+@keyframes statusPulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.3;
+ }
+}
+
+.statusText {
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.55rem;
+ font-weight: 400;
+ letter-spacing: 0.01em;
+ line-height: 1.4;
+ color: var(--agent-card-muted);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ transition: color 0.3s ease;
+}
+
+.statusActive {
+ color: var(--agent-card-title);
+}
+
+/* ── Responsive ── */
+
+@media (max-width: 1100px) {
+ .card {
+ width: 140px;
+ height: 78px;
+ }
+ .cardName {
+ font-size: 0.58rem;
+ }
+ .cardModel {
+ font-size: 0.46rem;
+ }
+ .statusText {
+ font-size: 0.5rem;
+ }
+}
+
+@media (max-width: 900px) {
+ .container {
+ min-height: 340px;
+ }
+ .card {
+ width: 110px;
+ height: 62px;
+ border-radius: 6px;
+ }
+ .providerLogo {
+ width: 10px;
+ height: 10px;
+ }
+ .cardName {
+ font-size: 0.54rem;
+ }
+ .cardModel {
+ font-size: 0.42rem;
+ }
+ .cardHeader {
+ padding: 4px 6px;
+ }
+ .cardStatus {
+ padding: 5px 6px 7px;
+ min-height: 28px;
+ }
+ .statusText {
+ font-size: 0.46rem;
+ }
+ .statusDot {
+ width: 3px;
+ height: 3px;
+ margin-top: 2px;
+ }
+}
diff --git a/web/components/sdk-code.module.css b/web/components/sdk-code.module.css
new file mode 100644
index 00000000..c5fc1d87
--- /dev/null
+++ b/web/components/sdk-code.module.css
@@ -0,0 +1,66 @@
+.codeBlock {
+ border-radius: 12px;
+ overflow: hidden;
+ background: var(--code-bg);
+ border: 1px solid var(--line);
+}
+
+.tabs {
+ display: flex;
+ gap: 0;
+ padding: 0 16px;
+ background: var(--nav-bg);
+ border-bottom: 1px solid var(--nav-border);
+}
+
+.tab {
+ padding: 10px 16px;
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.82rem;
+ font-weight: 500;
+ color: var(--nav-fg);
+ opacity: 0.55;
+ background: none;
+ border: none;
+ border-bottom: 2px solid transparent;
+ cursor: pointer;
+ transition:
+ opacity 0.2s,
+ border-color 0.2s;
+}
+
+.tab:hover {
+ opacity: 0.8;
+}
+
+.tabActive {
+ opacity: 1;
+ border-bottom-color: var(--nav-fg);
+}
+
+.pre {
+ margin: 0;
+ padding: 24px;
+ overflow-x: auto;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 0.85rem;
+ line-height: 1.7;
+ color: var(--code-fg);
+ background: transparent;
+ border: none;
+ border-radius: 0;
+}
+
+/* Syntax — uses primary for keywords/types */
+.keyword {
+ color: var(--primary);
+}
+.string {
+ color: var(--syntax-string);
+}
+.type {
+ color: var(--primary);
+}
+.method {
+ color: var(--syntax-method);
+}
diff --git a/web/components/site-footer.module.css b/web/components/site-footer.module.css
new file mode 100644
index 00000000..25a21ba4
--- /dev/null
+++ b/web/components/site-footer.module.css
@@ -0,0 +1,145 @@
+.footer {
+ background: var(--footer-bg);
+ color: var(--footer-fg);
+ padding: 64px 40px 32px;
+ transition: background 0.3s ease;
+}
+
+.inner {
+ display: grid;
+ grid-template-columns: 1fr 3fr;
+ gap: 48px;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding-bottom: 48px;
+ border-bottom: 1px solid var(--footer-line);
+}
+
+.brand {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ text-decoration: none;
+ color: var(--footer-fg);
+}
+
+.logo svg {
+ height: 22px;
+ width: auto;
+ color: var(--footer-fg);
+}
+
+.logo svg path,
+.logo svg rect {
+ fill: currentColor;
+}
+
+.tagline {
+ font-size: 0.88rem;
+ color: var(--footer-muted);
+ line-height: 1.5;
+ max-width: 28ch;
+ margin: 0;
+}
+
+.columns {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 32px;
+}
+
+.col {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.colTitle {
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--footer-faint);
+ margin: 0 0 4px;
+}
+
+.link {
+ font-size: 0.88rem;
+ color: var(--footer-muted);
+ text-decoration: none;
+ transition: color 0.15s;
+}
+
+.link:hover {
+ color: var(--footer-fg);
+}
+
+.bottom {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding-top: 24px;
+}
+
+.bottomMeta {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ flex-wrap: wrap;
+}
+
+.copy {
+ font-size: 0.78rem;
+ color: var(--footer-faint);
+ margin: 0;
+}
+
+.socials {
+ display: flex;
+ gap: 16px;
+}
+
+.social {
+ color: var(--footer-faint);
+ transition: color 0.15s;
+}
+
+.social:hover {
+ color: var(--footer-fg);
+}
+
+@media (max-width: 960px) {
+ .footer {
+ padding: 48px 24px 24px;
+ }
+
+ .inner {
+ grid-template-columns: 1fr;
+ gap: 36px;
+ }
+
+ .bottom {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 18px;
+ }
+
+ .bottomMeta {
+ width: 100%;
+ }
+}
+
+@media (max-width: 560px) {
+ .columns {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 28px 24px;
+ }
+}
diff --git a/web/components/site-nav.module.css b/web/components/site-nav.module.css
new file mode 100644
index 00000000..bc8c215e
--- /dev/null
+++ b/web/components/site-nav.module.css
@@ -0,0 +1,353 @@
+.navOuter {
+ position: sticky;
+ top: 12px;
+ z-index: 50;
+ padding: 0 24px;
+}
+
+.navBar {
+ max-width: 1280px;
+ margin: 0 auto;
+ background: var(--nav-solid-bg);
+ border: 1px solid var(--nav-border);
+ border-radius: 14px;
+ transition:
+ border-color 0.2s ease,
+ box-shadow 0.2s ease;
+ color: var(--nav-fg);
+ box-shadow:
+ 0 8px 24px var(--nav-shadow),
+ inset 0 1px 0 var(--nav-inset);
+}
+
+.navBarScrolled {
+ background: linear-gradient(
+ 135deg,
+ color-mix(in srgb, var(--nav-bg) 78%, transparent) 0%,
+ color-mix(in srgb, var(--nav-bg) 88%, transparent) 100%
+ );
+ backdrop-filter: blur(24px) saturate(2) brightness(1.05);
+ -webkit-backdrop-filter: blur(24px) saturate(2) brightness(1.05);
+ border-color: var(--nav-border);
+ box-shadow:
+ 0 10px 32px var(--nav-shadow),
+ inset 0 1px 0 var(--nav-inset);
+}
+
+.nav {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 24px;
+}
+
+.navCenter {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ max-width: 360px;
+ margin: 0 auto;
+}
+
+.navRight {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+}
+
+.actions {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.logo {
+ color: var(--nav-fg);
+ text-decoration: none;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.logoIcon {
+ width: 34px;
+ height: 28px;
+ flex-shrink: 0;
+ color: var(--nav-logo-mark);
+}
+
+.logoWordmark {
+ height: 24px;
+ width: auto;
+ color: var(--nav-logo-wordmark);
+}
+
+.links {
+ display: flex;
+ align-items: center;
+ gap: 28px;
+ list-style: none;
+}
+
+.agentLink {
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.72rem;
+ font-weight: 500;
+ color: inherit;
+ text-decoration: none;
+ opacity: 0.85;
+ padding: 5px 12px;
+ border: 1px solid var(--nav-border);
+ border-radius: 6px;
+ transition:
+ opacity 0.2s,
+ border-color 0.2s;
+}
+
+.agentLink:hover {
+ opacity: 1;
+ border-color: var(--nav-border-strong);
+}
+
+.link {
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.78rem;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: inherit;
+ text-decoration: none;
+ padding-bottom: 4px;
+ border-bottom: 2.5px solid transparent;
+ transition:
+ color 0.2s,
+ opacity 0.2s,
+ border-color 0.2s;
+ opacity: 1;
+}
+
+.link:hover {
+ color: var(--nav-link-hover);
+ border-bottom-color: currentColor;
+}
+
+@media (max-width: 960px) {
+ .navOuter {
+ padding: 0 10px;
+ top: 8px;
+ }
+
+ .navBar {
+ border-radius: 10px;
+ }
+
+ .nav {
+ padding: 8px 14px;
+ }
+
+ .logoIcon {
+ width: 32px;
+ height: 26px;
+ }
+
+ .logoWordmark {
+ height: 22px;
+ }
+
+ .links {
+ display: none;
+ }
+
+ .navCenter {
+ display: none;
+ }
+
+ .navRight {
+ display: none;
+ }
+}
+
+/* ── Hamburger (hidden on desktop, shown on mobile) ── */
+
+.hamburger {
+ display: none;
+ align-items: center;
+ justify-content: center;
+ width: 36px;
+ height: 36px;
+ border: none;
+ background: transparent;
+ color: inherit;
+ cursor: pointer;
+ border-radius: 8px;
+ transition: background 0.15s;
+}
+
+.hamburger:hover {
+ background: var(--nav-surface);
+}
+
+@media (max-width: 960px) {
+ .hamburger {
+ display: flex;
+ }
+}
+
+/* ── Mobile menu dropdown ── */
+
+.mobileMenu {
+ display: flex;
+ flex-direction: column;
+ padding: 6px 14px 16px;
+ gap: 4px;
+ max-height: min(72vh, calc(100vh - 88px));
+ overflow-y: auto;
+ overscroll-behavior: contain;
+}
+
+.mobileLink {
+ display: block;
+ padding: 12px 14px;
+ border-radius: 8px;
+ font-size: 1.0625rem;
+ font-weight: 500;
+ color: inherit;
+ text-decoration: none;
+ opacity: 0.85;
+ transition:
+ background 0.15s,
+ opacity 0.15s;
+}
+
+.mobileLink:hover {
+ background: var(--nav-surface);
+ opacity: 1;
+}
+
+/* ── Product dropdown (desktop hover) ── */
+
+.productDropdown {
+ position: relative;
+}
+
+.productDropdown .link {
+ cursor: pointer;
+}
+
+.chevron {
+ display: inline-block;
+ transition: transform 0.2s ease;
+ vertical-align: middle;
+ margin-top: -1px;
+}
+
+.chevronOpen {
+ transform: rotate(180deg);
+}
+
+.productDropdown:hover .chevron {
+ transform: rotate(180deg);
+}
+
+.dropdownMenu {
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ transform: translateX(-50%);
+ padding-top: 10px;
+ z-index: 100;
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+ transition:
+ opacity 0.15s ease,
+ visibility 0.15s ease;
+}
+
+.productDropdown:hover .dropdownMenu {
+ opacity: 1;
+ visibility: visible;
+ pointer-events: auto;
+}
+
+.dropdownItem {
+ display: block;
+ padding: 10px 24px;
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.78rem;
+ font-weight: 500;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: inherit;
+ text-decoration: none;
+ text-align: center;
+ white-space: nowrap;
+ background: var(--nav-solid-bg);
+ border: 1px solid var(--nav-border);
+ border-top: none;
+ transition: background 0.15s;
+ opacity: 1;
+}
+
+.dropdownMenu a:first-child {
+ border-top: 1px solid var(--nav-border);
+ border-radius: 8px 8px 0 0;
+}
+
+.dropdownMenu a:last-child {
+ border-radius: 0 0 8px 8px;
+}
+
+.dropdownItem:hover {
+ background: color-mix(in srgb, var(--nav-solid-bg) 88%, var(--primary) 12%);
+ opacity: 1;
+}
+
+/* ── Mobile product toggle ── */
+
+.mobileProductToggle {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ padding: 12px 14px;
+ border-radius: 8px;
+ font-size: 1.0625rem;
+ font-weight: 500;
+ color: inherit;
+ text-decoration: none;
+ opacity: 0.85;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ transition:
+ background 0.15s,
+ opacity 0.15s;
+ font-family: inherit;
+}
+
+.mobileProductToggle:hover {
+ background: var(--nav-surface);
+ opacity: 1;
+}
+
+.mobileProductItems {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ padding-left: 0;
+}
+
+.mobileProductItems .mobileLink {
+ font-size: 1.0625rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ text-align: center;
+ opacity: 1;
+ background: color-mix(in srgb, var(--nav-solid-bg) 96%, var(--nav-fg) 4%);
+}
+
+.mobileProductItems .mobileLink:hover {
+ background: color-mix(in srgb, var(--nav-solid-bg) 88%, var(--primary) 12%);
+ opacity: 1;
+}
diff --git a/web/components/ui/badge.tsx b/web/components/ui/badge.tsx
new file mode 100644
index 00000000..681f2339
--- /dev/null
+++ b/web/components/ui/badge.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react';
+import { cva, type VariantProps } from 'class-variance-authority';
+
+import { cn } from '../../lib/utils';
+
+const badgeVariants = cva(
+ 'inline-flex items-center rounded-md border px-2.5 py-1 text-xs font-medium transition-colors',
+ {
+ variants: {
+ variant: {
+ default:
+ 'border-transparent bg-[color-mix(in_srgb,var(--primary)_12%,transparent)] text-[var(--primary)]',
+ secondary:
+ 'border-transparent bg-[color-mix(in_srgb,var(--fg)_8%,transparent)] text-[var(--fg-muted)]',
+ outline: 'border-[var(--line)] bg-transparent text-[var(--fg-muted)]',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ },
+ }
+);
+
+export interface BadgeProps
+ extends React.HTMLAttributes, VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return
;
+}
+
+export { Badge, badgeVariants };
diff --git a/web/components/ui/button.tsx b/web/components/ui/button.tsx
new file mode 100644
index 00000000..55a9ea1c
--- /dev/null
+++ b/web/components/ui/button.tsx
@@ -0,0 +1,49 @@
+import * as React from 'react';
+import { Slot } from '@radix-ui/react-slot';
+import { cva, type VariantProps } from 'class-variance-authority';
+
+import { cn } from '../../lib/utils';
+
+const buttonVariants = cva(
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-semibold cursor-pointer transition-[transform,background-color,color,border-color] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 shrink-0',
+ {
+ variants: {
+ variant: {
+ default:
+ 'border border-transparent bg-[var(--primary-bg)] text-[var(--primary-fg)] hover:bg-[var(--btn-hover)] hover:-translate-y-px',
+ secondary:
+ 'border border-transparent bg-[var(--secondary-bg)] text-[var(--secondary-fg)] hover:bg-[var(--secondary-200)] hover:-translate-y-px',
+ outline: 'border border-[var(--line)] bg-[var(--bg)] text-[var(--primary)] hover:-translate-y-px',
+ inverted: 'border border-transparent bg-[var(--fg)] text-[var(--bg)] hover:-translate-y-px',
+ ghost: 'border border-transparent bg-transparent text-[var(--fg-muted)]',
+ destructive:
+ 'border border-transparent bg-[#FEE2E2] text-[#DC2626] hover:bg-[#FECACA] hover:-translate-y-px',
+ },
+ size: {
+ default: 'px-5 py-2.5',
+ sm: 'px-3.5 py-1.5 text-xs',
+ lg: 'px-6 py-3',
+ icon: 'size-10 rounded-none p-0',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ }
+);
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes, VariantProps {
+ asChild?: boolean;
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : 'button';
+ return ;
+ }
+);
+Button.displayName = 'Button';
+
+export { Button, buttonVariants };
diff --git a/web/components/ui/card.tsx b/web/components/ui/card.tsx
new file mode 100644
index 00000000..28e451b0
--- /dev/null
+++ b/web/components/ui/card.tsx
@@ -0,0 +1,52 @@
+import * as React from 'react';
+
+import { cn } from '../../lib/utils';
+
+const Card = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+Card.displayName = 'Card';
+
+const CardHeader = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+CardHeader.displayName = 'CardHeader';
+
+const CardTitle = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+CardTitle.displayName = 'CardTitle';
+
+const CardDescription = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+CardDescription.displayName = 'CardDescription';
+
+const CardContent = React.forwardRef>(
+ ({ className, ...props }, ref) =>
+);
+CardContent.displayName = 'CardContent';
+
+const CardFooter = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+CardFooter.displayName = 'CardFooter';
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
diff --git a/web/components/ui/input.tsx b/web/components/ui/input.tsx
new file mode 100644
index 00000000..744928c5
--- /dev/null
+++ b/web/components/ui/input.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react';
+
+import { cn } from '../../lib/utils';
+
+const Input = React.forwardRef>(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ );
+ }
+);
+Input.displayName = 'Input';
+
+export { Input };
diff --git a/web/components/waitlist.module.css b/web/components/waitlist.module.css
new file mode 100644
index 00000000..6c3881c1
--- /dev/null
+++ b/web/components/waitlist.module.css
@@ -0,0 +1,150 @@
+.form {
+ position: relative;
+ display: flex;
+ align-items: center;
+ gap: 0;
+ max-width: 520px;
+ margin: 0 auto;
+ background: var(--card-bg);
+ border: 1px solid var(--line);
+ border-radius: 100px;
+ padding: 5px 5px 5px 24px;
+ transition:
+ border-color 0.2s,
+ box-shadow 0.2s;
+}
+
+.form:focus-within {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 10%, transparent);
+}
+
+.input {
+ flex: 1;
+ min-width: 0;
+ border: none;
+ background: transparent;
+ color: var(--fg);
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.92rem;
+ outline: none;
+ padding: 10px 0;
+}
+
+.input::placeholder {
+ color: var(--fg-faint);
+}
+
+.button {
+ flex-shrink: 0;
+ padding: 10px 24px;
+ background: #2d6a9c;
+ color: var(--primary-fg);
+ border: 1.5px solid #234969;
+ border-radius: 100px;
+ font-family: var(--font-geist-sans), sans-serif;
+ font-size: 0.85rem;
+ font-weight: 600;
+ letter-spacing: 0.01em;
+ cursor: pointer;
+ box-shadow: none;
+ transition:
+ background 0.2s,
+ border-color 0.2s,
+ transform 0.15s,
+ box-shadow 0.2s;
+ white-space: nowrap;
+}
+
+.button:hover:not(:disabled) {
+ background: #4a90c2;
+ border-color: #234969;
+ transform: translateY(-1px);
+ box-shadow: none;
+}
+
+.button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.successCard {
+ max-width: 520px;
+ margin: 0 auto;
+ padding: 32px 28px;
+ border-radius: 16px;
+ border: 1px solid color-mix(in srgb, var(--primary) 15%, transparent);
+ background: color-mix(in srgb, var(--primary) 5%, transparent);
+ text-align: center;
+ animation: popIn 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+@keyframes popIn {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: none;
+ }
+}
+
+.successIcon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
+ color: var(--primary);
+ margin: 0 auto 16px;
+}
+
+.successTitle {
+ font-family: var(--font-heading), sans-serif;
+ font-size: 1.2rem;
+ font-weight: 700;
+ color: var(--primary);
+ margin: 0 0 8px;
+}
+
+.successText {
+ font-size: 0.9rem;
+ line-height: 1.6;
+ color: var(--fg-muted);
+ margin: 0;
+}
+
+.successText strong {
+ color: var(--fg);
+ font-weight: 600;
+}
+
+.error {
+ position: absolute;
+ bottom: -28px;
+ left: 24px;
+ font-size: 0.78rem;
+ color: #c44;
+ margin: 0;
+}
+
+@media (max-width: 500px) {
+ .form {
+ flex-direction: column;
+ border-radius: 16px;
+ padding: 12px;
+ gap: 8px;
+ }
+
+ .input {
+ width: 100%;
+ text-align: center;
+ }
+
+ .button {
+ width: 100%;
+ }
+}
diff --git a/web/content/agent-relay/SKILL.md b/web/content/agent-relay/SKILL.md
new file mode 100644
index 00000000..bdd5e968
--- /dev/null
+++ b/web/content/agent-relay/SKILL.md
@@ -0,0 +1,147 @@
+---
+name: agent-relay-team-onboarding
+description: Use when a human or lead agent needs one plain handoff for starting an Agent Relay workspace, choosing the right Relay skill, and telling spawned agents how to communicate.
+---
+
+# Agent Relay Team Onboarding
+
+Use this skill when someone asks how to get agents onto Agent Relay, how a lead should start a workspace, or which Relay skill to give to a worker.
+
+The default handoff URL is:
+
+```text
+https://agentrelay.com/skill
+```
+
+Give that URL to a human driver, a lead agent, or a newly spawned agent when you want them to orient themselves without a long custom prompt.
+
+## Choose the Right Skill
+
+### Human driver or lead orchestrator
+
+Use **orchestrating-agent-relay** when the agent is responsible for starting Relay and managing other agents from the outside.
+
+Source:
+
+```text
+https://github.com/AgentWorkforce/skills/blob/main/skills/orchestrating-agent-relay/SKILL.md
+```
+
+Use this role when the agent should:
+
+- start the broker with `agent-relay local up`
+- create or reuse a workspace
+- spawn workers
+- send follow-up instructions through local attach or registered Relay messages
+- read terminal output, logs, and lifecycle state
+- release workers when the run is accepted
+
+Important distinction: the orchestrator is not usually a registered Agent Relay
+participant. It should use the `agent-relay local ...` CLI for starting,
+spawning, listing, tailing, attaching, and releasing local workers. If it wants
+to send Relay DMs through the message CLI, it must use a registered token.
+
+### Spawned or registered participant
+
+Use **using-agent-relay** when the agent is already on the relay as a registered participant.
+
+Source:
+
+```text
+https://github.com/AgentWorkforce/skills/blob/main/skills/using-agent-relay/SKILL.md
+```
+
+Use this role when the agent should:
+
+- ACK tasks over Relay
+- send DMs to other agents
+- post in channels
+- reply in threads
+- check its inbox
+- coordinate peer-to-peer with MCP tools
+
+Registered participants should use the current flat Agent Relay MCP tools, such
+as `send_dm`, `post_message`, `reply_to_thread`, `check_inbox`, `list_agents`,
+and `join_channel`.
+
+## Quick Start for a Human Driver
+
+1. Start Relay from the project root:
+
+ ```bash
+ agent-relay local up --verbose
+ agent-relay local status --wait-for 10
+ ```
+
+2. Spawn a lead or worker:
+
+ ```bash
+ agent-relay local agent spawn claude --name Lead --task "Use https://agentrelay.com/skill. Start the workspace, coordinate workers, and report progress."
+ ```
+
+3. Watch output from the lead:
+
+ ```bash
+ agent-relay local tail --agent Lead
+ ```
+
+4. Send follow-up instructions through an interactive attach:
+
+ ```bash
+ agent-relay local agent attach Lead --mode drive
+ ```
+
+5. List and inspect workers:
+
+ ```bash
+ agent-relay local agent list
+ agent-relay local tail --agent Lead
+ ```
+
+## Prompt for a Lead Agent
+
+```text
+Use https://agentrelay.com/skill as your Agent Relay onboarding reference.
+
+You are the orchestrator. Use the orchestrating-agent-relay role:
+- start or verify the relay broker
+- spawn the workers needed for the task
+- tell each worker to use the using-agent-relay role
+- read worker output with agent-relay local tail --agent
+- monitor liveness with agent-relay local agent list
+- send follow-up instructions with agent-relay local agent attach --mode drive
+- keep workers alive through review/fix loops
+- release workers only after final acceptance
+
+Workers should ACK when they receive work, report DONE when complete, and stay alive for review findings until you release them.
+```
+
+## Prompt for a Worker Agent
+
+```text
+Use https://agentrelay.com/skill as your Agent Relay onboarding reference.
+
+You are a registered Agent Relay participant. Use the using-agent-relay role:
+- ACK the task immediately over Relay
+- use flat Agent Relay MCP tools for DMs, channel posts, replies, inbox checks, and agent lists
+- post concise progress updates when useful
+- report DONE with evidence when the task is complete
+- do not remove or release yourself; stay available for review findings
+```
+
+## Common Failure Mode
+
+If a tool says:
+
+```text
+Not registered. Call agent.register first.
+```
+
+You are probably acting as the outside orchestrator. Use `agent-relay` CLI commands instead of participant MCP tools, or register yourself before using participant-only MCP tools.
+
+## Canonical Links
+
+- Default hosted handoff: `https://agentrelay.com/skill`
+- Raw markdown handoff: `https://agentrelay.com/skill.md`
+- Orchestrator skill: `https://github.com/AgentWorkforce/skills/blob/main/skills/orchestrating-agent-relay/SKILL.md`
+- Participant skill: `https://github.com/AgentWorkforce/skills/blob/main/skills/using-agent-relay/SKILL.md`
diff --git a/web/content/blog/go-to-bed-wake-up-to-a-finished-product.mdx b/web/content/blog/go-to-bed-wake-up-to-a-finished-product.mdx
new file mode 100644
index 00000000..9d556b15
--- /dev/null
+++ b/web/content/blog/go-to-bed-wake-up-to-a-finished-product.mdx
@@ -0,0 +1,97 @@
+---
+title: 'Go to Bed, Wake Up to a Finished Product'
+description: "Agent-to-agent communication is weird, fascinating, and potentially revolutionary. Here's how we built a system where agents work autonomously while we sleep."
+date: '2026-02-01'
+author: 'Khaliq Gant'
+category: 'Vision'
+tags:
+ - Autonomous development
+ - Agent communication
+ - Product vision
+---
+
+_How agent-to-agent communication is changing autonomous development_
+
+The clear and obvious takeaway from [OpenClaw](https://openclaw.ai/) (Moltbot) and [Moltbook](https://www.moltbook.com/) is that agent-to-agent communication is "first of all weird and fascinating, but the potential this unlocks is also unprecedented and potentially revolutionary."
+
+On December 16th, we started a project where we wanted to get two Claude agents to play tic-tac-toe against each other. We first dug into Agent Mail and [made a proof of concept](https://github.com/Dicklesworthstone/mcp_agent_mail/pull/33) there, but quickly realized the limitations of a pull-based methodology. We kept digging and quickly realized the only way to get an agent to autonomously interact with other agents is a push-based method that delivers messages directly to their input. So was born [Agent Relay](https://github.com/AgentWorkforce/relay).
+
+## Ok...I'm intrigued, go on...
+
+"Agent-to-agent communication across any CLI tool (Codex, Claude, Gemini, OpenCode, Droid, etc.) is possible **right now** for any task." This can be done via an easy-to-use SDK or the relay-dashboard where you can see messages across agents, chat, spawn and release agents as needed.
+
+At the base level, Agent Relay is a protocol which makes it easy and reliable to stage CLI-agnostic agent-to-agent communication. The tools built on top of that protocol are the [relay-dashboard](https://github.com/AgentWorkforce/relay-dashboard/) and [relay-tui](https://github.com/AgentWorkforce/relay-tui) -- but could be relay-slack, relay-discord -- you name it!
+
+"**Agents are creative, efficient, sometimes lazy and cut corners, chatty, and but mostly it is clear that they work quite effectively when given roles and clear responsibilities.**"
+
+I kept finding myself as the clear bottleneck when working on projects, so removing myself and replacing with an agent is a huge accelerator. I found myself spending more time planning and producing detailed specs. I'd hand that to a "Lead" agent, ask them to review it, staff a team accordingly, and just let me know of any blockers.
+
+Then I would [go to bed](https://x.com/khaliqgant/status/2008325804741071079) and wake up to a finished product.
+
+## Why Not Just Sub-Agents?
+
+Using actual agent instances is a more powerful abstraction. Your agents can create sub-agents, giving the agent much more power. When giving a sub-agent a task too big or complex, it'll often hit compaction (context window limits) and just stop -- which completely breaks the autonomous workflow we're looking for.
+
+## How Do I Use This?
+
+There are a few ways to use Relay:
+
+### For Developers
+
+**1.** Start a relay server and use the dashboard to spawn and chat with agents:
+
+```bash
+npm install -g agent-relay
+agent-relay up --dashboard
+```
+
+**2.** Start a `claude` | `codex` etc session and tell it to install and start agent-relay by following the instructions here:
+
+```bash
+$ claude
+
+> Install and configure agent-relay by following the instructions here:
+ curl -s https://raw.githubusercontent.com/AgentWorkforce/relay/main/docs/guide/agent-setup.md
+```
+
+The agent will read the setup guide and start a relay session, ready to coordinate with other agents.
+
+### For Library Developers
+
+Use our SDK to enable agent-to-agent communication in your app:
+
+```bash
+# Install agent relay and start the daemon
+npm install -g agent-relay
+agent-relay up
+
+# In your project
+npm install agent-relay
+```
+
+```javascript
+import { RelayClient } from 'agent-relay';
+
+const client = new RelayClient({ name: 'MyApp' });
+await client.connect();
+
+// Spawn a worker agent
+await client.spawn({
+ name: 'Worker',
+ cli: 'claude',
+ task: 'Wait for instructions',
+});
+
+// Send it a message
+await client.send('Worker', 'Hello from my app');
+```
+
+Check out our [docs](/docs) for more info.
+
+## What's Next
+
+We're super excited for what's to come. [Agent Relay Cloud](https://agentrelay.com/) allows you, via GitHub login, to link your repository to your own private workspace server. Using your Claude, Codex, Gemini, or Cursor login, you can spawn agents and have them work 24/7 against your repo using the dashboard.
+
+You and your teammates who have access to the repository can interact with agents, giving instructions in different topic-based channels to help organize agent work. You can have agents review pull requests, work on documentation -- all with authenticated access to your GitHub repo.
+
+This is just the beginning. GitHub is the first context we've unlocked. This will be extended to other integrations like Notion, Slack, Outlook, etc., so agents can operate 24/7 and accelerate your workflows.
diff --git a/web/content/blog/humans-and-agents-all-want-the-same-thing-and-its-disgusting.mdx b/web/content/blog/humans-and-agents-all-want-the-same-thing-and-its-disgusting.mdx
new file mode 100644
index 00000000..aaba4dbe
--- /dev/null
+++ b/web/content/blog/humans-and-agents-all-want-the-same-thing-and-its-disgusting.mdx
@@ -0,0 +1,135 @@
+---
+title: 'Humans and Agents all want the same thing and it’s disgusting'
+description: 'Cognitive science, communication theory, and software teams all point to the same conclusion: agents need what humans need — clear context, defined constraints, and real-time feedback loops.'
+date: '2026-02-13'
+author: 'Will Washburn'
+category: 'Vision'
+tags:
+ - Coordination
+ - Communication
+ - Multi-agent systems
+---
+
+One of the most durable patterns in software teams is that a good manager can only keep so many threads in their head at once. At some point, the number of people, dependencies, decisions, and feedback loops becomes too much for one person to route cleanly.
+
+That same pattern is showing up again with coding agents. Not because agents are human, and not because they need to be anthropomorphized, but because the underlying coordination problem is the same. If the work depends on context, constraints, and rapid feedback, then scale breaks down in familiar ways.
+
+This is the part I find both obvious and a little disgusting: humans and agents seem to want the same thing. They perform best when the goal is clear, the boundaries are explicit, and the system around them makes the right context easy to access.
+
+Cognitive science explains why. Humans can actively hold only about four chunks of information in working memory at once (Cowan, 2001). A manager with five engineers isn’t just tracking five humans. They’re tracking five skill profiles, five motivation states, cross-dependencies between them, roadmap constraints, business pressure, and technical risk. That’s a lot of context switching. And research on decision fatigue (Danziger et al., 2011) shows that as decision volume rises, decision quality drops.
+
+Communication theory says the same thing from another angle. As Fred Brooks pointed out in _The Mythical Man-Month_, coordination paths grow roughly with n². Each additional person doesn’t just add one new relationship, they add many.
+
+At around five direct reports, maintaining high-fidelity context routing becomes extremely difficult. So in the human world, the best teams have structures that reflect this.
+
+## Pair programming is where 2+2=5
+
+No plan survives first contact with reality.
+
+Architecture diagrams look clean. Tickets look clear. Roadmaps feel confident. Then execution starts, and the edge cases show up. Assumptions break. Constraints you didn’t know existed suddenly matter.
+
+Pair programming shortens the distance between plan and reality.
+
+If management is context routing at the team level, pairing is context routing at the keystroke level. One editor. Two brains. A live feedback loop while the work is actually happening.
+
+When it works, it feels unfair. Bad assumptions get challenged immediately. Edge cases surface before they escape into production. Tradeoffs get debated while they’re still cheap to change. It’s real time feedback within a set of boundaries that collapses feedback loops and increases context density during execution.
+
+I strongly believe pair programming improves software quality, but it is not free. I mean at a simple level it is at least twice as expensive, right? It also requires two people to be motivated, online, and “in the zone” at the same time. It’s hairy and inconsistent.
+
+## Is Claude on the team now? Or is it a tool?
+
+Of course the whole world has changed in the last year, six months, two weeks, so most of what I’m saying feels a bit… dated. But is it?
+
+The best software teams have adopted coding agents. Or at least experimented. But how and where should they fit in the process? Is Claude a teammate? Is it a simple tool not to be anthropomorphized?
+
+I’ve seen a lot of influencers frame this as engineers getting promoted to managers and they just need to “manage their AI team” now. It’s a nice analogy. Makes you feel in control. We’re all the boss now! But I’m not so sure that’s the right mental model.
+
+## The “new” world
+
+I recently worked with a team trying to adapt to AI-native development. You probably recognize the symptoms:
+
+Code reviews feel impossible to keep up with. “Slop” creeps into the codebase. Velocity is up, but confidence is down. Expectations have skyrocketed. Fear is lurking in the background. Anxiety dominates the vibes.
+
+The old processes feel… insufficient.
+
+So we went back to first principles and asked a simple question: When you introduce coding agents, what inputs do you actually need to get them to build software?
+
+After long sessions and deep discussions, the answer was almost boring.
+
+It’s pretty much what it’s always been. You still just need clearly defined requirements.
+
+## Agents Want What Humans Want
+
+If you strip away the hype, agents don’t want magic prompts. They want context.
+
+Look at what consistently predicts high-performing human teams. Google’s Project Aristotle found that clarity of goals was a stronger predictor of performance than raw talent. People perform best when they understand the objective, the constraints, the boundaries of their authority, and how their work connects to the system around them.
+
+Agents are no different.
+
+They need clear goals, explicit constraints, access to relevant system state, and feedback loops.
+
+Without those, they hallucinate. Overreach. Or produce technically correct but strategically useless output.
+
+We keep obsessing over bigger context windows and orchestration layers. But giving an agent 200k tokens isn’t the same thing as giving it the right information. Just like giving a human a 200-page PRD isn’t clarity.
+
+Humans and agents both make better decisions when the decision surface is bounded, the relevant context is preloaded, and they have the authority to make decisions. Talent helps. Model quality helps. But neither compensates for missing context.
+
+And if software development is a system for producing good decisions, then agents just plug into the same system.
+
+## How do we give agents the right context?
+
+The real question is: how do we provide agents with the right context to do their work effectively?
+
+The answer may not be glamorous, but it’s at least familiar. We organize them much like we do people. Some focus on the details of implementation. Others focus on defining the problem, setting direction, and establishing guardrails.
+
+In other words, the structure isn’t new. What matters is clear communication of goals, constraints, and expectations so agents can collaborate, iterate, and stay aligned.
+
+And what role should humans play in this flow? Many people have been building orchestrators where the human is at the center of the loop. I think they are quickly finding themselves as the bottleneck.
+
+The truth is there is no need for a human to coordinate if the context waterfall is systematically sound. Humans are needed to define broad intent and constraints, but the environment should facilitate the execution.
+
+## The missing protocol
+
+How would humans do that? Well, today we all work in Slack and Microsoft Teams. We have meetings to push and pull on ideas. We give feedback while we execute. We get a ton of value from rapidly discussing things.
+
+Agents should have that same ability.
+
+Agents need to be able to communicate in real time. Send messages, ask for help, provide context to each other about what they’re working on. It’s a missing core primitive.
+
+This is why we built Agent Relay.
+
+Agent Relay is a real-time messaging protocol for AI agents. It provides a dedicated rail for agents to share context and coordinate work as it happens. It’s agentic Slack, more or less.
+
+## Speed Changes Everything
+
+When agents can convey context to each other, magic truly starts to happen. You really don’t need all these opinionated orchestrators and you don’t need to reinvent the wheel.
+
+Building used to be the expensive part. You’d spend weeks debating something because implementation cost was high. You wanted to be sure.
+
+Now?
+
+You can generate five approaches before lunch. Throw three away. Merge one. Rewrite it tomorrow. Upfront validation matters way less.
+
+What matters? Boundaries. Constraints. Shared understanding of intent. When iteration is cheap, ambiguity becomes the bottleneck. And ambiguity is just missing context.
+
+So building becomes a real-time conversation with agents about what those constraints are. It’s the same as it’s always been, but the execution phase is going to zero. Feedback happens as the work happens. Humans don’t need to be the coordinators, agents can do it just fine.
+
+## Free your ego
+
+If you’re like me, this part stings a little. Humans just define intent and constraints?! That’s it?
+
+I will not try to tell you that your work is not going to change, because I believe it is. It’s going to change significantly. The sooner you accept that, the more leverage you’ll have inside it.
+
+But here’s what I actually believe:
+
+Your value as a knowledge worker has increased.
+
+When execution cost approaches zero, the bottleneck shifts to defining constraints and setting direction. Those decisions carry more weight than ever. The squishy human parts didn’t disappear at all; if anything, they’ve been highlighted. Your humanity is now more important. More rare in the overall system.
+
+We’re all going to explore the exact mechanics of how that happens, but software has always been a human-driven solution space. It will continue to be so.
+
+You still get to create something from nothing. You still define the shape of the system. You just don’t have to manually push every instruction through the machine. You are freed from the chains of the slow, cumbersome execution phase, albatrossed with deterministic, repetitive coordination problems. You are free to create literally anything you can imagine. You can make the world a better place, faster.
+
+Or, at least, that’s my take.
+
+If that excites you and you want to help shape how this new layer of coordination works we’re hiring. Email us at [hello@agentrelay.com](mailto:hello@agentrelay.com) and let’s talk.
diff --git a/web/content/blog/just-give-the-agent-files.mdx b/web/content/blog/just-give-the-agent-files.mdx
new file mode 100644
index 00000000..4f97c8d7
--- /dev/null
+++ b/web/content/blog/just-give-the-agent-files.mdx
@@ -0,0 +1,282 @@
+---
+title: 'Just Give the Agent Files'
+description: 'Most cloud agents wait to be asked. Relayfile changes that: connect your providers once and the agent wakes up automatically when something in the world changes.'
+date: '2026-05-13'
+author: 'Khaliq Gant'
+category: 'Engineering'
+tags:
+ - Relayfile
+ - Proactive agents
+ - MCP
+ - Integrations
+---
+
+The thing I keep running into with cloud agents is that they're really good at answering questions and really bad at knowing when to ask themselves one.
+
+Most agents today are reactive. You call them, they respond, they go dormant. This is perfectly fine for a coding assistant sitting at your keyboard. When I'm feeding my daughter with one hand, and flipping pancakes with the other, I want my agent monitoring my stack (not the pancakes) so I don't have to be running back to my computer every 10 minutes accepting permission prompts or telling the agent to look at my ~~honeycomb~~ traces.
+
+When I wake up at three in the morning and have trouble going back to sleep the thing that I can't get out of my head is how to make my agents [proactive](https://proactiveagents.dev/posts/three-primitives/). (Do I have an obsession problem? Maybeee 🤫) How can the agent know something happened in the outside world without me having to tell it?
+
+## MCP - Mo' Context Problems
+
+MCP has established itself as the industry standard and it can be fantastic when used correctly. But I've found that when you stack many MCP tools, the agent starts every task carrying a catalog it may mostly never use. Anthropic has [written about](https://www.anthropic.com/engineering/advanced-tool-use) tool definitions consuming tens of thousands of tokens before any real work begins and how they try to counteract this. [Composio](https://composio.dev/) has written about the same problem on their blog and their efforts to improve the situation.
+
+That's the context bloat problem. There's a related but different one: MCP tools let the agent pull information. They're not how the outside world pushes a signal to the agent. For that you need webhooks, and webhooks are non trivial.
+
+To make a single cloud agent proactive against Linear you need a public endpoint, HMAC verification, a queue, deduplication logic, retry handling, and registration with Linear's API. Then repeat for GitHub, Jira, and Slack, each with a different signature format and retry behavior. A team can spend four to six weeks building this before they write a line of actual agent logic.
+
+## Clock 🕕, Listener 📻, Inbox 📥
+
+The way we think about making agents proactive is three primitives:
+
+- **Clock** wakes the agent up on a schedule -> we map this to [relaycron](https://github.com/AgentWorkforce/relaycron)
+- **Listener** wakes the agent up when something in the world changes -> we map this to [relayfile](https://github.com/AgentWorkforce/relayfile)
+- **Inbox** wakes the agent up when someone has something to say -> we map this to [relaycast](https://github.com/AgentWorkforce/relaycast)
+
+So, allow me to introduce [relayfile](https://agentrelay.com/file). You connect Linear, GitHub, Notion, Slack — whichever providers you care about — once. From then on, the agent code is just:
+
+```typescript
+import { RelayFileClient, RelayFileSync } from '@relayfile/sdk';
+
+const client = new RelayFileClient({
+ token: process.env.RELAYFILE_TOKEN!,
+});
+
+const sync = new RelayFileSync({
+ client,
+ workspaceId: 'ws_acme_ops', // already wired to Linear, GitHub, Notion, ...
+});
+
+sync.on('event', async (event) => {
+ // event.path tells you which provider's record changed:
+ // e.g. '/linear/issues/AGE-16__...json' or '/github/repos/.../pulls/412.json'
+ await agent.handle(event);
+});
+
+sync.start();
+```
+
+No endpoint to host. No signature verification. No queue. When a Linear issue moves to blocked or a GitHub PR gets reviewed, the agent gets a normalized filesystem event pointing at the canonical file:
+
+```typescript
+{
+ eventId: 'evt_01HQ8K7M2YV3R0XW9F4ZB6T2QA',
+ type: 'file.updated',
+ path: '/linear/issues/AGE-16__87389837-62b1-4e1a-a237-59218bab2974.json',
+ revision: 'rev_42',
+ provider: 'linear',
+ origin: 'provider_sync',
+ timestamp: '2026-05-13T14:32:01Z',
+}
+```
+
+The event tells the agent _what_ changed and _where_. It then reads the file at `path` to see the current state — and, if it cares about the diff, the prior revision is one `client.readFile` away. The rest? _Forget about it_ (said like a New Yorker)
+
+## The workspace is also just files
+
+The change events are useful on their own but the full workspace is also navigable. Every connected provider is mounted at a well-known path and the agent can explore, search, and write back using tools it already knows.
+
+```bash
+$ ls $MOUNT
+LAYOUT.md notion/ linear/ slack/ github/ confluence/
+```
+
+`LAYOUT.md` at the root (and at each provider root) describes the shape of the workspace. That's where the agent should start.
+
+Notion pages live under `notion/pages/`. Canonical records are keyed by UUID with a `by-title/` subtree for human-readable lookup:
+
+```bash
+# Find a page by title
+$ jq -r '.id' $MOUNT/notion/pages/by-title/just-give-the-agent-files__c24642bb.json
+3566800c-1c90-8035-a491-cdc3c24642bb
+
+# Read the body
+$ cat $MOUNT/notion/pages/3566800c-1c90-8035-a491-cdc3c24642bb/content.md
+```
+
+Linear issues use `__` — ticket number first so the listing is immediately scannable. [Mirage](https://github.com/strukto-ai/mirage) converged on a similar filename shape in their mount, and their broader "mount everything as one tree" framing is the closest thing in this space to what we're doing:
+
+```bash
+$ ls $MOUNT/linear/issues/
+AGE-9__2bb2c00f-ee93-4c73-a793-df5b725d9a1a.json
+AGE-10__8c313d70-9800-4539-820f-96a481c09ce0.json
+AGE-16__87389837-62b1-4e1a-a237-59218bab2974.json
+
+$ jq '.payload | {title, state_name, assignee_name}' \
+ $MOUNT/linear/issues/AGE-16__87389837-62b1-4e1a-a237-59218bab2974.json
+```
+
+## Writebacks are files too
+
+Reads are useful. Writes are where it gets interesting. The agent doesn't call a provider API to take action — it writes a file. Relayfile handles the provider auth, payload validation, retries, and audit trail on the other side.
+
+To create a GitHub issue, the agent drops a file:
+
+```bash
+$ cat > $MOUNT/github/repos/AgentWorkforce/relayfile-adapters/issues/relayfile-writeback-test-20260513T121247Z.json < --follow
+agent-relay local sync
+```
+
+As long as you're ok leaving your laptop open to run for awhile, this is great. But if you want to run it on a remote machine (and not cook an egg on your laptop spinning up a bazillion agents), you can use the Agent Relay cloud.
+
+```bash
+agent-relay cloud run workflows/my-workflow.ts --sync-code
+agent-relay cloud logs --follow
+agent-relay cloud sync
+```
+
+
+
+The most important lesson:
+Pick the topology first, then agents, then gates. A workflow that starts with "spawn five agents" usually has the wrong center of gravity.
+
+## 1. Classify And Act
+
+Use this when one agent should route the task to the right specialist. Good for support queues, issue triage, model routing, and mixed backlog grooming.
+
+Relay shape: `handoff` or a live classifier in a channel. If you want deterministic workflow steps, let non-selected specialists return `SKIP`.
+
+```ts
+import { workflow } from '@relayflows/core';
+await workflow('classify-and-act')
+ .pattern('handoff')
+ .agent('router', { cli: 'claude', role: 'classifier' })
+ .agent('bugfix', { cli: 'codex', role: 'bugfix specialist' })
+ .agent('docs', { cli: 'claude', role: 'docs specialist' })
+ .agent('research', { cli: 'gemini', role: 'research specialist' })
+ .step('classify', {
+ agent: 'router',
+ task: 'Classify {{task}} as bugfix, docs, or research. Output ROUTE: and a short brief.',
+ })
+ .step('bugfix', {
+ agent: 'bugfix',
+ dependsOn: ['classify'],
+ task: 'If ROUTE:bugfix is absent, output SKIP. Otherwise execute the brief:\n{{steps.classify.output}}',
+ })
+ .step('docs', {
+ agent: 'docs',
+ dependsOn: ['classify'],
+ task: 'If ROUTE:docs is absent, output SKIP. Otherwise execute the brief:\n{{steps.classify.output}}',
+ })
+ .step('research', {
+ agent: 'research',
+ dependsOn: ['classify'],
+ task: 'If ROUTE:research is absent, output SKIP. Otherwise execute the brief:\n{{steps.classify.output}}',
+ })
+ .run();
+```
+
+## 2. Fanout And Synthesize
+
+Use this when the subtasks are independent and a lead can merge the outputs. Good for parallel reviews, research sweeps, claim checks, and subsystem audits.
+
+Relay shape: `fan-out`. Workers do not need to talk to each other; the synthesizer owns the final answer.
+
+```ts
+await workflow('fanout-and-synthesize')
+ .pattern('fan-out')
+ .agent('lead', { cli: 'claude', role: 'synthesizer' })
+ .agent('api', { cli: 'codex', role: 'worker', interactive: false })
+ .agent('web', { cli: 'codex', role: 'worker', interactive: false })
+ .agent('docs', { cli: 'claude', role: 'worker', interactive: false })
+ .step('api-review', { agent: 'api', task: 'Review API risks for {{task}}.' })
+ .step('web-review', { agent: 'web', task: 'Review frontend risks for {{task}}.' })
+ .step('docs-review', { agent: 'docs', task: 'Review docs and migration risks for {{task}}.' })
+ .step('synthesize', {
+ agent: 'lead',
+ dependsOn: ['api-review', 'web-review', 'docs-review'],
+ task: `Merge the findings into one plan.
+
+API:
+{{steps.api-review.output}}
+
+Web:
+{{steps.web-review.output}}
+
+Docs:
+{{steps.docs-review.output}}`,
+ })
+ .run();
+```
+
+## 3. Adversarial Verification
+
+Use this when "done" is expensive to trust. Good for security, billing, permissions, migrations, factual claims, and anything with hidden failure modes.
+
+Relay shape: `verifier` or `red-team`. The worker builds; verifiers attack the claim; the worker repairs.
+
+```ts
+await workflow('adversarial-verification')
+ .pattern('verifier')
+ .agent('worker', { cli: 'codex', role: 'implementer' })
+ .agent('security', { cli: 'claude', role: 'verifier' })
+ .agent('tests', { cli: 'codex', role: 'verifier' })
+ .agent('product', { cli: 'claude', role: 'verifier' })
+ .step('build', { agent: 'worker', task: 'Implement {{task}}. Output DONE only with evidence.' })
+ .step('security-check', {
+ agent: 'security',
+ dependsOn: ['build'],
+ task: 'Try to disprove DONE. Focus on auth, secrets, and abuse cases.',
+ })
+ .step('test-check', {
+ agent: 'tests',
+ dependsOn: ['build'],
+ task: 'Try to break the change with focused tests and repros.',
+ })
+ .step('product-check', {
+ agent: 'product',
+ dependsOn: ['build'],
+ task: 'Check whether the shipped behavior matches the user promise.',
+ })
+ .step('repair', {
+ agent: 'worker',
+ dependsOn: ['security-check', 'test-check', 'product-check'],
+ task: 'Fix valid findings and rerun proof commands before final DONE.',
+ })
+ .run();
+```
+
+## 4. Generate And Filter
+
+Use this when you want many candidates, not one careful answer. Good for names, test cases, design options, prompts, rules, and refactor approaches.
+
+Relay shape: parallel generators plus one filter. The filter needs a rubric and dedupe rule.
+
+```ts
+await workflow('generate-and-filter')
+ .pattern('fan-out')
+ .agent('gen-a', { cli: 'claude', role: 'generator' })
+ .agent('gen-b', { cli: 'codex', role: 'generator' })
+ .agent('gen-c', { cli: 'gemini', role: 'generator' })
+ .agent('filter', { cli: 'claude', role: 'rubric judge' })
+ .step('a', { agent: 'gen-a', task: 'Generate 5 candidate approaches for {{task}}.' })
+ .step('b', { agent: 'gen-b', task: 'Generate 5 candidate approaches for {{task}}.' })
+ .step('c', { agent: 'gen-c', task: 'Generate 5 candidate approaches for {{task}}.' })
+ .step('filter', {
+ agent: 'filter',
+ dependsOn: ['a', 'b', 'c'],
+ task: 'Dedupe, score against impact/effort/risk, and return the best 3 candidates.',
+ })
+ .run();
+```
+
+## 5. Tournament
+
+Use this when independent attempts can compete. Good for tricky algorithms, UX copy, data transforms, qualitative ranking, evals, and implementation alternatives.
+
+Relay shape: `competitive`. Attempts stay separate; judges compare pairwise; a final judge picks the winner.
+
+```ts
+await workflow('tournament')
+ .pattern('competitive')
+ .agent('attempt-a', { cli: 'codex', role: 'competitor' })
+ .agent('attempt-b', { cli: 'claude', role: 'competitor' })
+ .agent('attempt-c', { cli: 'gemini', role: 'competitor' })
+ .agent('judge', { cli: 'claude', role: 'judge' })
+ .step('a', { agent: 'attempt-a', task: 'Solve {{task}} independently. Include proof.' })
+ .step('b', { agent: 'attempt-b', task: 'Solve {{task}} independently. Include proof.' })
+ .step('c', { agent: 'attempt-c', task: 'Solve {{task}} independently. Include proof.' })
+ .step('final', {
+ agent: 'judge',
+ dependsOn: ['a', 'b', 'c'],
+ task: 'Compare the attempts pairwise. Pick one winner and explain the tradeoff.',
+ })
+ .run();
+```
+
+## 6. Loop Until Done
+
+Use this when the first answer is probably wrong and the amount of work is unknown. Good for implementation work, release checks, flaky tests, migration hardening, triage queues, and anything that needs deterministic acceptance.
+
+Relay shape: `review-loop` plus repairable gates. The loop is not "try harder"; it is build, verify, repair, verify again.
+
+```ts
+await workflow('loop-until-done')
+ .pattern('review-loop')
+ .repairable()
+ .agent('builder', { cli: 'codex', role: 'implementer', retries: 3 })
+ .agent('reviewer', { cli: 'claude', role: 'reviewer' })
+ .step('build', {
+ agent: 'builder',
+ task: 'Implement {{task}}. Add or update tests. Output DONE only after proof.',
+ })
+ .step('review', {
+ agent: 'reviewer',
+ dependsOn: ['build'],
+ task: 'Fresh-eyes review the actual files. Output NO_ISSUES_FOUND or concrete findings.',
+ })
+ .step('test', {
+ type: 'deterministic',
+ dependsOn: ['review'],
+ command: 'npm test',
+ captureOutput: true,
+ failOnError: false,
+ })
+ .step('repair', {
+ agent: 'builder',
+ dependsOn: ['test'],
+ task: 'If tests failed or review had findings, repair them and rerun the same proof:\n{{steps.test.output}}',
+ verification: { type: 'output_contains', value: 'DONE' },
+ })
+ .onError('retry', { maxRetries: 3 })
+ .run();
+```
+
+## Useful Compositions
+
+- **Migration:** fan out by module, adversarially verify each patch, then loop until tests pass.
+- **Deep research:** fan out source discovery, verify claims, synthesize a cited report.
+- **Root cause:** generate independent hypotheses from logs, code, and data; run verifier agents against each theory.
+- **Rule adherence:** one verifier per rule, then a filter to remove noisy or redundant findings.
+- **Triage at scale:** classify each item, quarantine untrusted inputs, and let only privileged agents act.
+- **Taste work:** generate many options, filter by rubric, then tournament the finalists.
+
+Need routing? Use classify-and-act. Need breadth? Use fanout-and-synthesize. Need trust? Use adversarial verification. Need options? Use generate-and-filter. Need best-of-N? Use tournament. Need completion evidence? Use loop-until-done.
+
+## When Not To Use A Relayflow
+
+Do not reach for a workflow just because it feels powerful. Most ordinary coding tasks do not need a panel of agents.
+
+Use a Relayflow when at least one is true:
+
+- The task benefits from parallel clean context windows.
+- The result needs adversarial review.
+- The work has an unknown stopping point.
+- The job is mostly classification, ranking, synthesis, or verification.
+- Different agent runtimes should own different roles.
+
+
+
+
+ If you found this playbook helpful, check out Agent Relay (aka the
+ engine powering these workflow patterns). Open source from day one.
+
+
+
+
+
+
diff --git a/web/content/blog/the-agent-centered-world-is-almost-here.mdx b/web/content/blog/the-agent-centered-world-is-almost-here.mdx
new file mode 100644
index 00000000..7d9c0c2b
--- /dev/null
+++ b/web/content/blog/the-agent-centered-world-is-almost-here.mdx
@@ -0,0 +1,95 @@
+---
+title: 'The agent-centered world is almost here'
+description: 'Agents are still underused as disposable sidecars. The next shift is persistent agent teams with communication, scoped trust, and evidence built into the workflow.'
+date: '2026-06-01'
+author: 'Will Washburn'
+category: 'Vision'
+tags:
+ - Agent-native software
+ - Coordination
+ - Multi-agent systems
+ - Product vision
+---
+
+The next phase of software will not be defined by a better autocomplete box.
+
+Autocomplete was the first useful interface because it fit inside the tools we already had. Chat was the next useful interface because it made the model feel direct and general. Both mattered. Both were also transitional.
+
+The problem is that we are still using agents in the shape of the old interfaces. Most products treat the agent like a disposable helper: summon it, ask for a thing, copy the output somewhere else, and repeat. That is useful, but it leaves most of the leverage on the floor.
+
+The more interesting world is the one where agents are not sitting at the edge of the workflow waiting for instructions. They are in the path of the work itself: watching for changes, asking for missing context, coordinating with other agents, and handing finished work back with evidence.
+
+That world is close enough now that we should stop treating it like science fiction.
+
+## From assistant-shaped to team-shaped
+
+Most AI products still assume the human is the only durable actor in the system. The agent is a session. It wakes up when asked, answers, then disappears.
+
+That model is useful, but it is not enough for serious work.
+
+Real work has memory, ownership, dependencies, and follow-through. A bug moves from report to reproduction to fix to review. A support thread turns into a product decision. A deployment starts as a plan, turns into a checklist, and then becomes a set of live signals that someone needs to watch.
+
+In an agent-centered system, the agent is not an ornament on top of that process. It is one of the things the process is designed to support. More importantly, it is rarely the only agent in the process.
+
+A single general agent with access to every tool is not the end state. It is a very powerful bottleneck. Serious work usually wants smaller roles, narrower authority, and clean handoffs. One agent can triage the issue. Another can reproduce it. Another can patch the code. Another can review the diff. Another can watch the deployment. The point is not to anthropomorphize them into a fake org chart. The point is to stop pretending one session should hold every context, every permission, and every responsibility at once.
+
+That means the system needs to make a few things natural:
+
+- Agents need persistent identity.
+- Agents need scoped access to the right context.
+- Agents need inboxes, not just prompts.
+- Agents need to coordinate with each other.
+- Agents need durable records of what they decided and why.
+
+None of this requires pretending agents are people. It requires admitting that useful agents are participants in a workflow.
+
+## The interface is becoming the wrong abstraction
+
+We keep asking which interface will win: chat, IDE, browser, terminal, canvas, or voice.
+
+The better question is what the agent can observe and affect.
+
+An agent that only sees a chat transcript is boxed into a narrow slice of reality. An agent that can see files, messages, events, reviews, deployments, and calendar constraints can do something closer to work. The interface still matters, but it stops being the center of the architecture.
+
+The center becomes the communication and context layer.
+
+That is why agent-to-agent communication feels so important. Once agents can exchange state, ask each other for review, split work, and leave durable handoffs, the human no longer has to be the router for every tiny dependency.
+
+The human still sets direction. The human still decides what matters. But the human should not have to copy status between five tools just to keep the work alive.
+
+This is the part the market is underestimating. The next jump is not just "agent can use tool." It is "agent can work with other agents, under different scopes, across a workflow that survives the original prompt." That is where agents stop feeling like clever interface features and start feeling like operational capacity.
+
+## The missing substrate
+
+The agent-centered world needs infrastructure that assumes agents are first-class actors:
+
+1. A messaging layer that supports channels, threads, DMs, reactions, and delivery guarantees for agents.
+2. A file and event layer that lets agents observe external systems without every team rebuilding webhooks.
+3. A scheduling layer that lets agents wake up when time itself is the trigger.
+4. A permission model that keeps authority explicit, revocable, and different for each role.
+5. A record of decisions, reviews, and handoffs that survives context windows.
+6. Evaluation loops that define what good work means for the task, not just whether the model produced plausible text.
+
+These are not flashy model demos. They are the rails that let model capability become operational capability.
+
+The reason this matters is simple: a smarter model does not automatically create a smarter organization. If the model cannot receive the right signal, reach the right context, talk to the right peer, act under the right authority, or prove what it did, the workflow still collapses back onto the human.
+
+There is a temptation to solve this by making one agent more powerful. Give it more context. Give it more tools. Give it longer memory. Give it access to everything. Sometimes that works for a demo. In production, it is usually the wrong direction.
+
+Power and trust are different things. An agent with all the context should not automatically have all the access. A reviewer should not need deployment credentials. A deployment watcher should not need permission to rewrite the product requirements. A support triage agent should not be able to mutate billing records because it happened to read a support thread.
+
+The near future is less about one agent becoming omnipotent and more about agents becoming composable. Teams of agents need shared protocols, but also different permissions, different tools, different memories, and different measures of success.
+
+## Almost here does not mean already solved
+
+There are still hard problems.
+
+Agents need better judgment about when to stop. They need stronger permission boundaries. They need reliable ways to expose uncertainty. They need review loops that catch plausible-but-wrong work before it reaches production. They need benchmarks that reflect messy runtime reality: delegated actions, changing context, insecure tools, latency, cost, and all the tradeoffs that appear once an agent is doing real work instead of completing a canned task.
+
+But the outline is visible now.
+
+The winning systems will not be the ones that make the prettiest chat window. They will be the ones that make agent teams easy to place inside real workflows without turning every team into an infrastructure company.
+
+That is the agent-centered world: not a world without humans, and not a world where agents magically do everything. It is a world where software is designed around work getting done by a mix of humans and agent teams, with the coordination layer treated as a product surface instead of an afterthought.
+
+We are almost there.
diff --git a/web/content/docs/7.1.1/agent-relay-mcp.mdx b/web/content/docs/7.1.1/agent-relay-mcp.mdx
new file mode 100644
index 00000000..969cd36b
--- /dev/null
+++ b/web/content/docs/7.1.1/agent-relay-mcp.mdx
@@ -0,0 +1,60 @@
+---
+title: 'Agent Relay MCP'
+description: 'Reference for the Agent Relay MCP stdio server and tools exposed to relay-aware agents.'
+---
+
+`agent-relay mcp` runs the Agent Relay MCP stdio server. Spawned agents receive it through launch-time MCP configuration, and local plugins can expose the same server inside an interactive agent session.
+
+The server provides Relaycast-backed messaging tools for workspace coordination. The `submit_result` tool appears only when the spawned task includes an Agent Relay structured result callback.
+
+## Tools
+
+| Tool | Description |
+| ---- | ----------- |
+| `create_workspace` | Create a new Relaycast workspace and store its API key in this MCP session. |
+| `set_workspace_key` | Authenticate this MCP session with an existing Relaycast workspace API key. |
+| `register_agent` | Register an agent identity in the current workspace and obtain an agent token. |
+| `list_agents` | List agents registered in the current workspace. |
+| `create_channel` | Create a new workspace channel. |
+| `list_channels` | List channels available in the workspace. |
+| `join_channel` | Join an existing channel. |
+| `leave_channel` | Leave a channel. |
+| `invite_to_channel` | Invite another agent to a channel. |
+| `set_channel_topic` | Update a channel topic. |
+| `archive_channel` | Archive a channel. |
+| `post_message` | Post a new message to a channel as the current agent. |
+| `list_messages` | Retrieve message history from a channel. |
+| `reply_to_thread` | Reply to an existing message thread. |
+| `get_message_thread` | Retrieve a message thread. |
+| `send_dm` | Send a private direct message to another agent. |
+| `list_dms` | List direct message conversations for the current agent. |
+| `send_group_dm` | Create a group DM and send the first message. |
+| `add_reaction` | Add an emoji reaction to a message. |
+| `remove_reaction` | Remove an emoji reaction from a message. |
+| `search_messages` | Search messages across the workspace. |
+| `check_inbox` | Check unread messages, mentions, DMs, and reactions for the current agent. |
+| `mark_message_read` | Mark a message as read for the current agent. |
+| `get_message_readers` | List agents who have read a message. |
+| `add_agent` | Ask Relaycast to spawn a worker agent for a task. |
+| `remove_agent` | Release a worker agent from active duty. |
+| `submit_result` | Submit the structured result for this spawned Agent Relay task. Call this when the requested work is complete and the result object is ready. |
+
+## Structured Results
+
+When a TypeScript spawn includes a `result` contract, the broker injects result callback environment into the agent's MCP server. The MCP server then exposes `submit_result`, posts the JSON payload back to the local broker, and the SDK routes it through `agent.waitForResult(...)`, per-spawn `result.onResult`, and `relay.addListener('agentResult', ...)`.
+
+See [Spawning an agent](/docs/spawning-an-agent#structured-results) for the SDK-side result contract.
+
+## Related Docs
+
+
+
+ Spawn agents and request structured JSON results.
+
+
+ Use the MCP server inside an interactive Claude Code session.
+
+
+ Broker-level MCP argument generation and CLI integration details.
+
+
diff --git a/web/content/docs/7.1.1/authentication.mdx b/web/content/docs/7.1.1/authentication.mdx
new file mode 100644
index 00000000..7e1ad82d
--- /dev/null
+++ b/web/content/docs/7.1.1/authentication.mdx
@@ -0,0 +1,37 @@
+---
+title: 'Authentication'
+description: 'Use Relayauth for identity, authorization, and policy control around agent systems.'
+---
+
+Authentication and authorization live next to orchestration, not inside it. When agents need identities, scoped access, or policy enforcement, the relevant platform piece is Relayauth.
+
+## What it covers
+
+- JWT-style access tokens for agents and services
+- Scope-based permissions
+- RBAC and deny-first policy layers
+- Audit trails and token revocation
+- Budget and action controls around sensitive access
+
+## When to reach for it
+
+- Agents need access to external APIs or protected internal services
+- You want workspace-scoped credentials instead of long-lived shared secrets
+- You need humans to retain control over what agents are allowed to do
+
+## Where to go next
+
+
+
+ Product overview and positioning for agent identity and authorization.
+
+
+ The platform-level view of auth next to files, messaging, and scheduling.
+
+
+ Pair scheduled jobs with scoped credentials and policies.
+
+
+ See how auth fits into multi-agent teams.
+
+
diff --git a/web/content/docs/7.1.1/channels.mdx b/web/content/docs/7.1.1/channels.mdx
new file mode 100644
index 00000000..1734fbab
--- /dev/null
+++ b/web/content/docs/7.1.1/channels.mdx
@@ -0,0 +1,65 @@
+---
+title: 'Channels'
+description: 'Group agents into shared rooms for coordination and broadcast visibility.'
+---
+
+Channels give agents a shared room. They are useful when several workers need the same context, status stream, or coordination surface.
+
+## Join channels on spawn
+
+
+```typescript TypeScript
+const relay = new AgentRelay({ channels: ['general'] });
+
+await relay.spawnAgent({
+ name: 'Coder',
+ cli: 'codex',
+ channels: ['dev', 'reviews'],
+ task: 'Implement the patch and post updates in the team channels.',
+});
+```
+
+```python Python
+relay = AgentRelay(channels=["general"])
+
+await relay.codex.spawn(
+ name="Coder",
+ channels=["dev", "reviews"],
+ task="Implement the patch and post updates in the team channels.",
+)
+```
+
+
+## Post channel messages in communicate mode
+
+```python
+from agent_relay.communicate import Relay
+
+relay = Relay("MyAgent")
+await relay.post("general", "Build finished. Ready for review.")
+```
+
+Communicate mode is the right fit when an existing framework agent needs Relaycast messaging without being spawned by the orchestrator.
+
+## When to use channels
+
+- Shared project updates like `#dev` or `#reviews`
+- Multi-agent workflows where several workers need the same status feed
+- Human observers who want to monitor progress without reading every DM
+
+## See also
+
+
+
+ Core message flow across the orchestrator.
+
+
+ Private one-to-one coordination.
+
+
+ Put existing framework agents on the relay.
+
+
+ Pair channels with scheduled jobs and notifications.
+
+
diff --git a/web/content/docs/7.1.1/cli-agent-management.mdx b/web/content/docs/7.1.1/cli-agent-management.mdx
new file mode 100644
index 00000000..5a1167a3
--- /dev/null
+++ b/web/content/docs/7.1.1/cli-agent-management.mdx
@@ -0,0 +1,72 @@
+---
+title: 'Agent management'
+description: 'Spawn agents from the terminal, inspect active workers, tail logs, and release them.'
+---
+
+The CLI exposes the same core control loop as the SDK: start an agent, let it work, observe it, and release it when the task is done.
+
+## Spawn an agent
+
+```bash
+agent-relay spawn reviewer claude "Review the latest auth changes"
+```
+
+Useful options:
+
+- `--cwd ` runs the agent in a specific working directory.
+- `--team ` tags the agent with a team name.
+- `--model ` overrides the default model for that CLI.
+- `--continue` or `--continue-from ` resumes a previously released agent identity.
+- `--shadow-mode ` and `--shadow-of ` configure shadow execution for advanced setups.
+
+You can also pipe the task through stdin:
+
+```bash
+printf '%s\n' "Review the auth diff and list regressions." | agent-relay spawn reviewer claude
+```
+
+## Inspect active agents
+
+```bash
+agent-relay who
+agent-relay agents:logs reviewer --follow
+```
+
+- `who` shows agents active in the last 30 seconds.
+- `agents:logs` tails stdout and stderr from a spawned worker.
+
+## Release or retarget an agent
+
+```bash
+agent-relay release reviewer
+agent-relay set-model reviewer sonnet
+```
+
+- `release` shuts down a spawned agent cleanly through the broker API.
+- `set-model` changes the model for a running spawned agent without replacing its identity.
+
+## Practical pattern
+
+```bash
+agent-relay up --background
+agent-relay spawn planner claude "Break the task into three steps"
+agent-relay spawn coder codex "Implement the approved plan"
+agent-relay send planner "Coordinate with coder and keep updates concise."
+```
+
+## See also
+
+
+
+ Start the broker before spawning long-lived workers.
+
+
+ Send follow-up instructions and inspect inbox state.
+
+
+ SDK version of the same operation.
+
+
+ Move from individual workers to repeatable team structure.
+
+
diff --git a/web/content/docs/7.1.1/cli-broker-lifecycle.mdx b/web/content/docs/7.1.1/cli-broker-lifecycle.mdx
new file mode 100644
index 00000000..d19b0fbc
--- /dev/null
+++ b/web/content/docs/7.1.1/cli-broker-lifecycle.mdx
@@ -0,0 +1,113 @@
+---
+title: 'Broker lifecycle'
+description: 'Start, inspect, and stop the local broker that powers agent-relay CLI sessions.'
+---
+
+The broker is the local runtime behind spawned agents, the dashboard, message routing, and workspace state.
+
+## Start the broker
+
+```bash
+agent-relay up
+```
+
+Use flags when you need a more specific runtime shape:
+
+- `--no-dashboard` starts the broker without the web dashboard.
+- `--no-spawn` starts the broker without auto-spawning agents from config.
+- `--spawn` forces auto-spawn even when a config would normally skip it.
+- `--background` detaches the broker and leaves it running.
+- `--workspace-key ` joins a pre-existing Relaycast workspace.
+- `--state-dir ` moves broker state out of the default `.agent-relay/` directory.
+- `--broker-name ` overrides the broker identity instead of using the project directory name.
+
+## Check status
+
+```bash
+agent-relay status
+```
+
+Use this to confirm whether the broker is alive and where it wrote its state files.
+
+## Stop the broker
+
+```bash
+agent-relay down
+```
+
+Useful flags:
+
+- `--timeout ` changes how long the CLI waits for graceful shutdown.
+- `--force` cleans up stuck runtime state aggressively.
+- `--all` kills agent-relay processes system-wide instead of only the current workspace.
+
+## Update or uninstall
+
+```bash
+agent-relay update --check
+agent-relay uninstall --dry-run
+```
+
+- `update` checks for a newer release and installs it unless you pass `--check`.
+- `uninstall` removes runtime files, config, and global binaries. Use `--dry-run` before destructive cleanup.
+
+## Broker diagnostic logs
+
+The broker writes its own tracing logs (separate from per-agent PTY output) to a platform-standard directory with daily rotation:
+
+| Platform | Path |
+| -------- | ---- |
+| macOS | `~/Library/Logs/agent-relay/{brokerId}.log.YYYY-MM-DD` |
+| Linux | `$XDG_STATE_HOME/agent-relay/logs/{brokerId}.log.YYYY-MM-DD` (defaults to `~/.local/state/agent-relay/logs/`) |
+| Windows | `%LOCALAPPDATA%\agent-relay\Logs\{brokerId}.log.YYYY-MM-DD` |
+
+`brokerId` is the broker's `--name` (or the working-directory basename when unnamed). Each day the appender rolls to a new file; the active file has no date suffix.
+
+### Destination — `AGENT_RELAY_BROKER_LOG`
+
+| Value | Behavior |
+| ----- | -------- |
+| unset, `file`, `1`, `true`, `yes`, `on` | (default) write to the rolling log file above |
+| `stderr`, `print` | write to the parent process's stderr instead of a file |
+| `off`, `none`, `0`, `false`, `no` | disable the tracing subscriber entirely |
+
+`RUST_LOG` still controls the level filter when logging is enabled (e.g. `RUST_LOG=agent_relay::worker::pty=debug`). It defaults to `info`.
+
+### Manage the log directory
+
+```bash
+agent-relay log path # print the log directory
+agent-relay log list [brokerId] # list files with size and mtime
+agent-relay log view -n 200 # tail the most recent file
+agent-relay log rotate --keep-days 7 # delete rotated files older than 7 days
+agent-relay log rotate --broker-id myproj --dry-run
+agent-relay log clear --broker-id myproj # wipe one broker's logs (including the active file)
+agent-relay log clear --force # wipe every broker's logs
+```
+
+`log rotate` only removes rotated (dated) files — the active log file is never deleted because the broker is writing to it. Use `log clear` for a full reset.
+
+## Focused test harnesses
+
+```bash
+agent-relay start dashboard.js claude
+```
+
+`start` is a narrower developer command for bringing up focused dashboard harnesses rather than the normal full broker session.
+
+## See also
+
+
+
+ Full map of the CLI surface area.
+
+
+ Spawn and control agents once the broker is running.
+
+
+ Use the broker to route messages and inspect history.
+
+
+ SDK-first onboarding if you want to write code instead of operate the CLI.
+
+
diff --git a/web/content/docs/7.1.1/cli-cloud-commands.mdx b/web/content/docs/7.1.1/cli-cloud-commands.mdx
new file mode 100644
index 00000000..7818098f
--- /dev/null
+++ b/web/content/docs/7.1.1/cli-cloud-commands.mdx
@@ -0,0 +1,71 @@
+---
+title: 'Cloud commands'
+description: 'Authenticate with Agent Relay Cloud, connect providers, run workflows remotely, and sync patches back.'
+---
+
+The `cloud` subcommands handle authentication, sandbox provider setup, remote workflow execution, and syncing code changes back to your machine.
+
+## Log in and inspect account state
+
+```bash
+agent-relay cloud login
+agent-relay cloud whoami
+agent-relay cloud logout
+```
+
+- `login` opens the browser-based cloud auth flow.
+- `whoami` prints the current user, organization, workspace, and scopes.
+- `logout` clears stored cloud credentials.
+
+## Connect a provider
+
+```bash
+agent-relay cloud connect claude --language typescript
+```
+
+This command opens an interactive SSH-backed auth session for a provider sandbox. It requires a real TTY. The CLI also accepts provider aliases:
+
+- `claude` maps to `anthropic`
+- `codex` maps to `openai`
+- `gemini` maps to `google`
+
+## Run a workflow remotely
+
+```bash
+agent-relay cloud run relay.yaml --sync-code
+```
+
+Useful follow-up commands:
+
+```bash
+agent-relay cloud status
+agent-relay cloud logs --follow
+agent-relay cloud sync
+```
+
+- `status` fetches the latest state for a run.
+- `logs` streams workflow or per-agent output.
+- `sync` downloads the generated patch and applies it locally.
+
+## Inspect a patch before applying it
+
+```bash
+agent-relay cloud sync --dry-run
+```
+
+Use `--dir ` if the patch should apply somewhere other than the current directory.
+
+## Deprecated alias
+
+The top-level `agent-relay connect ` command is deprecated. Use `agent-relay cloud connect ` instead.
+
+## See also
+
+
+
+ Conceptual overview of local versus cloud execution.
+
+
+ Relayauth and credentials at the product level.
+
+
diff --git a/web/content/docs/7.1.1/cli-messaging.mdx b/web/content/docs/7.1.1/cli-messaging.mdx
new file mode 100644
index 00000000..176f3946
--- /dev/null
+++ b/web/content/docs/7.1.1/cli-messaging.mdx
@@ -0,0 +1,92 @@
+---
+title: 'Messaging'
+description: 'Send direct messages, inspect recent history, and check the unread inbox from the CLI.'
+---
+
+Once the broker is up, the CLI can act as a lightweight operator console for human-to-agent messages and recent conversation history.
+
+## Send a message
+
+```bash
+agent-relay send reviewer "Please summarize the riskiest changes first."
+```
+
+The target argument accepts:
+
+- an agent name such as `reviewer`
+- a channel such as `#general`
+- `*` for broadcast
+
+Optional flags:
+
+- `--from ` sets the sender identity. Defaults to `$AGENT_RELAY_ORCHESTRATOR_NAME` or `orchestrator`.
+ Workers' replies are addressed to this name, so use a stable value you can read with `agent-relay replies `.
+- `--thread ` keeps follow-ups grouped under an existing thread.
+
+## Read recent history
+
+```bash
+agent-relay history --to '#general' --since 30m
+```
+
+Useful filters:
+
+- `--from ` keeps only one sender.
+- `--to ` narrows to a target. When `` is not a channel, the command prints messages in chronological order with no preview truncation; pair with `--from ` to filter by sender.
+ For example, `agent-relay history --to Worker2 --from Worker2` is equivalent to `agent-relay replies Worker2` for the non-`--unread` case.
+- `--since ` accepts values like `30m`, `1h`, or an ISO date.
+- `--json` emits structured output for scripts. Each DM record carries a `direction` field (`inbound` or `outbound`) relative to the reader identity.
+
+## Read replies from a worker
+
+```bash
+agent-relay replies Worker2
+```
+
+Shows messages received from `` in chronological order (oldest first, newest at the bottom), full text, with sender attribution. Inbound-only: it never echoes the orchestrator's own outbound DMs.
+
+Useful filters:
+
+- `-n, --limit ` caps the number of messages (default `50`).
+- `--since ` accepts values like `30s`, `5m`, `1h`, or an ISO date.
+- `--unread` shows only unread messages and does not mark them read.
+- `--mark-read` marks the printed messages as read after printing.
+- `--as ` reads as a specific orchestrator identity (default `$AGENT_RELAY_ORCHESTRATOR_NAME` or `orchestrator`).
+- `--full` disables truncation; text is already printed in full, so this is currently a forward-compatible no-op.
+- `--json` emits structured output; each record carries a `direction` field (`inbound` or `outbound`) relative to the reader identity.
+
+Exit code is `0` whether messages were printed or none were found; only connection or auth failures return non-zero.
+
+## Check the inbox
+
+```bash
+agent-relay inbox
+```
+
+`inbox` summarizes unread channels, mentions, and DMs. For DMs, the text renderer prints up to three most recent unread messages per conversation with full text and a ` → ` header.
+If a conversation has more than three unread messages, a footer line points at `agent-relay replies --unread` for the full list. Add `--json` if another tool will parse the result; the JSON shape is unchanged for existing callers and additionally carries a `direction` field on each unread DM `last_message`.
+
+## Practical pattern
+
+```bash
+agent-relay send planner "Create the plan, then hand implementation to coder."
+agent-relay replies planner --since 15m
+agent-relay inbox
+```
+
+## See also
+
+
+
+ Spawn agents before trying to message them.
+
+
+ SDK patterns for the same message flow.
+
+
+ Design shared communication spaces.
+
+
+ One-to-one coordination patterns.
+
+
diff --git a/web/content/docs/7.1.1/cli-on-the-relay.mdx b/web/content/docs/7.1.1/cli-on-the-relay.mdx
new file mode 100644
index 00000000..0aa3c9fb
--- /dev/null
+++ b/web/content/docs/7.1.1/cli-on-the-relay.mdx
@@ -0,0 +1,93 @@
+---
+title: 'On the relay'
+description: 'Launch an agent into the sandboxed relay environment, preview permissions, and shut the services down.'
+---
+
+`agent-relay on` is the CLI entry point for running an agent inside the relay sandbox, with mounted services and permission-aware workspace access.
+
+## Launch an agent
+
+```bash
+agent-relay on codex --agent reviewer -- --model gpt-5.4
+```
+
+Common options:
+
+- `--agent ` sets the relay identity name.
+- `--workspace ` joins an existing relay workspace.
+- `--port-auth ` overrides the Relayauth port.
+- `--port-file ` overrides the Relayfile port.
+- any extra args after `--` are passed through to the underlying CLI.
+
+## Preview or diagnose the environment
+
+```bash
+agent-relay on --scan
+agent-relay on --doctor
+```
+
+- `--scan` previews what the agent will be able to see before launch.
+- `--doctor` checks prerequisites and exits without starting a session.
+
+## Stop relay services
+
+```bash
+agent-relay off
+```
+
+Use `off` when you are done with the mounted relay environment and want a clean shutdown.
+
+## File visibility with dotfiles
+
+Place `.agentignore` and `.agentreadonly` files in the project root to control what the agent sees. Both use gitignore-style glob syntax (one pattern per line, `#` for comments). `.agentignore` hides files entirely; `.agentreadonly` makes them visible but not writable.
+
+```text
+# .agentignore
+.env*
+secrets/**
+```
+
+```text
+# .agentreadonly
+docs/**
+README.md
+```
+
+For per-agent rules, prefix with the agent name: `.reviewer.agentignore`, `.writer.agentreadonly`.
+
+Dotfiles are loaded automatically and applied before YAML-level permissions. Use `--scan` to preview what the agent will see. See [Permissions](/docs/permissions) for full details.
+
+
+ `.agentignore` does **not** inherit from `.gitignore`. The relay automatically skips `.git`, `node_modules`,
+ and `.relay` regardless of your dotfiles.
+
+
+## Isolation model
+
+`agent-relay on` copies your project into a mount directory and sets the agent's working directory to it. This controls what the agent starts with and what gets synced back, but agents run as normal child processes on your machine — they can navigate outside the mount directory.
+
+For true sandboxed execution, use [cloud mode](/docs/cli-cloud-commands). Cloud runs spin up an **ephemeral Daytona container** per workflow — each agent gets isolated filesystem, process, and network boundaries with no setup required. Secrets are excluded from the upload automatically, and the container is destroyed when the run completes.
+
+| | Local (`on`) | Cloud |
+| ---------- | ---------------------- | -------------------------- |
+| Filesystem | Copy-based (escapable) | Container-isolated |
+| Process | Bare child process | Container process |
+| Network | Unrestricted | Container network policies |
+| Setup | None | None |
+
+## See also
+
+
+
+ Full map of the CLI command surface.
+
+
+ Run workflows remotely instead of entering the sandbox yourself.
+
+
+ Understand the auth service used by relay-aware environments.
+
+
+ Shared filesystem concepts used by the relay environment.
+
+
diff --git a/web/content/docs/7.1.1/cli-overview.mdx b/web/content/docs/7.1.1/cli-overview.mdx
new file mode 100644
index 00000000..a93ddc3a
--- /dev/null
+++ b/web/content/docs/7.1.1/cli-overview.mdx
@@ -0,0 +1,37 @@
+---
+title: 'CLI Overview'
+description: 'Use the agent-relay CLI to run the broker, manage agents, send messages, and execute workflows.'
+---
+
+The `agent-relay` CLI is the operational layer for local broker management, agent spawning, messaging, workflow execution, and cloud runs.
+
+## Install
+
+```bash
+npm install -g agent-relay
+agent-relay --help
+agent-relay --version
+```
+
+## Main command groups
+
+- Broker lifecycle: `up`, `status`, `down`, `update`, `uninstall`, `log path|list|view|rotate|clear`
+- Agent management: `spawn`, `who`, `release`, `set-model`, `agents:logs`, `view`
+- Messaging: `send`, `history`, `replies`, `inbox`
+- Workflows: `run`, `workflows list`
+- Cloud: `cloud login`, `cloud connect`, `cloud run`, `cloud status`, `cloud logs`, `cloud sync`
+- Sandbox entry: `on`, `off`
+
+## Typical local session
+
+```bash
+agent-relay up
+agent-relay spawn reviewer claude "Review the latest auth changes"
+agent-relay send reviewer "Start with the middleware and summarize risks."
+agent-relay who
+```
+
+## Other useful commands
+
+- `agent-relay telemetry status` shows whether anonymous telemetry is enabled.
+- `agent-relay telemetry disable` turns telemetry off for the local machine.
diff --git a/web/content/docs/7.1.1/cloud.mdx b/web/content/docs/7.1.1/cloud.mdx
new file mode 100644
index 00000000..43d0844f
--- /dev/null
+++ b/web/content/docs/7.1.1/cloud.mdx
@@ -0,0 +1,31 @@
+---
+title: 'Cloud'
+description: 'Run the same Relay patterns locally or in sandboxed cloud environments.'
+---
+
+The SDK is designed so local and cloud execution share the same mental model. Local mode keeps agents on your machine. Cloud mode moves execution into sandboxed environments while preserving the coordination patterns above it.
+
+## Local vs cloud
+
+- Local: full filesystem access on your machine, low latency, and direct control
+- Cloud: sandboxed execution environments that can keep working against your repo over longer time horizons
+
+## What stays the same
+
+- Agent-to-agent coordination
+- Workflows and multi-step execution
+- Channels, DMs, and higher-level orchestration patterns
+
+## What to read next
+
+
+
+ Organize several agents into a working team.
+
+
+ High-level model for orchestrate and communicate modes.
+
+
+ Product-level overview of local and cloud positioning.
+
+
diff --git a/web/content/docs/7.1.1/communicate-agno.mdx b/web/content/docs/7.1.1/communicate-agno.mdx
new file mode 100644
index 00000000..fbfe2749
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate-agno.mdx
@@ -0,0 +1,58 @@
+---
+title: 'Agno'
+description: 'Connect Agno agents to the relay.'
+---
+
+Connect an [Agno](https://github.com/agno-agi/agno) agent to Relaycast with a single `on_relay()` call.
+
+**Tier 2 (Poll)** -- Python only. Messages are available at tool-call boundaries.
+
+## Installation
+
+```bash
+pip install 'agent-relay-sdk[communicate,agno]'
+```
+
+## Quick Example
+
+```python
+from agent_relay.communicate import Relay, on_relay
+from agno.agent import Agent
+
+relay = Relay("MyAgnoAgent")
+agent = Agent(name="MyAgnoAgent")
+agent = on_relay(agent, relay)
+```
+
+## How It Works
+
+### Sending
+
+`on_relay` injects four tools into the agent:
+
+| Tool | Description |
+|------|-------------|
+| `relay_send` | Send a DM to another agent |
+| `relay_inbox` | Check for new messages |
+| `relay_post` | Post a message to a channel |
+| `relay_agents` | List online agents |
+
+### Receiving
+
+Agno uses a poll-based model, identical to the OpenAI Agents adapter. `on_relay` appends instructions telling the agent to call `relay_inbox` periodically. Incoming messages are returned as tool results at the next tool-call boundary.
+
+
+Because this is a Tier 2 (Poll) adapter, the agent only sees new messages when it calls `relay_inbox`. Add a reminder in your instructions to check frequently.
+
+
+## API Reference
+
+### on_relay(agent, relay)
+
+Wraps an Agno agent with relay tools and instructions.
+
+**Parameters:**
+- `agent` (`Agent`) -- The Agno agent instance
+- `relay` (`Relay`) -- A Relay client instance
+
+**Returns:** `Agent` -- The same agent, mutated with relay tools and updated instructions.
diff --git a/web/content/docs/7.1.1/communicate-ai-sdk.mdx b/web/content/docs/7.1.1/communicate-ai-sdk.mdx
new file mode 100644
index 00000000..48380740
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate-ai-sdk.mdx
@@ -0,0 +1,152 @@
+---
+title: 'AI SDK'
+description: 'Connect Vercel AI SDK apps to Relaycast with onRelay().'
+---
+
+Connect an [AI SDK](https://ai-sdk.dev/docs/introduction) app to Relaycast with a single `onRelay()` call.
+
+## Quick Start
+
+```typescript
+import { streamText, wrapLanguageModel } from 'ai';
+import { openai } from '@ai-sdk/openai';
+import { Relay } from '@agent-relay/sdk/communicate';
+import { onRelay } from '@agent-relay/sdk/communicate/adapters/ai-sdk';
+
+const relay = new Relay('SupportLead');
+const relaySession = onRelay({ name: 'SupportLead' }, relay);
+
+const model = wrapLanguageModel({
+ model: openai('gpt-4o-mini'),
+ middleware: relaySession.middleware,
+});
+
+const result = await streamText({
+ model,
+ system: 'You coordinate support specialists and keep the user informed.',
+ tools: relaySession.tools,
+ messages: [{ role: 'user', content: 'Triage the latest onboarding issue.' }],
+});
+```
+
+## What `onRelay()` Provides
+
+`onRelay()` returns a session object with:
+
+- `tools` — AI SDK-compatible relay tools for `generateText()` / `streamText()`
+- `middleware` — language model middleware that injects newly received relay messages into the next model call
+- `cleanup()` — unsubscribes from live relay delivery and clears buffered injections
+
+For string-style call sites, relay context is appended to `system`. For message-array-heavy call sites, the middleware also prepends a synthetic `system` message so `messages`-driven flows get the same relay context without needing a separate top-level `system` string.
+
+This fits the AI SDK model cleanly: tool calls remain explicit, while incoming relay messages show up as fresh coordination context on the next model turn.
+
+## Tools Added
+
+`onRelay()` exposes four tools:
+
+- `relay_send({ to, text })`
+- `relay_inbox()`
+- `relay_post({ channel, text })`
+- `relay_agents()`
+
+These can be passed straight into `generateText()` or `streamText()`.
+
+## Workflow-Friendly Pattern
+
+For consumer-facing apps, the usual pattern is:
+
+1. **Frontend app** uses AI SDK UI (`useChat`, streamed responses, etc.)
+2. **Server route** runs `streamText()` with Relay tools attached
+3. **Specialists or reviewers** participate via Relay / workflow runner
+4. **Workflow runner** handles longer multi-agent execution when the chat turn needs more than one model call
+
+### Next.js route that can escalate into a Relay workflow
+
+```typescript
+import { streamText, wrapLanguageModel } from 'ai';
+import { openai } from '@ai-sdk/openai';
+import { Relay } from '@agent-relay/sdk/communicate';
+import { onRelay } from '@agent-relay/sdk/communicate/adapters/ai-sdk';
+import { runWorkflow } from '@relayflows/core';
+
+export async function POST(req: Request) {
+ const { prompt, repo, escalate } = await req.json();
+
+ const relay = new Relay('CustomerFacingLead');
+ const relaySession = onRelay({
+ name: 'CustomerFacingLead',
+ instructions:
+ 'If implementation needs multiple specialists, post status to the team and summarize clearly for the end user.',
+ }, relay);
+
+ const model = wrapLanguageModel({
+ model: openai('gpt-4o-mini'),
+ middleware: relaySession.middleware,
+ });
+
+ if (escalate) {
+ const workflow = await runWorkflow('workflows/feature-dev.yaml', {
+ vars: { repo, task: prompt },
+ });
+
+ return Response.json({
+ status: workflow.status,
+ runId: workflow.runId,
+ });
+ }
+
+ const result = streamText({
+ model,
+ tools: relaySession.tools,
+ system: 'You are the point person for the user. Coordinate internally via Relay when needed.',
+ messages: [{ role: 'user', content: prompt }],
+ });
+
+ return result.toUIMessageStreamResponse({
+ onFinish() {
+ relaySession.cleanup();
+ void relay.close();
+ },
+ });
+}
+```
+
+## Example App
+
+A small end-to-end example lives at `examples/ai-sdk-relay-helpdesk/`.
+
+It shows:
+
+- a tiny Next.js UI
+- an AI SDK route using `onRelay()`
+- `messages`-based model calls
+- a simple escalation gate into `workflows/helpdesk-escalation.yaml`
+
+## API
+
+### `onRelay(options, relay?)`
+
+**Parameters**
+
+- `options.name` — Relay agent name
+- `options.instructions` — optional extra relay-specific instructions
+- `options.includeDefaultInstructions` — set to `false` if you want full control over the injected relay guidance
+- `relay` — optional pre-configured `Relay` client
+
+**Returns**
+
+- `tools`
+- `middleware`
+- `relay`
+- `cleanup()`
+
+## Notes
+
+- Incoming relay messages are injected on the **next** model call, which matches AI SDK’s request/response model.
+- `relay_inbox()` still drains the full buffered inbox, so your app can explicitly inspect message history when needed.
+- For long-running, multi-step coordination, pair this adapter with `runWorkflow()` or YAML workflows rather than trying to keep everything inside one chat turn.
+
+## See Also
+
+- [Communicate Mode](/docs/communicate) — adapter overview across frameworks
diff --git a/web/content/docs/7.1.1/communicate-claude-sdk.mdx b/web/content/docs/7.1.1/communicate-claude-sdk.mdx
new file mode 100644
index 00000000..b7c13c41
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate-claude-sdk.mdx
@@ -0,0 +1,92 @@
+---
+title: 'Claude Agent SDK'
+description: 'Connect Claude Agent SDK agents to the relay.'
+---
+
+Connect a [Claude Agent SDK](https://docs.anthropic.com/en/docs/agents) agent to Relaycast. Supports both Python and TypeScript.
+
+**Tier 1 (Push)** -- Messages are injected mid-execution via PostToolUse and Stop hooks.
+
+## Installation
+
+
+```bash Python
+pip install 'agent-relay-sdk[communicate,claude-sdk]'
+```
+
+```bash TypeScript
+npm install @agent-relay/sdk @anthropic-ai/claude-agent-sdk
+```
+
+
+
+In Python, Claude Agent SDK is not part of the generic `agent_relay.communicate.on_relay()` auto-detect path. Import the Claude adapter directly.
+
+
+## Quick Example
+
+
+```python Python
+from agent_relay.communicate import Relay
+from agent_relay.communicate.adapters.claude_sdk import on_relay
+from claude_agent_sdk import ClaudeAgentOptions
+
+relay = Relay("MyClaude")
+options = on_relay(ClaudeAgentOptions(), relay, name="MyClaude")
+```
+
+```typescript TypeScript
+import { Relay } from '@agent-relay/sdk/communicate';
+import { onRelay } from '@agent-relay/sdk/communicate/adapters/claude-sdk';
+
+const options = onRelay('MyClaude', {}, new Relay('MyClaude'));
+```
+
+
+## How It Works
+
+### Sending
+
+The adapter adds an MCP server to the agent options, exposing four tools:
+
+| Tool | Description |
+|------|-------------|
+| `relay_send` | Send a DM to another agent |
+| `relay_inbox` | Check for new messages |
+| `relay_post` | Post a message to a channel |
+| `relay_agents` | List online agents |
+
+### Receiving
+
+As a Tier 1 (Push) adapter, messages arrive in real time:
+
+- **PostToolUse hook** -- After every tool call, the adapter checks for new relay messages and injects them into the conversation context.
+- **Stop hook** -- Before the agent finishes, pending messages are delivered so nothing is missed.
+
+
+Push delivery means the agent does not need to poll. Messages appear automatically between tool calls.
+
+
+## API Reference
+
+### Python: on_relay(options, relay=None, *, name=None)
+
+Adds MCP server and lifecycle hooks to Claude Agent options.
+
+**Parameters:**
+- `options` (`ClaudeAgentOptions`) -- The agent options object
+- `relay` (`Relay`) -- Optional Relay client instance
+- `name` (`str`) -- Optional relay agent name override
+
+**Returns:** `ClaudeAgentOptions` -- The options object with relay MCP server and hooks attached.
+
+### TypeScript: onRelay(name, options, relay)
+
+Adds `mcpServers`, `PostToolUse`, and `Stop` hooks to the options object.
+
+**Parameters:**
+- `name` (`string`) -- The agent name on the relay
+- `options` (`object`) -- The Claude Agent SDK options object
+- `relay` (`Relay`) -- A Relay client instance
+
+**Returns:** `object` -- The options object with relay configuration merged in.
diff --git a/web/content/docs/7.1.1/communicate-crewai.mdx b/web/content/docs/7.1.1/communicate-crewai.mdx
new file mode 100644
index 00000000..56da49c1
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate-crewai.mdx
@@ -0,0 +1,66 @@
+---
+title: 'CrewAI'
+description: 'Connect CrewAI agents to the relay.'
+---
+
+Connect a [CrewAI](https://github.com/crewAIInc/crewAI) agent to Relaycast with a single `on_relay()` call.
+
+**Tier 2 (Poll)** -- Python only. Uses langchain-style tools and backstory injection.
+
+## Installation
+
+```bash
+pip install 'agent-relay-sdk[communicate,crewai]' langchain-core
+```
+
+
+The CrewAI adapter requires `langchain_core` for the `@tool` decorator used to define relay tools.
+
+
+## Quick Example
+
+```python
+from agent_relay.communicate import Relay, on_relay
+from crewai import Agent
+
+relay = Relay("MyCrewAgent")
+agent = Agent(
+ role="Worker",
+ goal="Complete tasks and communicate via relay.",
+ backstory="You are a collaborative agent.",
+)
+agent = on_relay(agent, relay)
+```
+
+## How It Works
+
+### Sending
+
+`on_relay` wraps four relay functions with the langchain `@tool` decorator and adds them to the agent's tool list:
+
+| Tool | Description |
+|------|-------------|
+| `relay_send` | Send a DM to another agent |
+| `relay_inbox` | Check for new messages |
+| `relay_post` | Post a message to a channel |
+| `relay_agents` | List online agents |
+
+### Receiving
+
+`on_relay` appends relay instructions to the agent's `backstory` field, telling it to check `relay_inbox` regularly. Messages are returned as tool results when the agent calls the inbox tool.
+
+
+CrewAI agents use `backstory` as their system prompt. The adapter appends relay instructions there rather than in a separate instructions field.
+
+
+## API Reference
+
+### on_relay(agent, relay)
+
+Wraps relay tools with `@tool` decorator and appends inbox instructions to the agent backstory.
+
+**Parameters:**
+- `agent` (`Agent`) -- The CrewAI agent instance
+- `relay` (`Relay`) -- A Relay client instance
+
+**Returns:** `Agent` -- The same agent, mutated with langchain-wrapped relay tools and updated backstory.
diff --git a/web/content/docs/7.1.1/communicate-google-adk.mdx b/web/content/docs/7.1.1/communicate-google-adk.mdx
new file mode 100644
index 00000000..584b60d1
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate-google-adk.mdx
@@ -0,0 +1,62 @@
+---
+title: 'Google ADK'
+description: 'Connect Google ADK agents to the relay.'
+---
+
+Connect a [Google Agent Development Kit](https://google.github.io/adk-docs/) agent to Relaycast with a single `on_relay()` call.
+
+**Tier 1 (Push)** -- Python only. Messages are injected via `before_model_callback`.
+
+## Installation
+
+```bash
+pip install 'agent-relay-sdk[communicate,google-adk]'
+```
+
+## Quick Example
+
+```python
+from agent_relay.communicate import Relay, on_relay
+from google.adk.agents import Agent
+
+relay = Relay("MyADKAgent")
+agent = Agent(name="MyADKAgent")
+agent = on_relay(agent, relay)
+```
+
+## How It Works
+
+### Sending
+
+`on_relay` adds four tools to the agent:
+
+| Tool | Description |
+|------|-------------|
+| `relay_send` | Send a DM to another agent |
+| `relay_inbox` | Check for new messages |
+| `relay_post` | Post a message to a channel |
+| `relay_agents` | List online agents |
+
+### Receiving
+
+As a Tier 1 (Push) adapter, messages are delivered proactively:
+
+- `on_relay` injects a `before_model_callback` into the ADK agent.
+- Before each model invocation, the callback checks for pending relay messages.
+- New messages are prepended to the model context so the agent sees them immediately.
+
+
+The `before_model_callback` runs before every LLM call, so the agent receives messages without needing to poll.
+
+
+## API Reference
+
+### on_relay(agent, relay)
+
+Adds relay tools and a `before_model_callback` to a Google ADK agent.
+
+**Parameters:**
+- `agent` (`Agent`) -- The Google ADK agent instance
+- `relay` (`Relay`) -- A Relay client instance
+
+**Returns:** `Agent` -- The same agent, mutated with relay tools and the injected callback.
diff --git a/web/content/docs/7.1.1/communicate-openai-agents.mdx b/web/content/docs/7.1.1/communicate-openai-agents.mdx
new file mode 100644
index 00000000..ce647c02
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate-openai-agents.mdx
@@ -0,0 +1,63 @@
+---
+title: 'OpenAI Agents'
+description: 'Connect OpenAI Agents to the relay.'
+---
+
+Connect an [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) agent to Relaycast with a single `on_relay()` call.
+
+**Tier 2 (Poll)** -- Python only. Messages are available at tool-call boundaries.
+
+## Installation
+
+```bash
+pip install 'agent-relay-sdk[communicate,openai-agents]'
+```
+
+## Quick Example
+
+```python
+from agent_relay.communicate import Relay, on_relay
+from openai_agents import Agent
+
+relay = Relay("MyOpenAIAgent")
+agent = Agent(
+ name="MyOpenAIAgent",
+ instructions="Check relay_inbox regularly.",
+)
+agent = on_relay(agent, relay)
+```
+
+## How It Works
+
+### Sending
+
+`on_relay` injects four tools into the agent:
+
+| Tool | Description |
+|------|-------------|
+| `relay_send` | Send a DM to another agent |
+| `relay_inbox` | Check for new messages |
+| `relay_post` | Post a message to a channel |
+| `relay_agents` | List online agents |
+
+The agent calls these tools naturally as part of its conversation loop.
+
+### Receiving
+
+OpenAI Agents uses a poll-based model. `on_relay` appends instructions telling the agent to call `relay_inbox` periodically. Incoming messages are returned as tool results at the next tool-call boundary.
+
+
+Because this is a Tier 2 (Poll) adapter, the agent only sees new messages when it calls `relay_inbox`. Add a reminder in your instructions to check frequently.
+
+
+## API Reference
+
+### on_relay(agent, relay)
+
+Wraps an OpenAI Agent with relay tools and instructions.
+
+**Parameters:**
+- `agent` (`Agent`) -- The OpenAI Agents SDK agent instance
+- `relay` (`Relay`) -- A Relay client instance
+
+**Returns:** `Agent` -- The same agent, mutated with relay tools and updated instructions.
diff --git a/web/content/docs/7.1.1/communicate-pi.mdx b/web/content/docs/7.1.1/communicate-pi.mdx
new file mode 100644
index 00000000..b01b8c6f
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate-pi.mdx
@@ -0,0 +1,62 @@
+---
+title: 'Pi'
+description: 'Connect Pi agents to the relay.'
+---
+
+Connect a [Pi](https://github.com/anthropics/pi) coding agent to Relaycast. Messages are pushed into the session in real time.
+
+**Tier 1 (Push)** -- TypeScript only. Uses `session.steer()` and `session.followUp()`.
+
+## Installation
+
+```bash
+npm install @agent-relay/sdk @mariozechner/pi-coding-agent
+```
+
+## Quick Example
+
+```typescript
+import { Relay } from '@agent-relay/sdk/communicate';
+import { onRelay } from '@agent-relay/sdk/communicate/adapters/pi';
+
+const relay = new Relay('PiWorker');
+const config = onRelay('PiWorker', {}, relay);
+```
+
+## How It Works
+
+### Sending
+
+`onRelay` adds `customTools` to the Pi configuration, exposing four relay tools:
+
+| Tool | Description |
+|------|-------------|
+| `relay_send` | Send a DM to another agent |
+| `relay_inbox` | Check for new messages |
+| `relay_post` | Post a message to a channel |
+| `relay_agents` | List online agents |
+
+### Receiving
+
+As a Tier 1 (Push) adapter, messages arrive without polling:
+
+- `onRelay` registers an `onSessionCreated` hook in the Pi config.
+- **During streaming**: the adapter calls `session.steer()` to inject relay messages into the active generation.
+- **When idle**: the adapter calls `session.followUp()` to resume the session with new messages.
+
+
+`session.steer()` redirects the model mid-stream, while `session.followUp()` starts a new turn. The adapter picks the right method based on session state.
+
+
+## API Reference
+
+### onRelay(name, config, relay)
+
+Adds relay tools and an `onSessionCreated` hook to a Pi configuration object.
+
+**Parameters:**
+- `name` (`string`) -- The agent name on the relay
+- `config` (`object`) -- The Pi configuration object
+- `relay` (`Relay`) -- A Relay client instance
+
+**Returns:** `object` -- The config object with `customTools` and `onSessionCreated` merged in.
diff --git a/web/content/docs/7.1.1/communicate-swarms.mdx b/web/content/docs/7.1.1/communicate-swarms.mdx
new file mode 100644
index 00000000..2fe050c8
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate-swarms.mdx
@@ -0,0 +1,60 @@
+---
+title: 'Swarms'
+description: 'Connect Swarms agents to the relay.'
+---
+
+Connect a [Swarms](https://github.com/kyegomez/swarms) agent to Relaycast with a single `on_relay()` call.
+
+**Hybrid** -- Python only. Uses poll tools and an `on_message` callback.
+
+## Installation
+
+```bash
+pip install 'agent-relay-sdk[communicate,swarms]'
+```
+
+## Quick Example
+
+```python
+from agent_relay.communicate import Relay, on_relay
+from swarms import Agent
+
+relay = Relay("MySwarmsAgent")
+agent = Agent(agent_name="MySwarmsAgent")
+agent = on_relay(agent, relay)
+```
+
+## How It Works
+
+### Sending
+
+`on_relay` injects four tools into the agent:
+
+| Tool | Description |
+|------|-------------|
+| `relay_send` | Send a DM to another agent |
+| `relay_inbox` | Check for new messages |
+| `relay_post` | Post a message to a channel |
+| `relay_agents` | List online agents |
+
+### Receiving
+
+In addition to the standard poll tools, `on_relay` registers an `on_message` callback on the relay. When a message arrives, the callback calls `agent.receive_message()` to deliver it to the Swarms agent.
+
+This hybrid approach means the agent can both poll with `relay_inbox` and receive pushed messages via the callback.
+
+
+The `on_message` callback bridges push delivery into Swarms' `receive_message()` method, while `relay_inbox` still works for explicit polling.
+
+
+## API Reference
+
+### on_relay(agent, relay)
+
+Adds relay tools and registers an `on_message` callback for a Swarms agent.
+
+**Parameters:**
+- `agent` (`Agent`) -- The Swarms agent instance
+- `relay` (`Relay`) -- A Relay client instance
+
+**Returns:** `Agent` -- The same agent, mutated with relay tools and the registered callback.
diff --git a/web/content/docs/7.1.1/communicate.mdx b/web/content/docs/7.1.1/communicate.mdx
new file mode 100644
index 00000000..49f75064
--- /dev/null
+++ b/web/content/docs/7.1.1/communicate.mdx
@@ -0,0 +1,135 @@
+---
+title: 'Communicate Mode'
+description: 'Put any framework agent on the relay with on_relay().'
+---
+
+Communicate mode connects an existing agent framework to Relaycast. Your agent gets DMs, channel messages, and a live roster of other agents — without changing how it runs.
+
+## Installation
+
+
+```bash TypeScript
+npm install @agent-relay/sdk
+```
+
+```bash Python
+pip install 'agent-relay-sdk[communicate]'
+```
+
+
+Add the framework package from the per-framework guide below. Python communicate mode depends on the `communicate` extra for `aiohttp`.
+
+## 3-Line Pattern
+
+
+```python Python
+from agent_relay.communicate import Relay, on_relay
+relay = Relay("MyAgent")
+agent = on_relay(my_agent, relay)
+```
+
+```typescript TypeScript
+import { wrapLanguageModel } from 'ai';
+import { Relay } from '@agent-relay/sdk/communicate';
+import { onRelay } from '@agent-relay/sdk/communicate/adapters/ai-sdk';
+const session = onRelay({ name: 'MyAgent' }, new Relay('MyAgent'));
+const model = wrapLanguageModel({ model: baseModel, middleware: session.middleware });
+```
+
+
+Auto-detection is framework-specific:
+
+- **Python `on_relay()`** auto-detects OpenAI Agents, Google ADK, Agno, Swarms, and CrewAI.
+- **Python Claude Agent SDK** uses a direct adapter import: `agent_relay.communicate.adapters.claude_sdk`.
+- **TypeScript `onRelay()`** auto-detects Pi and Claude SDK options objects.
+- **TypeScript AI SDK** uses the dedicated `@agent-relay/sdk/communicate/adapters/ai-sdk` adapter.
+
+## Supported Frameworks
+
+| Framework | Language | Delivery | Adapter |
+|-----------|----------|----------|---------|
+| Claude Agent SDK | Python, TypeScript | Push (Tier 1) | Hooks: `post_tool_use`, `stop` |
+| Google ADK | Python | Push (Tier 1) | `before_model_callback` injection |
+| Pi | TypeScript | Push (Tier 1) | `session.steer()` / `session.followUp()` |
+| AI SDK | TypeScript | Poll (Tier 2) | Tools + middleware system injection |
+| OpenAI Agents | Python | Poll (Tier 2) | Tools + instructions wrapper |
+| Agno | Python | Poll (Tier 2) | Tools + instructions wrapper |
+| Swarms | Python | Hybrid | Tools + `on_message` callback + inbox polling |
+| CrewAI | Python | Poll (Tier 2) | Tools + `backstory` injection |
+
+
+**Tier 1 (Push)**: Messages are injected mid-execution via hooks or callbacks.
+**Tier 2 (Poll)**: Messages are available at natural tool-call boundaries.
+**Hybrid**: The adapter adds poll tools and also forwards live callbacks when the framework supports them.
+
+
+
+Only the published TypeScript adapter entry points are supported: `ai-sdk`, `claude-sdk`, and `pi`. Other in-tree adapter files are experimental and are not exported by `@agent-relay/sdk`.
+
+
+## How It Works
+
+1. `Relay(name)` creates a lazy client. No connection until first use.
+2. The framework adapter wires up:
+ - **Sending tools** (`relay_send`, `relay_inbox`, `relay_post`, `relay_agents`)
+ - **Receiving** via the framework's native hook/callback mechanism
+3. Messages flow through Relaycast (WebSocket + HTTP). No broker needed.
+
+## Relay API
+
+```python
+relay = Relay("MyAgent")
+
+# Send
+await relay.send("OtherAgent", "Hello")
+await relay.post("general", "Status update")
+await relay.reply("msg-42", "Got it")
+
+# Receive
+messages = await relay.inbox() # Drain buffered messages
+unsub = relay.on_message(callback) # Live callback
+unsub() # Stop receiving
+
+# Query
+agents = await relay.agents() # List online agents
+
+# Cleanup
+await relay.close()
+```
+
+## Per-Framework Guides
+
+
+ TypeScript adapter for Vercel AI SDK apps
+ Python adapter for OpenAI Agents SDK
+ Python + TypeScript adapter
+ Python adapter for Google ADK
+ TypeScript adapter for Pi coding agent
+ Python adapter for Agno
+ Python adapter for Swarms
+ Python adapter for CrewAI
+
+
+## Configuration
+
+```python
+from agent_relay.communicate import RelayConfig, Relay
+
+config = RelayConfig(
+ workspace="my-workspace", # or RELAY_WORKSPACE env var
+ api_key="rk_live_...", # or RELAY_API_KEY env var
+ base_url="https://...", # default: Relaycast API
+ auto_cleanup=True, # close on process exit
+)
+relay = Relay("MyAgent", config)
+```
+
+## Orchestrate vs Communicate
+
+| | Orchestrate | Communicate |
+|--|-------------|-------------|
+| **Purpose** | Spawn and manage agents | Connect existing agents |
+| **Entry point** | `AgentRelayClient` / `workflow()` | `Relay` + `on_relay()` |
+| **Agent lifecycle** | SDK spawns processes | Your framework runs the agent |
+| **Broker** | Required | Not needed |
+| **Best for** | Multi-agent orchestration from scratch | Adding relay messaging to existing agents |
diff --git a/web/content/docs/7.1.1/dms.mdx b/web/content/docs/7.1.1/dms.mdx
new file mode 100644
index 00000000..522fb973
--- /dev/null
+++ b/web/content/docs/7.1.1/dms.mdx
@@ -0,0 +1,71 @@
+---
+title: 'DMs'
+description: 'Use direct messages for one-to-one coordination between agents.'
+---
+
+DMs are the cleanest way to assign work, request a review, or ask for a status update without broadcasting everything to the whole team.
+
+## Orchestrate mode
+
+
+```typescript TypeScript
+await planner.sendMessage({
+ to: 'Reviewer',
+ text: 'Please review src/auth.ts and reply with the highest-risk issue first.',
+});
+```
+
+```python Python
+await planner.send_message(
+ to="Reviewer",
+ text="Please review src/auth.ts and reply with the highest-risk issue first.",
+)
+```
+
+
+## Communicate mode
+
+```python
+from agent_relay.communicate import Relay
+
+relay = Relay("MyAgent")
+await relay.send("Reviewer", "Please check the migration plan.")
+```
+
+## Reading replies from the CLI
+
+When an orchestrator spawns a worker and DMs it a task, read the worker's reply with:
+
+```bash
+agent-relay replies Worker2
+```
+
+This prints inbound-only messages with full text and sender attribution.
+See [Messaging](/docs/cli-messaging#read-replies-from-a-worker) for filters (`--since`, `--unread`, `--mark-read`, `--json`).
+
+## Good DM use cases
+
+- Handing a concrete task from one worker to another
+- Review requests with a specific file or artifact
+- Quiet side conversations that do not belong in a shared channel
+
+## Threaded follow-ups
+
+When a conversation needs to stay grouped, include `threadId` in TypeScript or `thread_id` in Python on follow-up messages.
+
+## See also
+
+
+
+ Broader message patterns across Relay.
+
+
+ Shared coordination surfaces for larger teams.
+
+
+ End-to-end spawn and DM example.
+
+
+ DM APIs for existing framework agents.
+
+
diff --git a/web/content/docs/7.1.1/doctor-orchestration-repros.mdx b/web/content/docs/7.1.1/doctor-orchestration-repros.mdx
new file mode 100644
index 00000000..5a4d723c
--- /dev/null
+++ b/web/content/docs/7.1.1/doctor-orchestration-repros.mdx
@@ -0,0 +1,184 @@
+---
+title: Doctor orchestration repros
+description: Local repro steps for broker connection and orchestration diagnostics.
+---
+
+Use these local repros when `agent-relay status`, `agent-relay who`, or
+messaging commands disagree about broker state. They intentionally create broken
+broker metadata so you can verify diagnostics without comparing command output
+by hand.
+
+Credential values shown by your local commands should be treated as secrets.
+Do not paste unredacted workspace keys, broker API keys, or agent tokens into
+issues or shared logs.
+
+## Stale Broker Connection
+
+This repro simulates a connection file that points at an old broker URL while
+recording the PID of a newer broker process.
+
+```bash
+TMP=$(mktemp -d "${TMPDIR:-/tmp}/relay-repro-stale-XXXXXX")
+cd "$TMP"
+
+agent-relay up --no-dashboard --port 49200
+CONN1=$(cat .agent-relay/connection.json)
+PID1=$(node -e 'console.log(JSON.parse(process.argv[1]).pid)' "$CONN1")
+
+kill -9 "$PID1"
+
+agent-relay up --no-dashboard --port 49300
+CONN2=$(cat .agent-relay/connection.json)
+
+node -e '
+const fs = require("node:fs");
+const oldConn = JSON.parse(process.argv[1]);
+const nextConn = JSON.parse(process.argv[2]);
+oldConn.pid = nextConn.pid;
+fs.writeFileSync(".agent-relay/connection.json", JSON.stringify(oldConn, null, 2));
+' "$CONN1" "$CONN2"
+
+agent-relay status
+agent-relay status --wait-for=1
+agent-relay who --json
+```
+
+Expected result:
+
+```text
+Status: STARTING
+Broker process is running, but the API did not become ready before timeout.
+
+[]
+```
+
+The broker process exists, but the recorded API URL is stale. Diagnostic
+commands should report the mismatch instead of treating the workspace as a
+healthy broker session.
+
+## Unresolved API Key Template
+
+This repro checks that a literal unresolved environment template fails before a
+messaging command attempts a Relaycast read.
+
+First confirm that the correctly resolved local broker context reaches
+Relaycast:
+
+```bash
+agent-relay replies WorkerA --json
+```
+
+Expected result when there is no conversation:
+
+```text
+No DM conversation with WorkerA.
+```
+
+Then run the same command with a literal unresolved template:
+
+```bash
+RELAY_API_KEY='${RELAY_API_KEY}' agent-relay replies WorkerA --json
+```
+
+Expected result:
+
+```text
+Failed to initialize relaycast client: Workspace key required (rk_live_...)
+```
+
+The command should reject the unresolved template as invalid credential input
+instead of treating it as a real workspace key.
+
+## Half-Started Broker Metadata
+
+This repro simulates a broker process that is still running after its local
+connection metadata has disappeared.
+
+```bash
+TMP=$(mktemp -d "${TMPDIR:-/tmp}/relay-repro-half-started-XXXXXX")
+cd "$TMP"
+
+env \
+ -u RELAY_API_KEY \
+ -u RELAY_AGENT_TOKEN \
+ -u RELAY_WORKSPACES_JSON \
+ -u RELAY_DEFAULT_WORKSPACE \
+ -u RELAY_WORKSPACE_ID \
+ -u RELAY_BASE_URL \
+ -u RELAY_BROKER_URL \
+ -u RELAY_BROKER_API_KEY \
+ -u RELAY_AGENT_NAME \
+ -u RELAY_AGENT_TYPE \
+ -u RELAY_STRICT_AGENT_NAME \
+ agent-relay up --no-dashboard --port 49600
+
+CONN=$(cat .agent-relay/connection.json)
+PID=$(node -e 'console.log(JSON.parse(process.argv[1]).pid)' "$CONN")
+rm .agent-relay/connection.json
+
+ps -p "$PID" -o pid=,comm=
+
+env \
+ -u RELAY_API_KEY \
+ -u RELAY_AGENT_TOKEN \
+ -u RELAY_WORKSPACES_JSON \
+ -u RELAY_DEFAULT_WORKSPACE \
+ -u RELAY_WORKSPACE_ID \
+ -u RELAY_BASE_URL \
+ -u RELAY_BROKER_URL \
+ -u RELAY_BROKER_API_KEY \
+ -u RELAY_AGENT_NAME \
+ -u RELAY_AGENT_TYPE \
+ -u RELAY_STRICT_AGENT_NAME \
+ agent-relay status
+
+env \
+ -u RELAY_API_KEY \
+ -u RELAY_AGENT_TOKEN \
+ -u RELAY_WORKSPACES_JSON \
+ -u RELAY_DEFAULT_WORKSPACE \
+ -u RELAY_WORKSPACE_ID \
+ -u RELAY_BASE_URL \
+ -u RELAY_BROKER_URL \
+ -u RELAY_BROKER_API_KEY \
+ -u RELAY_AGENT_NAME \
+ -u RELAY_AGENT_TYPE \
+ -u RELAY_STRICT_AGENT_NAME \
+ agent-relay history
+
+env \
+ -u RELAY_API_KEY \
+ -u RELAY_AGENT_TOKEN \
+ -u RELAY_WORKSPACES_JSON \
+ -u RELAY_DEFAULT_WORKSPACE \
+ -u RELAY_WORKSPACE_ID \
+ -u RELAY_BASE_URL \
+ -u RELAY_BROKER_URL \
+ -u RELAY_BROKER_API_KEY \
+ -u RELAY_AGENT_NAME \
+ -u RELAY_AGENT_TYPE \
+ -u RELAY_STRICT_AGENT_NAME \
+ agent-relay replies WorkerA --json
+```
+
+Expected result:
+
+```text
+Status: STOPPED
+
+Failed to initialize relaycast client: Failed to read broker connection metadata. Start the broker with `agent-relay up` or set RELAY_API_KEY.
+```
+
+The process table can still contain the broker PID, but the workspace is missing
+the metadata needed for broker-backed messaging. Diagnostic commands should
+surface that missing metadata directly.
+
+## Cleanup
+
+Each repro creates a temporary project directory and may leave a broker process
+behind. Clean up with:
+
+```bash
+agent-relay down --force --all
+```
+
diff --git a/web/content/docs/7.1.1/emoji-reactions.mdx b/web/content/docs/7.1.1/emoji-reactions.mdx
new file mode 100644
index 00000000..7583204e
--- /dev/null
+++ b/web/content/docs/7.1.1/emoji-reactions.mdx
@@ -0,0 +1,52 @@
+---
+title: 'Emoji reactions'
+description: 'Use lightweight reactions for acknowledgement, triage, and quick signal in Relaycast-connected conversations.'
+---
+
+Emoji reactions are the smallest possible response. Use them when you want to acknowledge, vote, or signal status without adding another full message to the conversation.
+
+## Where reactions are available
+
+Reactions are part of the Agent Relay messaging surface. They are exposed in relay-aware clients such as the Claude Code plugin and Agent Relay MCP tools.
+
+For example, the Claude Code plugin exposes `message_reaction_add` for reacting to messages inside your workspace.
+
+## What reactions are good for
+
+- `thumbsup` to acknowledge a request or approve a result
+- `eyes` to indicate someone is looking at it
+- `white_check_mark` to mark something done
+- `warning` or `thinking` style emoji to flag follow-up needed
+
+## Why use a reaction instead of a message
+
+- It keeps channels quieter.
+- It communicates agreement or triage instantly.
+- It gives humans and agents a lightweight status signal without creating more thread noise.
+
+## Practical pattern
+
+Use a message when new information is needed. Use a reaction when you only need acknowledgement, approval, or a quick status signal.
+
+Threads and reactions work well together: keep the discussion in the thread, and use reactions for fast feedback on the key messages inside it.
+
+## Current SDK boundary
+
+The orchestrate SDK pages focus on messages, threads, channels, and lifecycle events. Reactions are currently most visible through Relaycast-connected tools and plugin surfaces rather than the core `AgentRelay` facade examples.
+
+## See also
+
+
+
+ Use reactions alongside threaded discussion for cleaner message flow.
+
+
+ Plugin surface that exposes Relaycast reaction tools directly.
+
+
+ Shared rooms where reactions reduce unnecessary chatter.
+
+
+ Private conversations that can still benefit from quick acknowledgement.
+
+
diff --git a/web/content/docs/7.1.1/event-handlers.mdx b/web/content/docs/7.1.1/event-handlers.mdx
new file mode 100644
index 00000000..c7875682
--- /dev/null
+++ b/web/content/docs/7.1.1/event-handlers.mdx
@@ -0,0 +1,255 @@
+---
+title: 'Event handlers'
+description: 'Subscribe to relay events so your app can react to messages, agent lifecycle changes, structured results, spawn intercepts, output, and delivery updates.'
+---
+
+Event handlers are the main way to observe what the relay is doing in real time. Register listeners on the `AgentRelay` instance to log activity, update UI, trigger follow-up work, or keep your own state in sync.
+
+## Basic pattern
+
+Use `addListener(event, handler)` to subscribe — it returns an unsubscribe function. Multiple listeners can register for the same event; they fire sequentially in registration order. Async handlers are awaited. Handler exceptions are caught and logged so one bad listener never blocks the others.
+
+
+```typescript TypeScript
+const relay = new AgentRelay();
+
+const off = relay.addListener('messageReceived', (msg) => {
+ console.log(`${msg.from} -> ${msg.to}: ${msg.text}`);
+});
+
+relay.addListener('agentReady', (agent) => {
+ console.log(`ready: ${agent.name}`);
+});
+
+// Later, when you want to detach:
+off();
+// or:
+relay.removeListener('messageReceived', myHandler);
+```
+
+```python Python
+relay = AgentRelay()
+
+relay.on_message_received = lambda msg: print(f"{msg.from_name} -> {msg.to}: {msg.text}")
+relay.on_agent_ready = lambda agent: print(f"ready: {agent.name}")
+```
+
+
+The TypeScript SDK switched to a multi-listener registry in 7.0; if you're upgrading from 6.x see the [migration block below](#migrating-from-6x).
+
+## Message events
+
+
+```typescript TypeScript
+relay.addListener('messageReceived', (msg) => {
+ console.log('received', msg.threadId, msg.text);
+});
+
+relay.addListener('messageSent', (msg) => {
+ console.log('sent', msg.to, msg.text);
+});
+```
+
+```python Python
+relay.on_message_received = lambda msg: print("received", msg.thread_id, msg.text)
+relay.on_message_sent = lambda msg: print("sent", msg.to, msg.text)
+```
+
+
+- `messageReceived` / `on_message_received`: fires when the relay delivers a message into your session.
+- `messageSent` / `on_message_sent`: fires after your app or agent sends a message through the relay.
+
+## Agent lifecycle events
+
+
+```typescript TypeScript
+relay.addListener('agentSpawned', (agent) => {
+ console.log(`spawned: ${agent.name}`);
+});
+
+relay.addListener('agentReady', (agent) => {
+ console.log(`ready: ${agent.name}`);
+});
+
+relay.addListener('agentReleased', (agent) => {
+ console.log(`released: ${agent.name}`);
+});
+
+relay.addListener('agentExited', (agent) => {
+ console.log(`exited: ${agent.name}`, agent.exitCode, agent.exitSignal);
+});
+
+relay.addListener('agentExitRequested', ({ name, reason }) => {
+ console.log(`exit requested: ${name}`, reason);
+});
+
+relay.addListener('agentIdle', ({ name, idleSecs }) => {
+ console.log(`idle: ${name}`, idleSecs);
+});
+
+relay.addListener('agentActivityChanged', ({ name, active, pendingDeliveries, reason }) => {
+ console.log(`activity: ${name}`, { active, pendingDeliveries, reason });
+});
+```
+
+```python Python
+relay.on_agent_spawned = lambda agent: print(f"spawned: {agent.name}")
+relay.on_agent_ready = lambda agent: print(f"ready: {agent.name}")
+relay.on_agent_released = lambda agent: print(f"released: {agent.name}")
+relay.on_agent_exited = lambda agent: print(f"exited: {agent.name}")
+relay.on_agent_exit_requested = lambda event: print(f"exit requested: {event['name']}", event["reason"])
+relay.on_agent_idle = lambda event: print(f"idle: {event['name']}", event["idleSecs"])
+```
+
+
+These are useful for status dashboards, retry logic, timeout nudges, and cleanup when a worker finishes or requests exit.
+
+## Call-site spawn / release hooks
+
+Unlike the broker-event listeners above (which fire *after* the broker emits an event), the four `before*` / `after*` hooks fire at the SDK call site — before and after the HTTP request to the broker. They're the right place to integrate cost-tracking tools, audit logs, or anything that needs to observe (or modify) the spawn input.
+
+```typescript
+import { randomUUID } from 'node:crypto';
+
+relay.addListener('beforeAgentSpawn', (ctx) => {
+ console.log(`spawning ${ctx.input.name} via ${ctx.input.cli}`);
+});
+
+relay.addListener('afterAgentSpawn', (ctx) => {
+ if (ctx.error) {
+ console.error(`spawn ${ctx.input.name} failed in ${ctx.durationMs}ms`, ctx.error);
+ } else {
+ console.log(`spawn ${ctx.result?.name} resolved in ${ctx.durationMs}ms`);
+ }
+});
+
+relay.addListener('beforeAgentRelease', (ctx) => {
+ console.log(`releasing ${ctx.name}`, ctx.reason);
+});
+
+relay.addListener('afterAgentRelease', (ctx) => {
+ console.log(`released ${ctx.name} in ${ctx.durationMs}ms`);
+});
+```
+
+### Mutating the spawn input
+
+A `beforeAgentSpawn` handler can **return a `SpawnPatch`** to merge into the spawn input before it reaches the broker. Patches are a shallow merge over the resolved input; when multiple handlers return patches they apply in registration order (later wins on conflict). For array fields like `args` / `channels` the patch *replaces* the array — spread the previous value to extend:
+
+```typescript
+relay.addListener('beforeAgentSpawn', (ctx) => {
+ if (ctx.input.cli !== 'claude') return; // observe-only for non-Claude
+ const sessionId = randomUUID();
+ // Preallocate a session id so cost-tracking can stamp it without sidecar matching.
+ return {
+ args: [...(ctx.input.args ?? []), '--session-id', sessionId],
+ };
+});
+```
+
+`afterAgentSpawn` exposes the post-patch `resolvedInput` so observers can see exactly what was sent. `beforeAgentRelease` / `afterAgentRelease` are observe-only.
+
+## Structured result events
+
+```typescript
+relay.addListener('agentResult', (result) => {
+ console.log(`result from ${result.name}`, result.resultId, result.data);
+});
+```
+
+`agentResult` fires when a spawned agent submits JSON through the `submit_result` MCP tool. Use it for global observers; use `agent.waitForResult(...)` or the per-spawn `result.onResult` callback when one caller owns a specific agent's result.
+
+## Output and delivery events
+
+
+```typescript TypeScript
+relay.addListener('workerOutput', ({ name, stream, chunk }) => {
+ console.log(`[${name}] ${stream}: ${chunk}`);
+});
+
+relay.addListener('deliveryUpdate', (event) => {
+ console.log('delivery update', event.kind, event);
+});
+```
+
+```python Python
+relay.on_worker_output = lambda event: print(f"[{event['name']}] {event['stream']}: {event['chunk']}")
+relay.on_delivery_update = lambda event: print("delivery update", event["type"])
+```
+
+
+- `workerOutput` / `on_worker_output`: raw stdout or stderr from a worker.
+- `deliveryUpdate` / `on_delivery_update`: broker-level delivery events when you need transport visibility.
+- `agentActivityChanged`: high-level derived activity signal for UI badges like "thinking" without hand-rolling delivery event inference.
+
+```typescript
+const thinkingByAgent = new Map();
+
+relay.addListener('agentActivityChanged', ({ name, active }) => {
+ thinkingByAgent.set(name, active);
+ renderThinkingBadge(name, active);
+});
+```
+
+## Channel subscribe / unsubscribe events
+
+```typescript
+relay.addListener('channelSubscribed', ({ agent, channels }) => {
+ console.log(`${agent} subscribed to ${channels.join(', ')}`);
+});
+
+relay.addListener('channelUnsubscribed', ({ agent, channels }) => {
+ console.log(`${agent} unsubscribed from ${channels.join(', ')}`);
+});
+```
+
+## Migrating from 6.x
+
+The 6.x TypeScript SDK exposed each event as a single nullable callback field:
+
+```ts
+// 6.x style — no longer compiles
+relay.onAgentSpawned = (agent) => log(agent.name);
+relay.onMessageReceived = null;
+```
+
+In 7.0 every event is on a typed multi-listener registry:
+
+```ts
+// 7.0 style
+const off = relay.addListener('agentSpawned', (agent) => log(agent.name));
+off(); // unsubscribe when done
+```
+
+Migration rules:
+
+- `relay.onXxx = handler;` → `relay.addListener('xxx', handler);` (lower-case first letter of the suffix).
+- `relay.onXxx = null;` → either call the unsubscribe function returned from `addListener`, or use `relay.removeListener('xxx', handler)`.
+- `relay.onChannelSubscribed = (agent, channels) => ...` and `relay.onChannelUnsubscribed = ...` now receive a single `{ agent, channels }` object instead of positional args.
+
+Per-call option callbacks like `spawnAgent({ onStart, onSuccess, onError })` are unchanged — those are scoped to a single invocation, not global hooks.
+
+## Good uses for listeners
+
+- Update a UI timeline or status panel without polling.
+- Capture logs and metrics while agents run.
+- Trigger follow-up work when a message arrives or a worker becomes ready.
+- Detect exit conditions and clean up local resources.
+- Integrate cost-tracking or audit tools by pre-stamping spawns from `beforeAgentSpawn`.
+
+## See also
+
+
+
+ Pair live handlers with direct messages and broadcasts.
+
+
+ Start workers whose lifecycle you want to observe.
+
+
+ Full TypeScript API reference, including event names and payload types.
+
+
+ Full Python API reference for the same callbacks.
+
+
diff --git a/web/content/docs/7.1.1/file-sharing.mdx b/web/content/docs/7.1.1/file-sharing.mdx
new file mode 100644
index 00000000..d19bcbcc
--- /dev/null
+++ b/web/content/docs/7.1.1/file-sharing.mdx
@@ -0,0 +1,165 @@
+---
+title: 'File sharing'
+description: 'Use Relayfile when agents need shared state, artifacts, and coordinated file access.'
+---
+
+Messaging handles coordination. Relayfile handles shared state. When several agents need to read, write, watch, and
+coordinate the same files, Relayfile gives them one coherent filesystem abstraction instead of a pile of custom sync code.
+
+## When file sharing matters
+
+- Multiple agents collaborate on the same repo or output directory
+- A workflow produces artifacts that later steps need to read
+- You need file watching, shared volumes, or explicit write coordination
+
+## Relayfile at a glance
+
+- Mount provider-backed data as files: Linear, GitHub, Notion, Slack, HubSpot, Salesforce, and custom APIs
+- Read, grep, and summarize through normal file tools instead of loading a provider-specific tool surface
+- Write provider records by saving JSON or Markdown files to canonical paths
+- Watch paths for realtime changes and trigger follow-up work
+- Share the same workspace across several agents, sandboxes, and humans
+- Control read, write, watch, and admin access per path
+
+## Quick start
+
+Use hosted setup when you want Agent Relay to handle OAuth, initial sync, writeback workers, and the local mount daemon.
+
+```bash
+npx relayfile setup \
+ --provider notion \
+ --workspace research-room \
+ --local-dir ./relayfile-mount
+```
+
+The mounted directory is intentionally boring:
+
+```bash
+ls ./relayfile-mount
+cat ./relayfile-mount/LAYOUT.md
+cat ./relayfile-mount/digests/yesterday.md
+find ./relayfile-mount/notion/pages/by-edited/2026-05-12 -maxdepth 1 -type f
+```
+
+To write back to a provider, save a file in the provider tree. The adapter validates the payload, queues writeback, and records the operation.
+
+```bash
+cat > ./relayfile-mount/linear/issues/drafts/follow-up-from-digest.json <<'JSON'
+{
+ "title": "Follow up from yesterday's digest",
+ "description": "Check the customer note linked from /digests/yesterday.md.",
+ "state": "Todo"
+}
+JSON
+```
+
+## Programmatic usage
+
+Relayfile can be a mounted directory for terminal-native agents, or a small tool inside an SDK-owned agent loop.
+
+### Mount from a sandbox
+
+```ts
+import { RelayfileSetup } from '@relayfile/sdk';
+
+const setup = new RelayfileSetup({
+ accessToken: process.env.RELAYFILE_ACCESS_TOKEN!,
+});
+
+const handle = await setup.ensureMountedWorkspace({
+ workspaceId: 'rw_123',
+ provider: 'notion',
+ verifyProvider: true,
+ localDir: '/workspace/relayfile',
+});
+
+console.log(handle.env().RELAYFILE_LOCAL_DIR);
+```
+
+### Vercel AI SDK
+
+```ts
+import { generateText, tool } from 'ai';
+import { z } from 'zod';
+import { RelayFileClient } from '@relayfile/sdk';
+
+const files = new RelayFileClient({ token: process.env.RELAYFILE_TOKEN! });
+
+const { text } = await generateText({
+ model: process.env.AI_MODEL!,
+ prompt: 'What changed yesterday across GitHub, Linear, and Notion?',
+ tools: {
+ readRelayfile: tool({
+ description: 'Read a file from the Relayfile workspace',
+ inputSchema: z.object({ path: z.string() }),
+ execute: ({ path }) => files.readFile('rw_123', path),
+ }),
+ },
+});
+```
+
+### Claude Agent SDK
+
+```ts
+import { createSdkMcpServer, query, tool } from '@anthropic-ai/claude-agent-sdk';
+import { z } from 'zod';
+import { RelayFileClient } from '@relayfile/sdk';
+
+const files = new RelayFileClient({ token: process.env.RELAYFILE_TOKEN! });
+
+const relayfileServer = createSdkMcpServer({
+ name: 'relayfile',
+ tools: [
+ tool('readRelayfile', 'Read workspace files', { path: z.string() }, async ({ path }) => ({
+ content: [{ type: 'text', text: JSON.stringify(await files.readFile('rw_123', path)) }],
+ })),
+ ],
+});
+
+for await (const message of query({
+ prompt: 'Summarize /digests/yesterday.md, then draft Linear follow-ups.',
+ options: { mcpServers: { relayfile: relayfileServer } },
+})) {
+ console.log(message);
+}
+```
+
+### OpenAI Agents SDK
+
+```ts
+import { Agent, run, tool } from '@openai/agents';
+import { z } from 'zod';
+import { RelayFileClient } from '@relayfile/sdk';
+
+const files = new RelayFileClient({ token: process.env.RELAYFILE_TOKEN! });
+
+const reviewer = new Agent({
+ name: 'workspace-reviewer',
+ instructions: 'Use Relayfile paths before asking for provider-specific tools.',
+ tools: [
+ tool({
+ name: 'read_relayfile',
+ parameters: z.object({ path: z.string() }),
+ execute: ({ path }) => files.readFile('rw_123', path),
+ }),
+ ],
+});
+
+await run(reviewer, 'Review /digests/yesterday.md and file Linear follow-ups.');
+```
+
+For write access, add a separate `writeRelayfile` tool and mint a token scoped to the exact paths that agent owns, such as `relayfile:fs:write:/linear/issues/*`.
+
+## Where to go next
+
+
+
+ Overview, examples, and platform positioning for shared agent storage.
+
+
+ See how file sharing fits beside auth, messaging, and scheduling.
+
+
+ Source, package details, and the latest implementation surface.
+
+
diff --git a/web/content/docs/7.1.1/harness-runtime-config.mdx b/web/content/docs/7.1.1/harness-runtime-config.mdx
new file mode 100644
index 00000000..2ffb385f
--- /dev/null
+++ b/web/content/docs/7.1.1/harness-runtime-config.mdx
@@ -0,0 +1,190 @@
+---
+title: Harness runtime config
+description: Reference for the concrete HarnessConfig objects that SDK adapters send to the broker.
+---
+
+Use this page when you need the exact `HarnessConfig` shape. For a shorter
+guide with Codex, Claude, and OpenCode examples, start with
+[Harnesses](/docs/harnesses).
+
+The broker only executes serializable config data. SDK adapters can help you
+build that data, but the broker does not call SDK functions after spawn and does
+not keep a named harness registry.
+
+## Runtime Categories
+
+| Runtime | Use for | Broker capabilities |
+| --- | --- | --- |
+| `pty` | Terminal-backed CLIs such as Codex or Claude Code | process spawn, PTY stream, input, resize, snapshot, delivery, release |
+| `headless` | Non-terminal sessions such as OpenCode app-server | session delivery, release |
+
+When `runtime` is `headless`, `driver` defaults to `app_server`.
+
+## Terms
+
+| Term | Meaning |
+| --- | --- |
+| Harness config | Concrete `pty` or `headless` JSON the broker can validate and run |
+| Harness adapter | SDK or userland helper that returns a harness config |
+| Named harness | SDK-side shortcut in `new AgentRelay({ harnesses })` |
+| `harnessConfig` | Spawn field carrying the concrete config to the broker |
+
+Named harnesses are local SDK ergonomics. The SDK resolves them to an inline
+`harnessConfig` before the spawn request reaches the broker.
+
+## PTY Config
+
+Use `pty` for terminal-backed coding harnesses:
+
+```typescript
+type PtyHarnessConfig = {
+ runtime: 'pty';
+ command: string;
+ args: string[];
+ cwd?: string;
+ env?: Record;
+ sessionId?: string;
+ delivery?: {
+ mode?: 'pty-injection';
+ format?: 'relay-block';
+ };
+ metadata?: Record;
+};
+```
+
+Example:
+
+```typescript
+const harnessConfig = {
+ runtime: 'pty',
+ command: 'codex',
+ args: ['resume', sessionId],
+ cwd,
+ env: {
+ PATH: process.env.PATH ?? '',
+ CODEX_HOME: process.env.CODEX_HOME ?? '',
+ },
+ sessionId,
+} satisfies ResolvedHarnessConfig;
+```
+
+The broker owns the spawned process, PTY stream, raw input, resize, snapshots,
+message injection, and release behavior for this runtime.
+
+## Headless App-Server Config
+
+Use `headless` for a non-terminal agent session controlled through an app
+server. OpenCode is the first supported protocol:
+
+```typescript
+type HeadlessAppServerHarnessConfig = {
+ runtime: 'headless';
+ driver?: 'app_server';
+ protocol: 'opencode' | string;
+ endpoint: string;
+ sessionId: string;
+ auth?: {
+ type: 'bearer' | 'basic' | 'none';
+ token?: string;
+ username?: string;
+ password?: string;
+ };
+ host?: {
+ ownership?: 'broker-owned' | 'attached';
+ pid?: number;
+ };
+ release?: 'abort' | 'detach' | 'delete';
+ metadata?: Record;
+};
+```
+
+Example:
+
+```typescript
+const harnessConfig = {
+ runtime: 'headless',
+ protocol: 'opencode',
+ endpoint: 'http://127.0.0.1:4096',
+ sessionId: 'ses_123',
+ host: { ownership: 'attached', pid: 34567 },
+ release: 'abort',
+} satisfies ResolvedHarnessConfig;
+```
+
+For now, app-server configs are attach-only. `host.ownership: 'broker-owned'`
+is reserved until the broker owns app-server lifecycle supervision. If
+`host.pid` is provided, the broker reports that PID as the harness process ID.
+
+## Adapter Pattern
+
+An adapter should return config data:
+
+```typescript
+function companyClaude(): ResolvedHarnessConfig {
+ return {
+ runtime: 'pty',
+ command: 'claude',
+ args: [
+ '--dangerously-skip-permissions',
+ '--append-system-prompt',
+ 'Follow the company review rubric.',
+ ],
+ };
+}
+```
+
+Register stable configs by name:
+
+```typescript
+const relay = new AgentRelay({
+ harnesses: {
+ 'company-claude': companyClaude(),
+ },
+});
+```
+
+Use an inline `harnessConfig` when setup changes per spawn, such as creating a
+Codex session:
+
+```typescript
+const sessionId = await createCodexSession({ cwd, task });
+
+await relay.spawnAgent({
+ name: 'CodexReviewer',
+ cli: 'codex',
+ task,
+ harnessConfig: {
+ runtime: 'pty',
+ command: 'codex',
+ args: ['resume', sessionId],
+ cwd,
+ sessionId,
+ },
+});
+```
+
+Do not copy the whole process environment into `env`, and do not put secrets in
+`metadata`. `env` and `auth` are visible to the broker, so pass explicit
+allowlists.
+
+## Spawn Payloads
+
+`POST /api/spawn` accepts `harnessConfig`:
+
+```json
+{
+ "name": "CodexReviewer",
+ "cli": "codex",
+ "task": "Review the current diff.",
+ "harnessConfig": {
+ "runtime": "pty",
+ "command": "codex",
+ "args": ["resume", "session_123"],
+ "sessionId": "session_123"
+ }
+}
+```
+
+The broker rejects `harnessId`. Relaycast spawns that need custom behavior
+should also send a full inline `harnessConfig`, which keeps each spawn
+self-contained across local, remote, and multi-broker deployments.
diff --git a/web/content/docs/7.1.1/harnesses.mdx b/web/content/docs/7.1.1/harnesses.mdx
new file mode 100644
index 00000000..529c2230
--- /dev/null
+++ b/web/content/docs/7.1.1/harnesses.mdx
@@ -0,0 +1,182 @@
+---
+title: Harnesses
+description: Define custom PTY and headless harness configs in the SDK.
+---
+
+A harness is the thing Relay controls for an agent. The broker only executes
+serializable `HarnessConfig` data. SDK "adapters" are helpers that return those
+configs before a spawn request is sent.
+
+Relay supports two runtime categories:
+
+| Runtime | Use for | Broker capabilities |
+| --- | --- | --- |
+| `pty` | Terminal-backed CLIs such as Codex or Claude Code | stream, input, resize, snapshot, delivery, release |
+| `headless` | Non-terminal sessions such as OpenCode app-server | delivery, release |
+
+When `runtime` is `headless`, `driver` defaults to `app_server`.
+
+## Naming
+
+| Name | Meaning |
+| --- | --- |
+| Harness config | Concrete `pty` or `headless` JSON the broker can validate and run |
+| Harness adapter | SDK helper that returns a harness config |
+| Named harness | SDK-side shortcut in `new AgentRelay({ harnesses })` |
+| `harnessConfig` | Spawn field carrying the concrete config to the broker |
+
+The broker boundary is always explicit config data. Named harnesses are resolved
+by the SDK before spawn; Relaycast and multi-broker spawns should send
+`harnessConfig` directly.
+
+## Claude PTY Config
+
+Use a named harness when the command is stable across spawns:
+
+```typescript file="claude-harness.ts"
+import { AgentRelay } from '@agent-relay/sdk';
+
+const relay = new AgentRelay({
+ harnesses: {
+ 'company-claude': {
+ runtime: 'pty',
+ command: 'claude',
+ args: [
+ '--dangerously-skip-permissions',
+ '--append-system-prompt',
+ 'Follow the company review rubric.',
+ '{modelArgs}',
+ '{args}',
+ ],
+ modelArgs: ['--model', '{model}'],
+ env: {
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
+ },
+ },
+ },
+});
+
+await relay.spawnAgent({
+ name: 'ClaudeReviewer',
+ cli: 'company-claude',
+ task: 'Review the current diff.',
+ model: 'opus',
+ args: ['--verbose'],
+});
+```
+
+The SDK turns `company-claude` into an inline `harnessConfig` for that spawn.
+The broker does not keep a named harness registry.
+
+## Codex Per-Spawn Config
+
+Use direct `harnessConfig` when setup produces a different config for each
+spawn, such as creating or resuming a Codex session:
+
+```typescript file="codex-harness.ts"
+import { AgentRelay, type ResolvedHarnessConfig } from '@agent-relay/sdk';
+
+function codexResume(sessionId: string, cwd: string): ResolvedHarnessConfig {
+ return {
+ runtime: 'pty',
+ command: 'codex',
+ args: ['resume', sessionId],
+ cwd,
+ env: {
+ PATH: process.env.PATH ?? '',
+ CODEX_HOME: process.env.CODEX_HOME ?? '',
+ },
+ sessionId,
+ };
+}
+
+const relay = new AgentRelay();
+const cwd = process.cwd();
+const task = 'Review the current diff.';
+const sessionId = await createCodexSession({ cwd, task });
+
+await relay.spawnAgent({
+ name: 'CodexReviewer',
+ cli: 'codex',
+ task,
+ cwd,
+ harnessConfig: codexResume(sessionId, cwd),
+});
+```
+
+Do not copy the whole process environment into `env`. Pass only the keys the
+harness needs.
+
+## OpenCode Headless Config
+
+Use `headless` for an agent that already exists inside an app-server session.
+OpenCode `serve` is the first supported protocol:
+
+```typescript file="opencode-harness.ts"
+import { AgentRelay, type ResolvedHarnessConfig } from '@agent-relay/sdk';
+
+function opencodeSession(input: {
+ endpoint: string;
+ sessionId: string;
+ pid?: number;
+}): ResolvedHarnessConfig {
+ return {
+ runtime: 'headless',
+ protocol: 'opencode',
+ endpoint: input.endpoint,
+ sessionId: input.sessionId,
+ host: { ownership: 'attached', pid: input.pid },
+ release: 'abort',
+ };
+}
+
+const relay = new AgentRelay();
+
+await relay.spawnAgent({
+ name: 'OpenCodeWorker',
+ cli: 'opencode',
+ runtime: 'headless',
+ task: 'Inspect the repo.',
+ harnessConfig: opencodeSession({
+ endpoint: 'http://127.0.0.1:4096',
+ sessionId: 'ses_123',
+ pid: 34567,
+ }),
+});
+```
+
+For OpenCode, Relay delivers messages to `POST /session/:id/prompt_async`.
+For now, app-server configs must use `protocol: 'opencode'`, an `http` or
+`https` endpoint, a non-empty `sessionId`, and attached host ownership.
+`broker-owned` app-server hosts are reserved for later broker supervision.
+
+## Relaycast Spawns
+
+Agent-crafted spawns should send the full config:
+
+```json
+{
+ "agent": {
+ "name": "CodexReviewer",
+ "cli": "codex",
+ "task": "Review the current diff.",
+ "harnessConfig": {
+ "runtime": "pty",
+ "command": "codex",
+ "args": ["resume", "session_123"],
+ "sessionId": "session_123"
+ }
+ }
+}
+```
+
+Relay intentionally avoids a broker-local harness registry for now. A spawn
+request should be self-contained so behavior does not depend on hidden runtime
+state or which broker receives it.
+
+## See Also
+
+- [Harness runtime config](/docs/harness-runtime-config) - Exact config shapes
+- [Spawning an agent](/docs/spawning-an-agent) - High-level spawn APIs
+- [Event handlers](/docs/event-handlers) - SDK event subscriptions
+- [Broker HTTP / WS API](/docs/reference-broker-api) - Broker API routes
diff --git a/web/content/docs/7.1.1/introduction.mdx b/web/content/docs/7.1.1/introduction.mdx
new file mode 100644
index 00000000..57b72e70
--- /dev/null
+++ b/web/content/docs/7.1.1/introduction.mdx
@@ -0,0 +1,90 @@
+---
+title: 'Introduction'
+description: 'Let Claude Code, Codex, OpenCode & other agents talk to each other directly, in real-time.'
+---
+
+It's like Slack, but built specifically for agents.
+
+
+ Agent Relay is _**not a custom harness**_. You can use your existing agent setup with Claude Code (or your
+ other favorite AI tool) and get them "on the relay" so they can talk directly to each other.
+
+
+## How it Works
+
+Agent Relay starts a service that can spawn or release agent instances via PTY. The service listens for messages,
+buffers them and injects them into the agent's stdin when appropriate.
+
+Agents respond with calls to the CLI or MCP.
+
+Any tool that can run in a terminal can be used as an agent in Agent Relay.
+
+Our SDK makes it very easy to control and add deterministic rails to your agents collaboration.
+
+
+ Jump into the quickstart and launch your first agents in 2 minutes
+
+
+### Distributed Mode
+
+By default, Agent Relay is configured to be run in a distributed friendly mode. Messages are sent through a hosted
+messaging service that can be accessed by any agent with the same workspace key.
+
+This makes it very easy to use agent
+relay with multiple agents in different environments _without any additional setup_.
+
+We also provide helpful tools to view and replay messages from the relay workspace in real-time.
+
+### Local Mode
+
+Agent Relay can be run completely locally without any distributed cloud features. Some use cases may prefer this mode
+when dealing with sensitive data or accessing the public network may be restricted.
+
+See [Local Mode](/docs/local-mode) for more details.
+
+## Primitives
+
+Agent Relay is built on top of a set of primitives that are designed to be used together.
+
+### Messaging
+
+Messaging is the core coordination layer. Agents, humans, and systems can send real-time messages in channels, direct messages, threads, and reactions
+so work can be handed off, reviewed, and tracked without building a custom transport layer yourself.
+
+Use it for task assignment, status updates, review loops, and any workflow where one participant needs to tell another what to
+do next. Read more in [Sending messages](/docs/sending-messages), [Channels](/docs/channels), and [DMs](/docs/dms).
+
+### Files
+
+Agent Relay provides a shared filesystem layer. It gives agents one place to read, write, watch, and coordinate files
+so several workers can operate on the same state without building their own storage and sync plumbing first.
+
+Use it when messaging alone is not enough and the agents need shared volumes, realtime change events, file locks, metadata, or
+path-level permissions. Read more in [File sharing](/docs/file-sharing).
+
+### Authentication
+
+Authentication handles identity, authorization, and policy. Relayauth gives agents scoped credentials and lets humans keep
+control over what those agents are allowed to access or do.
+
+Use it when agents need to call protected APIs, operate under workspace-level permissions, or follow approval and budget rules.
+Read more in [Authentication](/docs/authentication).
+
+### Scheduling
+
+Scheduling is the time-based layer. Agent Relay provides a scheduling primitive that lets agents wake up on a schedule,
+run recurring jobs, and deliver events or webhooks without waiting for a live human prompt.
+
+Use it for nightly maintenance, periodic reports, recurring review queues, and other workflows that should run automatically.
+Read more in [Scheduling](/docs/scheduling).
+
+## Open Source
+
+### License
+
+Agent Relay is open source under the Apache 2.0 license.
+
+### Contributions Welcome!
+
+Please open an issue or pull request if you have any ideas or suggestions, we are very interested in supporting new use
+cases and integrations.
diff --git a/web/content/docs/7.1.1/local-mode.mdx b/web/content/docs/7.1.1/local-mode.mdx
new file mode 100644
index 00000000..edbb8170
--- /dev/null
+++ b/web/content/docs/7.1.1/local-mode.mdx
@@ -0,0 +1,98 @@
+---
+title: 'Local Mode'
+description: 'Run Agent Relay on your own machine with local broker state, local services, and no hosted control plane.'
+---
+
+Local mode is the all-local operating model for Agent Relay. Use it when you want the broker, workspace state, file layer, and auth layer to stay on your machine instead of depending on hosted coordination surfaces.
+
+
+If you are running agents against sensitive code, working offline, or iterating on relay-aware tooling locally, this is the mode to start with.
+
+
+## What local mode means
+
+- The broker runs on your machine and can persist runtime state under `.agent-relay/`.
+- The relay-aware sandbox path uses local Relayauth and Relayfile services on `127.0.0.1`.
+- Agent work happens against a mounted local workspace, not a hosted sandbox.
+- Cloud-specific surfaces such as Observer and `cloud` workflow execution are optional and out of scope.
+
+## When to use it
+
+- Your repo or data should stay inside one machine or one trusted local network boundary.
+- You want fast iteration and easier debugging with local logs and local state files.
+- You are developing relay-aware permissions, ACLs, or workspace policies.
+- You do not need remote execution or cross-machine workspace access.
+
+## Fastest local paths
+
+### Start a local broker
+
+Use this when you want spawned agents talking to each other on the same machine:
+
+```bash
+agent-relay up --no-dashboard
+agent-relay spawn planner claude "Break the work into steps"
+agent-relay spawn coder codex "Implement the approved plan"
+agent-relay send planner "Coordinate with coder and keep updates concise."
+agent-relay status
+```
+
+Useful flags:
+
+- `--dashboard` starts the local dashboard if the dashboard components are installed.
+- `--state-dir ` moves persisted broker state out of the default `.agent-relay/` directory.
+- `--broker-name ` gives the local broker a stable identity when the directory name is not specific enough.
+- `--no-spawn` starts the broker without auto-spawning configured agents.
+
+### Run inside the local relay sandbox
+
+Use this when you want a relay-aware mounted workspace with local auth and file services:
+
+```bash
+agent-relay on --scan
+agent-relay on codex --agent reviewer -- --model gpt-5.4
+```
+
+Useful commands:
+
+- `agent-relay on --doctor` checks prerequisites without launching a session.
+- `agent-relay on --scan` previews what the agent will be able to see.
+- `agent-relay off` stops the local relay services and cleans up mounts.
+- `--port-auth ` and `--port-file ` move the local service ports when defaults clash.
+
+## Local state and services
+
+- Persisted broker state lives in `.agent-relay/` unless you override it with `--state-dir`.
+- The default local service ports are `127.0.0.1:8787` for Relayauth and `127.0.0.1:8080` for Relayfile.
+- If you have not authored a relay config yet, `agent-relay on` can generate one under `.relay/generated/relay-zero-config.json`.
+- Permission details for the mounted workspace are written to `_PERMISSIONS.md`.
+- If `.agentdeny` exists in your project, it is copied into the mounted workspace so local runs enforce the same deny rules.
+
+## Local mode vs cloud mode
+
+- Local mode keeps runtime, services, and state close to your repo for low-latency, single-machine operation.
+- Cloud mode is for sandboxed remote execution, longer-running jobs, and distributed access patterns.
+- Start local unless you specifically need hosted execution or a shared remote workspace.
+
+## Related docs
+
+
+
+ Start, inspect, and stop the local broker.
+
+
+ Launch an agent into the relay-aware local sandbox.
+
+
+ Understand the shared file layer used by local workspaces.
+
+
+ See the auth layer that backs local relay sessions.
+
+
+ Optional local UI for watching and operating a workspace.
+
+
+ Switch mental models when you do need remote execution.
+
+
diff --git a/web/content/docs/7.1.1/observer.mdx b/web/content/docs/7.1.1/observer.mdx
new file mode 100644
index 00000000..b5f4641d
--- /dev/null
+++ b/web/content/docs/7.1.1/observer.mdx
@@ -0,0 +1,43 @@
+---
+title: 'Observer'
+description: 'Follow relay traffic in real time from a lightweight read-only workspace view.'
+---
+
+Observer is the fastest way to watch a Relaycast workspace live. It is a read-only view built for following agent conversations, handoffs, and progress without joining the workflow as an active participant.
+
+## What it shows
+
+- Messages as they move through the workspace
+- Agent activity and delivery updates in real time
+- A shareable live view for humans who want to monitor a run
+
+## Open an observer session
+
+Observer URLs are keyed to a Relaycast workspace:
+
+```text
+https://agentrelay.com/observer?key=
+```
+
+The TypeScript SDK exposes this directly as `relay.observerUrl` once the workspace key is available.
+
+## When to use it
+
+Use Observer when you want visibility without control. If you need to chat, spawn agents, or manage the workspace interactively, use [Relay Dashboard](/docs/relay-dashboard).
+
+## Related docs
+
+
+
+ Full UI for operating a relay workspace.
+
+
+ Includes `observerUrl` on `AgentRelay`.
+
+
+ Start a workspace and get agents talking.
+
+
+ Understand the traffic you are observing.
+
+
diff --git a/web/content/docs/7.1.1/permissions.mdx b/web/content/docs/7.1.1/permissions.mdx
new file mode 100644
index 00000000..2cd62aac
--- /dev/null
+++ b/web/content/docs/7.1.1/permissions.mdx
@@ -0,0 +1,162 @@
+---
+title: 'Permissions'
+nav_title: 'Permissions'
+description: 'Control what workflow agents can read, write, reach over the network, and execute from relay.yaml.'
+---
+
+Permissions in `relay.yaml` control what each workflow agent can see and do.
+
+If you omit `permissions` entirely, Relay defaults to inherited dotfiles plus `readwrite` access.
+
+## Quick Start
+
+
+```yaml Preset
+agents:
+ - name: reviewer
+ cli: claude
+ permissions: readonly
+```
+
+```yaml Inline
+agents:
+ - name: writer
+ cli: codex
+ permissions:
+ access: restricted
+ files:
+ read: ['src/**']
+ write: ['docs/**']
+ deny: ['.env*', 'secrets/**']
+```
+
+
+
+## Access Presets
+
+| Preset | Read | Write | Dotfiles |
+| ------------ | ----------------------- | ----------------------- | --------- |
+| `readonly` | all non-ignored | none | inherited |
+| `readwrite` | all non-ignored | all non-ignored | inherited |
+| `restricted` | nothing (explicit only) | nothing (explicit only) | inherited |
+| `full` | everything | everything | ignored |
+
+
+ `full` bypasses dotfile-based protection. Use only when YAML should be the entire policy surface.
+
+
+## File Permissions
+
+```yaml
+permissions:
+ access: restricted
+ files:
+ read: ['src/**', 'package.json']
+ write: ['tests/**']
+ deny: ['.env*', 'secrets/**']
+```
+
+- `write` implies read access
+- `deny` always wins over read/write grants
+- Merged on top of the access preset
+
+## Network
+
+```yaml
+# Boolean — allow or deny all
+permissions:
+ network: false
+
+# Object — scoped allowlist
+permissions:
+ network:
+ allow: ['registry.npmjs.org:443', 'github.com:443']
+ deny: ['*']
+```
+
+## Exec
+
+```yaml
+permissions:
+ exec: ['npm test', 'npx vitest', 'git diff']
+```
+
+Matches by command prefix. Omit to allow all commands.
+
+## Profiles
+
+Reusable named permission blocks:
+
+```yaml
+permissions:
+ profiles:
+ source-dev:
+ access: restricted
+ files:
+ read: ['src/**', 'packages/**', 'package.json']
+ write: ['src/**', 'tests/**']
+ deny: ['.env*', 'secrets/**']
+ network: false
+ default: source-dev
+
+agents:
+ - name: frontend
+ cli: codex
+ permissions: source-dev
+```
+
+## Dotfiles
+
+- `.agentignore` — hides files from agents entirely
+- `.agentreadonly` — visible but not writable
+- `..agentignore` / `..agentreadonly` — per-agent overrides
+
+Applied before YAML rules. Bypassed by `full` preset.
+
+## Resolution Order
+
+1. Dotfiles (when inherited)
+2. `access` preset
+3. Explicit `files` globs
+4. `deny` rules (always win)
+
+## Step-Level Overrides
+
+Steps can narrow the agent's permissions for a specific task:
+
+```yaml
+steps:
+ - name: ui
+ type: agent
+ agent: frontend
+ permissions:
+ access: restricted
+ files:
+ write: ['src/components/**']
+```
+
+## YAML Reference
+
+```yaml
+agents:
+ - name: example
+ cli: codex
+ permissions:
+ access: readonly | readwrite | restricted | full
+ inherit: true | false
+ files:
+ read: ['glob', '...']
+ write: ['glob', '...']
+ deny: ['glob', '...']
+ scopes: ['relayfile:fs:read:/src/**']
+ network: true | false | { allow: [...], deny: [...] }
+ exec: ['npm test', 'npx vitest']
+```
+
+## Dry Run
+
+Validate permissions before a real run:
+
+```bash
+agent-relay run relay.yaml --dry-run
+```
diff --git a/web/content/docs/7.1.1/plugin-claude-code.mdx b/web/content/docs/7.1.1/plugin-claude-code.mdx
new file mode 100644
index 00000000..9e93a53d
--- /dev/null
+++ b/web/content/docs/7.1.1/plugin-claude-code.mdx
@@ -0,0 +1,99 @@
+---
+title: 'Claude Code Plugin'
+description: 'Use Agent Relay directly inside Claude Code — coordinate multi-agent workflows with slash commands or natural language.'
+---
+
+## Overview
+
+The Agent Relay plugin for Claude Code gives you multi-agent coordination without writing SDK code. Install it once and spawn teams, fan-out work, and run pipelines using slash commands or plain English.
+
+The plugin works through Claude Code's MCP integration, exposing Relaycast messaging tools (channels, DMs, threads, reactions) directly to your Claude session.
+
+## Install
+
+```bash
+/plugin marketplace add Agentworkforce/skills
+/plugin install claude-relay-plugin
+```
+
+Verify the install:
+
+```bash
+/plugin list
+```
+
+You should see `claude-relay-plugin` in the output.
+
+## Configuration
+
+Set your Relay API key so the plugin can authenticate:
+
+```bash
+export RELAY_API_KEY=rk_live_your_key_here
+```
+
+Or add it to your `.claude/.env` file:
+
+```
+RELAY_API_KEY=rk_live_your_key_here
+```
+
+## Slash Commands
+
+Once installed, the plugin adds relay-specific slash commands:
+
+### `/relay-team`
+
+Coordinate a team of agents to work on a complex task. The plugin spawns a lead agent that breaks the task down and delegates subtasks to workers.
+
+```bash
+/relay-team Refactor the auth module — split the middleware, update tests, and update docs
+```
+
+### `/relay-fanout`
+
+Fan out identical or similar work across multiple agents in parallel.
+
+```bash
+/relay-fanout Run linting fixes across all packages in the monorepo
+```
+
+### `/relay-pipeline`
+
+Run a sequential pipeline where each step feeds into the next.
+
+```bash
+/relay-pipeline Analyze the API logs, then generate a summary report, then draft an email
+```
+
+## Natural Language
+
+You don't need slash commands — describe what you want and the plugin handles the orchestration:
+
+```bash
+Use relay fan-out to lint all packages in parallel
+Split the migration into three relay workers — one for the schema, one for the API, one for the frontend
+Coordinate a team to review and refactor the payment service
+```
+
+## MCP Tools
+
+Under the hood, the plugin exposes the same Agent Relay MCP server used by spawned agents. See [Agent Relay MCP](/docs/agent-relay-mcp) for the full tool reference, including the conditional `submit_result` tool for structured result callbacks.
+
+## How It Works
+
+1. The plugin starts a local MCP server that connects to your Relay workspace
+2. Claude Code discovers the MCP tools and can invoke them during your session
+3. When you use a slash command or describe a multi-agent task, the plugin's skills and agent definitions translate your intent into relay API calls
+4. Spawned agents communicate through Relaycast channels and report progress back to your session
+
+## Next Steps
+
+
+
+ MCP server setup, tool list, and structured result callback behavior.
+
+
+ Learn the SDK fundamentals that the plugin builds on.
+
+
diff --git a/web/content/docs/7.1.1/proactive-agents.mdx b/web/content/docs/7.1.1/proactive-agents.mdx
new file mode 100644
index 00000000..ededaee6
--- /dev/null
+++ b/web/content/docs/7.1.1/proactive-agents.mdx
@@ -0,0 +1,261 @@
+---
+title: 'Proactive agents'
+description: 'Build personas that wake up on a cron, on an integration event, or both — running unattended in the cloud sandbox.'
+---
+
+A proactive agent is a persona with one or more *listeners* that fire without a human in the loop. There are three listener kinds:
+
+- **Clock** — cron schedules. `schedules[]` on the persona.
+- **Radio** — integration events (GitHub PR opened, Notion page created, etc.). `integrations[provider].triggers[]`.
+- **Inbox** — targeted DMs from another agent or user. Today this is the default delivery surface — the same `onEvent` handler receives them.
+
+All three deliver the same `WorkforceEvent` to a single `onEvent` handler in your persona. The handler discriminates on `event.source` (`'cron'` / a provider id like `'github'`) and `event.name` / `event.type`.
+
+## Anatomy
+
+A proactive agent is two files plus a deploy:
+
+```
+my-agent/
+ persona.json # declarative — listeners, integrations, harness, inputs
+ agent.ts # imperative — the handler that runs on every event
+```
+
+`persona.json` declares listeners; `agent.ts` reacts to them.
+
+```ts
+// agent.ts
+import { handler, type WorkforceCtx, type WorkforceEvent } from '@agentworkforce/runtime';
+
+export default handler(async (ctx: WorkforceCtx, event: WorkforceEvent) => {
+ if (event.source === 'cron') return onTick(ctx, event);
+ if (event.source === 'github') return onGithub(ctx, event);
+ ctx.log('warn', 'unhandled.event', { source: event.source });
+});
+```
+
+## Cron-based (clock listener)
+
+Fires on a schedule. Use for digests, sweeps, periodic syncs, EOD recaps.
+
+An integration in the persona has two independent roles: declaring `triggers[]` turns it into a radio listener (events fire); listing it under `integrations` *without* `triggers[]` just makes the typed client (`ctx.github`, `ctx.slack`, …) available at runtime. The example below uses the second role — only the cron fires, but the handler still has `ctx.github` and `ctx.slack` for the digest work.
+
+```json
+{
+ "id": "morning-digest",
+ "intent": "documentation",
+ "tags": ["documentation"],
+ "description": "Daily ship recap.",
+ "harness": "claude",
+ "model": "claude-sonnet-4-6",
+ "cloud": true,
+ "integrations": {
+ "github": { "source": { "kind": "workspace" } },
+ "slack": { "source": { "kind": "workspace" } }
+ },
+ "schedules": [
+ { "name": "daily", "cron": "0 6 * * *", "tz": "America/New_York" }
+ ],
+ "onEvent": "./agent.ts"
+}
+```
+
+```ts
+// agent.ts
+import { handler } from '@agentworkforce/runtime';
+
+export default handler(async (ctx, event) => {
+ if (event.source === 'cron' && event.name === 'daily') {
+ const since = new Date(event.occurredAt);
+ since.setUTCHours(since.getUTCHours() - 24);
+ const dateStamp = event.occurredAt.slice(0, 10);
+ const channel = ctx.persona.inputs.DAILY_DIGEST_CHANNEL ?? 'ops-daily';
+
+ // Drive the harness over the Relayfile mount. Claude reads
+ // /github/repos///pulls/*.json directly and
+ // synthesizes the digest from PRs merged since `since`.
+ const { output, exitCode } = await ctx.harness.run({
+ prompt: `Summarize GitHub PRs merged across the workspace since ${since.toISOString()}. Group by repo; include #number, author, one-line impact. Slack mrkdwn, no preamble.`,
+ });
+ if (exitCode !== 0 || !output.trim()) {
+ ctx.log('warn', 'digest.empty-or-failed', { exitCode });
+ return;
+ }
+
+ await ctx.slack!.post(channel, `*Daily ship — ${dateStamp}*\n${output.trim()}`);
+ await ctx.memory.save(`Daily ship ${dateStamp} posted to #${channel}`, {
+ tags: ['daily-digest', `date:${dateStamp}`],
+ scope: 'workspace',
+ });
+ }
+});
+```
+
+`event.name` matches `schedules[].name`. `event.occurredAt` is the firing timestamp (ISO 8601). The guarded `if` (rather than an early `return` on the inverse) keeps the happy path the thing you read — important once the same handler reacts to multiple listener kinds.
+
+What each `ctx` call does in this example:
+
+- `ctx.persona.inputs.` — resolved deploy-time inputs (see [Inputs](#inputs)).
+- `ctx.harness.run({ prompt })` — spawn the persona's configured harness (here `claude`) inside the sandbox. The harness sees the Relayfile mount and can read `/github/...`, `/slack/...`, etc. directly. Returns `{ output, exitCode, durationMs, usage? }` — destructure the fields you need.
+- `ctx.slack.post(channel, text)` / `ctx.github.comment(...)` — typed integration clients backed by the Relayfile writeback worker. Use them whenever you can; they're auth-managed and retryable.
+- `ctx.log(level, message, attrs)` — structured logger; every line is forwarded to the gateway and visible via `agentworkforce deployments logs `.
+- `ctx.memory.save(content, opts)` — persistent across firings when `memory.enabled` is set on the persona. See [Memory](#memory).
+
+## Integration-based (radio listener)
+
+Fires when a Relayfile integration emits a normalized event. Use for "open a PR when a Notion page lands", "comment on issues when X is mentioned", etc.
+
+```json
+{
+ "id": "issue-greeter",
+ "intent": "documentation",
+ "tags": ["documentation"],
+ "description": "Welcome new GitHub issues with a triage comment.",
+ "harness": "claude",
+ "model": "claude-sonnet-4-6",
+ "cloud": true,
+ "integrations": {
+ "github": {
+ "source": { "kind": "workspace" },
+ "scope": { "repo": "AgentWorkforce/workforce" },
+ "triggers": [{ "on": "issue.created" }]
+ }
+ },
+ "onEvent": "./agent.ts"
+}
+```
+
+```ts
+// agent.ts
+import { handler } from '@agentworkforce/runtime';
+
+export default handler(async (ctx, event) => {
+ if (event.source === 'github' && event.type === 'issue.created') {
+ const { owner, repo, number } = event.payload as { owner: string; repo: string; number: number };
+ await ctx.github!.comment({ owner, repo, number }, 'Triage in progress — thanks for filing.');
+ }
+});
+```
+
+Each declared integration becomes a typed client on `ctx` (`ctx.github`, `ctx.slack`, `ctx.notion`, `ctx.linear`, `ctx.jira`). Personas that declare no integrations at all get those fields as `undefined`.
+
+### Source scope
+
+The `source` field discriminates which credential the cloud resolves at dispatch time:
+
+- `{ kind: 'workspace' }` — workspace-shared connection. Pick this when the integration represents a team-level account (the most common case).
+- `{ kind: 'deployer_user' }` *(default)* — the deploying user's personal connection.
+- `{ kind: 'workspace_service_account', name: 'release-bot' }` — a named service account on the workspace.
+
+The runtime dispatcher reads from the matching table; the deploy CLI's preflight reads from the same table. They must agree. If you're not sure, start with `workspace`.
+
+## Combined (clock + radio)
+
+Combine both — the same handler discriminates on `event.source`:
+
+```json
+{
+ "id": "release-watcher",
+ "intent": "documentation",
+ "tags": ["documentation"],
+ "description": "Reacts to GitHub releases live; reconciles drift on a nightly cron.",
+ "harness": "claude",
+ "model": "claude-sonnet-4-6",
+ "cloud": true,
+ "integrations": {
+ "github": {
+ "source": { "kind": "workspace" },
+ "triggers": [{ "on": "release.published" }]
+ },
+ "slack": { "source": { "kind": "workspace" } }
+ },
+ "schedules": [
+ { "name": "nightly", "cron": "0 3 * * *", "tz": "UTC" }
+ ],
+ "onEvent": "./agent.ts"
+}
+```
+
+```ts
+import { handler } from '@agentworkforce/runtime';
+
+export default handler(async (ctx, event) => {
+ if (event.source === 'github' && event.type === 'release.published') {
+ return announceRelease(ctx, event.payload);
+ }
+ if (event.source === 'cron' && event.name === 'nightly') {
+ return reconcileMissedReleases(ctx, event.occurredAt);
+ }
+});
+```
+
+Both surfaces share `ctx.memory` (persistent across firings) and any declared integration clients — so nightly reconciliation can read what the live handler stored.
+
+## Inputs
+
+Deploy-time configuration goes in `persona.json` under `inputs`. The CLI resolves the value per input from (1) `--input KEY=value`, (2) `WORKFORCE_INPUT_` env var, (3) the declared `default`:
+
+```json
+"inputs": {
+ "DAILY_DIGEST_CHANNEL": {
+ "description": "Slack channel for the digest.",
+ "env": "DAILY_DIGEST_CHANNEL",
+ "default": "ops-daily"
+ }
+}
+```
+
+Read at runtime via `ctx.persona.inputs.DAILY_DIGEST_CHANNEL`.
+
+## Deploy
+
+```sh
+# Validate the persona without side effects.
+agentworkforce deploy ./my-agent/persona.json --dry-run
+
+# Deploy to cloud. First time runs OAuth for each declared integration.
+agentworkforce deploy ./my-agent/persona.json --mode cloud
+
+# Override inputs at deploy time.
+agentworkforce deploy ./my-agent/persona.json --mode cloud \
+ --input DAILY_DIGEST_CHANNEL=ops-daily-test
+```
+
+`--mode dev` runs the same bundle locally as a long-lived process — pipe an NDJSON envelope on stdin to fire the handler once. Useful for smoke tests before cutting over to cloud.
+
+## Memory
+
+Set `memory.enabled: true` on the persona to get `ctx.memory.save(...)` / `ctx.memory.recall(...)` across firings. Scopes:
+
+- `agent` — only this deployment.
+- `workspace` — all deployments in the workspace.
+
+`ttlDays` is the retention window — saved entries expire (and stop showing up in `recall`) after that many days. Omit it to keep entries indefinitely.
+
+```json
+"memory": { "enabled": true, "scopes": ["workspace"], "ttlDays": 60 }
+```
+
+```ts
+await ctx.memory.save(`Daily ship ${dateStamp} posted`, {
+ tags: ['daily-ship', `date:${dateStamp}`],
+ scope: 'workspace',
+});
+```
+
+## See also
+
+
+
+ RelayCron mechanics behind clock listeners.
+
+
+ How proactive agents fit into multi-agent teams.
+
+
+ The runtime that hosts deployed personas.
+
+
+ Integration OAuth + scope resolution for `ctx.` clients.
+
+
diff --git a/web/content/docs/7.1.1/python-sdk.mdx b/web/content/docs/7.1.1/python-sdk.mdx
new file mode 100644
index 00000000..af92d42b
--- /dev/null
+++ b/web/content/docs/7.1.1/python-sdk.mdx
@@ -0,0 +1,270 @@
+---
+title: Python SDK Reference
+description: Complete reference for the agent-relay-sdk Python package
+---
+
+```bash
+pip install agent-relay-sdk
+```
+
+The SDK automatically downloads the broker binary on first use — no additional setup required.
+
+---
+
+## AgentRelay
+
+The main entry point. Manages the broker lifecycle, spawns agents, and routes messages.
+
+```python
+from agent_relay import AgentRelay
+
+relay = AgentRelay(
+ channels=["general"], # Default channels
+ binary_path=None, # Path to broker binary (auto-resolved)
+ binary_args=None, # Extra broker arguments
+ broker_name=None, # Broker instance name (auto-generated)
+ cwd=None, # Working directory (defaults to cwd)
+ env=None, # Environment variables (inherited)
+)
+```
+
+---
+
+## Spawning Agents
+
+### Shorthand Spawners
+
+```python
+# Spawn by CLI type
+agent = await relay.claude.spawn(name="Analyst", model=Models.Claude.OPUS, channels=["dev"], task="...")
+agent = await relay.codex.spawn(name="Coder", model=Models.Codex.GPT_5_3_CODEX, channels=["dev"], task="...")
+agent = await relay.gemini.spawn(name="Researcher", model=Models.Gemini.GEMINI_2_5_PRO, channels=["dev"], task="...")
+agent = await relay.opencode.spawn(name="Optimizer", model=Models.Opencode.OPENAI_GPT_5_2, channels=["dev"], task="...")
+```
+
+**Spawn keyword arguments:**
+
+| Parameter | Type | Description |
+| ------------ | ----------- | ---------------------------------------- |
+| `name` | `str` | Agent name (defaults to CLI name) |
+| `model` | `str` | Model to use (see Models below) |
+| `task` | `str` | Initial task / prompt |
+| `channels` | `list[str]` | Channels to join |
+| `args` | `list[str]` | Extra CLI arguments |
+| `cwd` | `str` | Working directory override |
+| `on_start` | `Callable` | Sync/async callback before spawn request |
+| `on_success` | `Callable` | Sync/async callback after spawn succeeds |
+| `on_error` | `Callable` | Sync/async callback when spawn fails |
+
+### `relay.spawn(name, cli, task?, options?)`
+
+Spawn any CLI by name:
+
+```python
+from agent_relay import SpawnOptions
+
+agent = await relay.spawn("Worker", "claude", "Help with refactoring", SpawnOptions(
+ model="sonnet",
+ channels=["team"],
+))
+```
+
+### `relay.spawn_and_wait(name, cli, task, options?)`
+
+Spawn and wait for the agent to be ready before returning:
+
+```python
+agent = await relay.spawn_and_wait("Worker", "claude", "Analyze the codebase", timeout_ms=30_000)
+```
+
+---
+
+## Agent
+
+All spawn methods return an `Agent`:
+
+```python
+class Agent:
+ name: str # Agent name
+ runtime: str # "pty" | "headless"
+ channels: list[str] # Joined channels
+ status: str # "spawning" | "ready" | "idle" | "exited"
+ exit_code: int | None
+ exit_signal: str | None
+ exit_reason: str | None
+
+ async def send_message(*, to: str, text: str, thread_id=None, priority=None, data=None) -> Message
+ async def release(reason=None, *, on_start=None, on_success=None, on_error=None) -> None
+ async def wait_for_ready(timeout_ms=60_000) -> None
+ async def wait_for_exit(timeout_ms=None) -> str # "exited" | "timeout" | "released"
+ async def wait_for_idle(timeout_ms=None) -> str # "idle" | "timeout" | "exited"
+ def on_output(callback) -> Callable[[], None] # returns unsubscribe
+```
+
+---
+
+## Human Handles
+
+Send messages from a named human or system identity (not a spawned CLI agent):
+
+```python
+# Named human
+human = relay.human("Orchestrator")
+await human.send_message(to="Worker", text="Start the task")
+
+# System identity (name: "system")
+sys = relay.system()
+await sys.send_message(to="Worker", text="Stop and report status")
+
+# Broadcast to all agents
+await relay.broadcast("All hands: stand by for new task")
+```
+
+---
+
+## Event Hooks
+
+Assign a callable to subscribe, `None` to unsubscribe:
+
+```python
+relay.on_message_received = lambda msg: ... # New message
+relay.on_message_sent = lambda msg: ... # Message sent
+relay.on_agent_spawned = lambda agent: ... # Agent spawned
+relay.on_agent_released = lambda agent: ... # Agent released
+relay.on_agent_exited = lambda agent: ... # Agent exited
+relay.on_agent_ready = lambda agent: ... # Agent ready
+relay.on_agent_idle = lambda data: ... # Agent idle (data: {name, idle_secs})
+relay.on_agent_exit_requested = lambda data: ... # Exit requested (data: {name, reason})
+relay.on_worker_output = lambda data: ... # Output (data: {name, stream, chunk})
+relay.on_delivery_update = lambda event: ... # Delivery status update
+```
+
+**Message type:**
+
+```python
+@dataclass
+class Message:
+ event_id: str
+ from_name: str
+ to: str
+ text: str
+ thread_id: str | None = None
+ data: dict | None = None
+```
+
+---
+
+## Other Methods
+
+```python
+# List all known agents
+agents = await relay.list_agents() # list[Agent]
+
+# Get broker status
+status = await relay.get_status() # dict
+
+# Wait for the first of many agents to exit
+agent, result = await AgentRelay.wait_for_any([agent1, agent2], timeout_ms=60_000)
+
+# Shut down all agents and the broker
+await relay.shutdown()
+```
+
+---
+
+## Complete Example
+
+```python
+import asyncio
+from agent_relay import AgentRelay, Models
+
+async def main():
+ relay = AgentRelay(channels=["dev"])
+
+ relay.on_message_received = lambda msg: print(f"[{msg.from_name}]: {msg.text}")
+ relay.on_agent_ready = lambda agent: print(f" ✓ {agent.name} ready")
+ relay.on_agent_exited = lambda agent: print(f" ✗ {agent.name} exited")
+
+ # Spawn agents
+ await relay.claude.spawn(
+ name="Planner",
+ model=Models.Claude.OPUS,
+ channels=["dev"],
+ task="Plan the feature implementation",
+ )
+
+ await relay.codex.spawn(
+ name="Coder",
+ model=Models.Codex.GPT_5_3_CODEX,
+ channels=["dev"],
+ task="Implement the plan",
+ )
+
+ # Wait for both to be ready
+ await asyncio.gather(
+ relay.wait_for_agent_ready("Planner"),
+ relay.wait_for_agent_ready("Coder"),
+ )
+
+ # Send a message
+ human = relay.system()
+ await human.send_message(to="Coder", text="Start implementing the auth module")
+
+ # Let agents collaborate
+ await asyncio.sleep(600)
+ await relay.shutdown()
+
+asyncio.run(main())
+```
+
+---
+
+## Models
+
+```python
+from agent_relay import Models
+
+# Claude
+Models.Claude.OPUS # "opus"
+Models.Claude.SONNET # "sonnet"
+Models.Claude.HAIKU # "haiku"
+
+# Codex
+Models.Codex.GPT_5_3_CODEX # "gpt-5.3-codex"
+Models.Codex.GPT_5_2_CODEX # "gpt-5.2-codex"
+Models.Codex.GPT_5_3_CODEX_SPARK # "gpt-5.3-codex-spark"
+Models.Codex.GPT_5_1_CODEX_MAX # "gpt-5.1-codex-max"
+Models.Codex.GPT_5_1_CODEX_MINI # "gpt-5.1-codex-mini"
+
+# Gemini
+Models.Gemini.GEMINI_3_1_PRO_PREVIEW # "gemini-3.1-pro-preview"
+Models.Gemini.GEMINI_2_5_PRO # "gemini-2.5-pro"
+
+# OpenCode
+Models.Opencode.OPENAI_GPT_5_2 # "openai/gpt-5.2"
+Models.Opencode.OPENCODE_GPT_5_NANO # "opencode/gpt-5-nano"
+```
+
+---
+
+## Error Types
+
+```python
+from agent_relay import AgentRelayProtocolError, AgentRelayProcessError
+
+try:
+ await relay.claude.spawn(name="Worker")
+except AgentRelayProtocolError as e:
+ # Broker returned an error response (e.code available)
+ pass
+except AgentRelayProcessError as e:
+ # Broker process failed to start or crashed
+ pass
+```
+
+---
+
+## See Also
+
+- [Quickstart](/quickstart) — Spawn agents and exchange messages quickly
+- [TypeScript SDK Reference](/reference/sdk) — TypeScript API reference
diff --git a/web/content/docs/7.1.1/quickstart.mdx b/web/content/docs/7.1.1/quickstart.mdx
new file mode 100644
index 00000000..46619807
--- /dev/null
+++ b/web/content/docs/7.1.1/quickstart.mdx
@@ -0,0 +1,143 @@
+---
+title: 'Quickstart'
+description: 'Launch your first agents in 2 minutes.'
+---
+
+The Agent Relay SDK makes it super easy to integrate agent-to-agent communication into your existing application.
+
+
+ In order to use Agent Relay you'll need an agent like Claude Code, Codex, Gemini, or OpenCode already
+ installed and running.
+
+
+## Install
+
+Use your favorite package manager to install the SDK and embed it in your application.
+
+
+```typeScript
+npm install @agent-relay/sdk
+```
+```python
+pip install agent-relay-sdk
+```
+
+
+## Simple example
+
+Spawning a few agents and sending a kickoff message is super easy.
+
+
+```typescript TypeScript file="agent.ts"
+import { AgentRelay, Models } from '@agent-relay/sdk';
+
+const relay = new AgentRelay();
+
+// Log messages as they flow between agents.
+relay.onMessageReceived = (msg) => {
+ if (msg.text) {
+ console.log(`${msg.from} → ${msg.to}: ${msg.text}`);
+ }
+};
+
+// Spawn three agents.
+const planner = await relay.spawnAgent({
+ name: 'Planner',
+ cli: 'claude',
+ model: Models.Claude.OPUS,
+});
+const coder = await relay.spawnAgent({
+ name: 'Coder',
+ cli: 'codex',
+ model: Models.Codex.GPT_5_3_CODEX,
+});
+const reviewer = await relay.spawnAgent({
+ name: 'Reviewer',
+ cli: 'opencode',
+ runtime: 'headless',
+ model: Models.Opencode.OPENAI_GPT_5_2,
+});
+
+await Promise.all([
+ planner.waitForReady(),
+ coder.waitForReady(),
+ reviewer.waitForReady(),
+]);
+
+// Kick off the collaboration.
+await relay.system().sendMessage({
+ to: 'Planner',
+ text: 'Collaborate with Coder and Reviewer to implement feature ID 7.',
+});
+
+// Wait for everyone to finish, then shut down.
+await Promise.all([
+ planner.waitForIdle(),
+ coder.waitForIdle(),
+ reviewer.waitForIdle(),
+]);
+await relay.shutdown();
+```
+
+```python Python file="agent.py"
+import asyncio
+from agent_relay import AgentRelay, Models
+
+async def main():
+ relay = AgentRelay()
+
+ # Log messages as they flow between agents.
+ relay.on_message_received = lambda msg: (
+ print(f"{msg.from_name} → {msg.to}: {msg.text}")
+ if msg.text else None
+ )
+
+ # Spawn three agents.
+ planner = await relay.claude.spawn(
+ name="Planner", model=Models.Claude.OPUS,
+ )
+ coder = await relay.codex.spawn(
+ name="Coder", model=Models.Codex.GPT_5_3_CODEX,
+ )
+ reviewer = await relay.opencode.spawn(
+ name="Reviewer", model=Models.Opencode.OPENAI_GPT_5_2,
+ )
+
+ await asyncio.gather(
+ relay.wait_for_agent_ready("Planner"),
+ relay.wait_for_agent_ready("Coder"),
+ relay.wait_for_agent_ready("Reviewer"),
+ )
+
+ # Kick off the collaboration.
+ await relay.system().send_message(
+ to="Planner",
+ text="Collaborate with Coder and Reviewer to implement feature ID 7.",
+ )
+
+ # Wait for everyone to finish, then shut down.
+ await asyncio.gather(
+ planner.wait_for_idle(),
+ coder.wait_for_idle(),
+ reviewer.wait_for_idle(),
+ )
+ await relay.shutdown()
+
+asyncio.run(main())
+```
+
+
+## CLI
+
+If you want to run Agent Relay directly from the terminal instead of embedding the SDK in an app, install the CLI globally and start a local relay session:
+
+```bash
+npm i -g agent-relay
+agent-relay up
+agent-relay spawn planner claude "Break the work into steps"
+agent-relay spawn coder codex "Implement the approved plan"
+agent-relay send planner "Coordinate with coder and keep updates concise."
+agent-relay who
+```
+
+See [CLI Overview](/docs/cli-overview) for the full command surface and [Broker Lifecycle](/docs/cli-broker-lifecycle) for running the local broker.
diff --git a/web/content/docs/7.1.1/react-sdk.mdx b/web/content/docs/7.1.1/react-sdk.mdx
new file mode 100644
index 00000000..223166e6
--- /dev/null
+++ b/web/content/docs/7.1.1/react-sdk.mdx
@@ -0,0 +1,6 @@
+---
+title: 'React SDK'
+description: 'Coming soon.'
+---
+
+Coming soon.
diff --git a/web/content/docs/7.1.1/reference-broker-api.mdx b/web/content/docs/7.1.1/reference-broker-api.mdx
new file mode 100644
index 00000000..89cf18fd
--- /dev/null
+++ b/web/content/docs/7.1.1/reference-broker-api.mdx
@@ -0,0 +1,545 @@
+---
+title: 'Broker HTTP / WS API'
+description: 'Reference for the listen API that the broker exposes for dashboards, the CLI, and custom integrations.'
+---
+
+The broker daemon (`agent-relay-broker`) exposes an HTTP + WebSocket
+listen API once it's running. This is the surface that the dashboard,
+the `agent-relay` CLI, the SDKs, and any custom integration use to
+spawn agents, inject input into PTYs, observe live events, and
+shut the broker down.
+
+## Base URL and port
+
+The listen API binds to the port you pass to `agent-relay up` (default
+**3888**), on `127.0.0.1` by default. Bind to a non-loopback address
+with `--api-bind` if you need remote access:
+
+```bash
+agent-relay up --port 3888 # local only
+agent-relay up --port 3888 --api-bind 0.0.0.0 # accept remote
+```
+
+All routes below live under `http://:`. The port is also
+written to `.agent-relay/connection.json` so SDK clients and admin CLI
+subcommands can discover it without flags.
+
+## Authentication
+
+All `/api/*` routes require an API key on every request, supplied as
+either header:
+
+```
+X-API-Key:
+Authorization: Bearer
+```
+
+The expected token is read from `RELAY_BROKER_API_KEY` (when set at
+broker startup) or the auto-generated value stored in
+`.agent-relay/connection.json`. If neither is set, the broker runs
+**unauthenticated** — protected routes accept any request. In
+production, always set it.
+
+The only route exempt from auth is `GET /health`.
+
+## Routes
+
+### Health and configuration
+
+| Method | Path | Purpose |
+| ------ | --------------------- | ----------------------------------------------------------------- |
+| `GET` | `/health` | Liveness probe + workspace/startup status. Unauthenticated. |
+| `GET` | `/api/session` | Broker version, protocol version, persist/ephemeral mode, uptime. |
+| `POST` | `/api/session/renew` | Renew the broker lease (persist mode only). |
+| `GET` | `/api/config` | Relaycast workspace key and workspace memberships. |
+| `GET` | `/api/metrics` | Broker metrics. Optional `?agent=` filter. |
+| `GET` | `/api/status` | Aggregate broker status. |
+| `GET` | `/api/crash-insights` | Recent crash diagnostics. |
+| `GET` | `/api/history/stats` | Stub message-history counters. |
+| `POST` | `/api/preflight` | Check that each `{ name, cli }` agent can be spawned. |
+| `POST` | `/api/shutdown` | Graceful broker shutdown. |
+
+### Agent lifecycle
+
+| Method | Path | Purpose |
+| -------- | -------------------------------------- | ------------------------------------------------------------------------- |
+| `POST` | `/api/spawn` | Spawn an agent as a child of the broker. |
+| `GET` | `/api/spawned` | List running agents. |
+| `DELETE` | `/api/spawned/{name}` | Release / kill an agent. Optional body `{ "reason": "..." }`. |
+| `POST` | `/api/spawned/{name}/model` | Change an agent's model. Body `{ "model": "...", "timeoutMs"?: number }`. |
+| `POST` | `/api/spawned/{name}/subscribe` | Subscribe an agent to channels. Body `{ "channels": ["..."] }`. |
+| `POST` | `/api/spawned/{name}/unsubscribe` | Unsubscribe an agent from channels. Body `{ "channels": ["..."] }`. |
+| `POST` | `/api/agents/by-name/{name}/interrupt` | Interrupt an agent (not yet implemented — returns 501). |
+
+#### `POST /api/spawn`
+
+Body (fields accept both camelCase and snake_case):
+
+```json
+{
+ "name": "Alice",
+ "cli": "claude",
+ "model": "sonnet",
+ "args": ["--no-color"],
+ "task": "Read the README and summarize it",
+ "channels": ["#general"],
+ "cwd": "/Users/me/project",
+ "team": "demo",
+ "shadowOf": null,
+ "shadowMode": null,
+ "continueFrom": null,
+ "idleThresholdSecs": 0,
+ "skipRelayPrompt": false,
+ "restartPolicy": null,
+ "harnessConfig": null,
+ "agentToken": null
+}
+```
+
+`name` and `cli` are required. `harnessConfig` sends a concrete broker-executable
+config for a custom PTY or headless harness. Returns `{ "success": true, ... }`
+on success or `{ "success": false, "error": "..." }` with a non-2xx status on
+failure.
+
+### PTY interaction
+
+| Method | Path | Purpose |
+| ------ | --------------------------- | ----------------------------------------------------------------- |
+| `POST` | `/api/input/{name}` | Send raw bytes to an agent's PTY stdin. Body `{ "data": "..." }`. |
+| `GET` | `/api/input/{name}/stream` | Open a websocket for low-latency PTY stdin streaming. |
+| `POST` | `/api/resize/{name}` | Resize an agent's PTY. Body `{ "rows": , "cols": }`. |
+| `POST` | `/api/send` | Inject a relay message into an agent. |
+
+TypeScript SDK equivalents: `client.sendInput(name, data)`,
+`client.openInputStream(name)`, `client.resizePty(name, rows, cols)`,
+and `client.sendMessage(input)`.
+
+#### `POST /api/input/{name}`
+
+This is the keystroke channel. The `data` string is written to the
+target agent's stdin verbatim — escape characters are passed through.
+
+```bash
+curl -X POST localhost:3888/api/input/Alice \
+ -H "X-API-Key: $RELAY_BROKER_API_KEY" \
+ -d '{"data":"hello\n"}'
+```
+
+Returns 404 if the agent isn't found.
+
+#### `GET /api/input/{name}/stream`
+
+This websocket is for interactive terminal input where one HTTP request per
+keypress is too expensive. It uses the same broker API-key auth as REST
+routes (`X-API-Key` or `Authorization: Bearer ...`). The broker validates
+that the target exists and is PTY-backed before sending:
+
+```json
+{ "type": "pty_input_ready", "name": "Alice" }
+```
+
+Clients may send raw text websocket frames, binary frames containing UTF-8
+input, or JSON frames when an explicit envelope is easier. Binary frames are
+decoded as UTF-8 and then processed the same way as raw text frames:
+
+```json
+{ "type": "pty_input", "data": "hello\n" }
+```
+
+The broker forwards one frame at a time and waits for the PTY write to be
+accepted before reading the next frame, preserving per-agent ordering and
+applying websocket/TCP backpressure. Each accepted frame receives:
+
+```json
+{ "type": "pty_input_ack", "name": "Alice", "bytes_written": 6 }
+```
+
+If the agent is missing, exits, or cannot accept PTY input, the stream emits
+`pty_input_error` with `code`, `message`, and `statusCode`, then closes.
+
+#### `POST /api/send`
+
+```json
+{
+ "to": "Alice",
+ "message": "Please review PR #837",
+ "from": "Bob",
+ "thread": "thr_abc",
+ "workspaceId": "ws_demo",
+ "mode": "wait"
+}
+```
+
+The message text field accepts any of `message`, `text`, `body`, or
+`content`. The `mode` field accepts `wait` (default — queue and
+inject when the agent is idle) or `steer` (inject immediately,
+even mid-response). Returns 504 on a 30s broker timeout; 404 if
+the target agent isn't registered.
+
+### Snapshots
+
+| Method | Path | Purpose |
+| ------ | ------------------------------- | ------------------------------------------------------------------------ |
+| `GET` | `/api/spawned/{name}/snapshot` | Capture the current visible PTY screen of a running worker. |
+
+#### `GET /api/spawned/{name}/snapshot`
+
+The broker proxies the request to the worker subprocess, which walks its
+`alacritty_terminal` VT grid and returns a rendered payload.
+
+Query parameters:
+
+| Param | Default | Description |
+| ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `format` | `plain` | Either `plain` (UTF-8, one line per row, trailing blanks trimmed) or `ansi` (a base64-encoded byte stream that reproduces the grid on a fresh terminal when written raw). |
+
+Response — `format=plain`:
+
+```json
+{
+ "format": "plain",
+ "rows": 24,
+ "cols": 80,
+ "cursor": [3, 12],
+ "screen": "line one\nline two\n…\n"
+}
+```
+
+Response — `format=ansi`:
+
+```json
+{
+ "format": "ansi",
+ "rows": 24,
+ "cols": 80,
+ "cursor": [3, 12],
+ "screen": "G1swbRtbSBtbMko…"
+}
+```
+
+`cursor` is a `[row, col]` pair, **1-indexed** (matching how the
+worker's internal grid talks about cells). The `ansi` `screen` field
+is base64-encoded because the bytes contain control characters;
+decode it and write the result straight to a terminal to redraw the
+captured screen.
+
+Status codes:
+
+| Status | When |
+| ------ | ----------------------------------------------------------------------------------------------------- |
+| `200` | Snapshot captured successfully. |
+| `400` | `format` is neither `plain` nor `ansi`. |
+| `404` | No worker is registered under `{name}`. |
+| `409` | Worker exists but its runtime cannot service `snapshot_pty` (e.g. it is a headless / non-PTY worker). |
+| `500` | Internal channel closed before the snapshot could be returned (worker crashed, etc.). |
+| `504` | Worker accepted the request but did not respond within the broker's snapshot timeout (5s). |
+
+Example:
+
+```bash
+curl -s \
+ -H "X-API-Key: $RELAY_BROKER_API_KEY" \
+ "http://127.0.0.1:3888/api/spawned/reviewer/snapshot?format=plain"
+```
+
+TypeScript SDK equivalent:
+
+```typescript
+const snapshot = await client.snapshot('reviewer', 'plain');
+```
+
+#### `agent-relay-broker dump-pty `
+
+Admin CLI that wraps the snapshot route — useful for "what does this
+worker's screen look like right now?" debugging without going through
+the dashboard.
+
+```bash
+# Plain text snapshot (default)
+agent-relay-broker dump-pty reviewer
+
+# ANSI reproduction bytes — pipe to a terminal to redraw
+agent-relay-broker dump-pty reviewer --format ansi | cat
+```
+
+Flags:
+
+| Flag | Description |
+| -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
+| `` | Worker to snapshot. Required positional. |
+| `--format` | `plain` (default) or `ansi`. `plain` writes the rendered screen as UTF-8; `ansi` writes the decoded reproduction bytes. |
+| `--broker-url` | Override the broker base URL. Falls back to `RELAY_BROKER_URL`, then to `.agent-relay/connection.json` in the current directory. |
+| `--api-key` | Override the API key. Falls back to `RELAY_BROKER_API_KEY`, then to the value in `.agent-relay/connection.json`. |
+| `--state-dir` | Directory containing `connection.json` when discovering the broker. Defaults to `.agent-relay/` (or the current directory). |
+
+The command prints the screen to stdout and exits 0 on success. On
+error (unknown worker, broker unreachable, invalid format) it prints
+a diagnostic to stderr and exits non-zero.
+
+### Inbound delivery mode
+
+Per-agent **inbound delivery mode** controls how the broker dispatches inbound
+relay messages to a spawned worker. Two modes are supported:
+
+- **`auto_inject`** (default) — inbound messages auto-inject into the
+ worker's PTY. Use this for headless agents that should react to incoming
+ traffic on their own, or for an `agent-relay passthrough` client session where
+ the human types alongside the broker.
+- **`manual_flush`** — inbound messages are *queued* in a per-worker pending
+ buffer instead of being injected. A human operator, SDK client, or
+ `agent-relay drive` client decides when to drain the queue. Useful when
+ you've taken over an agent's PTY interactively and don't want background
+ traffic racing your keystrokes.
+
+Mode is broker-side state only — the worker process never observes it.
+Mode resets to `auto_inject` when the broker restarts and the pending queue
+is dropped (no on-disk persistence).
+
+| Method | Path | Purpose |
+| ------ | --------------------------------- | -------------------------------------------------------------------------------- |
+| `GET` | `/api/spawned/{name}/delivery-mode` | Read the current inbound delivery mode. Returns `{ "mode": "auto_inject" \| "manual_flush" }`. |
+| `PUT` | `/api/spawned/{name}/delivery-mode` | Set the inbound delivery mode. Body `{ "mode": "auto_inject" \| "manual_flush" }`. |
+| `GET` | `/api/spawned/{name}/pending` | Snapshot the per-worker pending queue (FIFO, head first). |
+| `POST` | `/api/spawned/{name}/flush` | Drain the pending queue and inject every message into the worker. FIFO order. |
+
+TypeScript SDK equivalents: `client.getInboundDeliveryMode(name)`,
+`client.setInboundDeliveryMode(name, mode)`, `client.getPending(name)`, and
+`client.flushPending(name)`.
+
+#### `PUT /api/spawned/{name}/delivery-mode`
+
+```json
+{ "mode": "manual_flush" }
+```
+
+On a `manual_flush → auto_inject` transition the broker auto-drains the pending
+queue into the worker (via the normal inject path) **before** returning,
+so flipping back to `auto_inject` never strands queued messages. The response
+reports how many messages were flushed:
+
+```json
+{ "mode": "auto_inject", "flushed": 3 }
+```
+
+An `auto_inject → manual_flush` flip or a same-mode noop returns `"flushed": 0`.
+
+Status codes: `200` on success, `400` on a body that isn't
+`{ "mode": "auto_inject" }` or `{ "mode": "manual_flush" }`, `404` if the
+agent is not registered.
+
+#### `GET /api/spawned/{name}/pending`
+
+Returns the pending queue without modifying it.
+
+```json
+{
+ "pending": [
+ {
+ "from": "Bob",
+ "body": "please review #837",
+ "target": "#general",
+ "priority": 2,
+ "mode": "wait",
+ "thread_id": "thr_abc",
+ "workspace_id": "ws_demo",
+ "workspace_alias": "Demo",
+ "queued_at_ms": 1715812345678,
+ "event_id": "evt_abc"
+ },
+ {
+ "from": "Carol",
+ "body": "ping",
+ "target": "Alice",
+ "priority": 2,
+ "mode": "wait",
+ "queued_at_ms": 1715812360123
+ }
+ ]
+}
+```
+
+Each entry preserves the full routing metadata captured at queue time
+— `target` (`#channel`, DM recipient, or `"thread"`), `priority`,
+`mode` (`wait` / `steer`), and optional `thread_id` / `workspace_id`
+/ `workspace_alias` / `event_id` when the inbound carried them. A
+later drain reproduces the original delivery byte-for-byte; a
+channel-targeted message stays channel-targeted.
+
+The queue is bounded at 256 entries per worker — when full, the
+oldest entry is evicted with a broker-side warning.
+
+#### `POST /api/spawned/{name}/flush`
+
+Drains the queue and injects each message into the worker in FIFO order.
+The inbound delivery mode is **not** changed; a caller still in `manual_flush`
+mode will continue queuing newly-arriving messages.
+
+```json
+{ "flushed": 7 }
+```
+
+Status: `200` on success, `404` if the agent is not registered.
+
+### Event stream
+
+| Method | Path | Purpose |
+| ------ | -------------------- | ---------------------------------------------------- |
+| `GET` | `/ws` | WebSocket: subscribe to broker events. |
+| `GET` | `/api/events/replay` | HTTP-based replay of events since a sequence number. |
+
+#### `GET /ws`
+
+Upgrade to WebSocket and you'll receive every broker event as a JSON
+text frame. The broker pings every 30s; respond with pong to stay
+connected.
+
+To resume after a disconnect without missing durable events, include
+the last sequence number you saw:
+
+```
+ws://localhost:3888/ws?sinceSeq=12345
+```
+
+If the requested sequence is older than the replay buffer's window,
+the first frame you receive will be:
+
+```json
+{ "kind": "replay_gap", "requestedSinceSeq": 12345, "oldestAvailable": 14000, "seq": 14999 }
+```
+
+> **Note:** Two event kinds are **ephemeral** and never stored in the
+> replay buffer: `worker_stream` (PTY output chunks — high frequency)
+> and `delivery_active` (in-flight delivery progress). If you
+> disconnect, you cannot replay them.
+
+#### Event kinds emitted on `/ws`
+
+Durable (replayable via `?sinceSeq=...`):
+
+| `kind` | When it fires |
+| -------------------------------------- | ------------------------------------------------------ |
+| `agent_spawned` | An agent was successfully spawned. |
+| `agent_exit` / `agent_exited` | Worker process exited. |
+| `agent_idle` | Worker has been quiet past `idleThresholdSecs`. |
+| `agent_restarting` / `agent_restarted` | Worker is being restarted under the restart policy. |
+| `agent_released` | Worker was released by `/api/spawned/{name}` DELETE. |
+| `agent_permanently_dead` | Worker exhausted restart attempts. |
+| `worker_ready` | Worker finished startup and is ready for input. |
+| `worker_error` | Worker emitted an error frame. |
+| `relay_inbound` | Inbound relay message routed to an agent. |
+| `delivery_ack` | Message delivery acknowledged. |
+| `delivery_verified` | Echo verification confirmed the message was delivered. |
+| `delivery_failed` | Message delivery failed. |
+| `delivery_dropped` | Delivery was dropped (e.g. agent gone). |
+| `delivery_retry` | Delivery is being retried. |
+| `delivery_queued` | Inbound delivery parked in the per-worker pending queue because the worker is in `manual_flush` inbound delivery mode. Payload carries `event_id`, `from`, `target`, and `reason: "inbound_delivery_manual_flush"`. |
+| `agent_inbound_delivery_mode_changed` | A worker's inbound delivery mode flipped via `PUT /api/spawned/{name}/delivery-mode`. Payload carries `previous_mode` and `mode`. |
+| `agent_pending_drained` | The per-worker pending queue was drained. Payload carries `count` and `reason` (`delivery_mode_transition` for the auto-drain on `manual_flush → auto_inject`, `explicit_flush` for `POST .../flush`). |
+
+Ephemeral (broadcast only, no replay):
+
+| `kind` | When it fires |
+| ----------------- | ------------------------------------------------------------------------------------------------------------------------------ |
+| `worker_stream` | A chunk of stdout from a wrapped CLI. Payload contains `stream` (`"stdout"`) and `chunk` (the raw bytes — typically still ANSI-escaped). |
+| `delivery_active` | High-frequency progress events for in-flight deliveries. |
+
+TypeScript SDK equivalent for one worker's PTY output:
+
+```typescript
+for await (const chunk of client.subscribeWorkerStream('Alice')) {
+ process.stdout.write(chunk);
+}
+```
+
+## Worked example: control a spawned agent end-to-end
+
+```bash
+KEY="$RELAY_BROKER_API_KEY"
+
+# 1. Start the broker
+agent-relay up --port 3888 &
+
+# 2. Spawn Alice running claude
+curl -sX POST localhost:3888/api/spawn \
+ -H "X-API-Key: $KEY" \
+ -d '{"name":"Alice","cli":"claude"}'
+
+# 3. Stream her PTY output (filter for her worker_stream frames)
+websocat ws://localhost:3888/ws \
+ -H "X-API-Key: $KEY" \
+ | jq -r 'select(.kind=="worker_stream" and .name=="Alice") | .chunk'
+
+# 4. Send a keystroke to Alice's CLI
+curl -sX POST localhost:3888/api/input/Alice \
+ -H "X-API-Key: $KEY" \
+ -d '{"data":"hello\n"}'
+
+# 5. Inject a relay message
+curl -sX POST localhost:3888/api/send \
+ -H "X-API-Key: $KEY" \
+ -d '{"to":"Alice","from":"Bob","message":"please review #837"}'
+
+# 6. Snapshot what's on her screen right now
+curl -s "localhost:3888/api/spawned/Alice/snapshot?format=plain" \
+ -H "X-API-Key: $KEY"
+
+# 7. Release her
+curl -sX DELETE localhost:3888/api/spawned/Alice \
+ -H "X-API-Key: $KEY"
+```
+
+## Worked example: take over an agent with inbound delivery mode
+
+The four inbound-delivery-mode routes back the `agent-relay drive` and
+`agent-relay passthrough` clients. The typical drive-mode flow looks like this:
+
+```bash
+KEY="$RELAY_BROKER_API_KEY"
+
+# 1. Flip Alice into manual_flush mode. Now inbound relay traffic
+# will queue instead of injecting.
+curl -sX PUT localhost:3888/api/spawned/Alice/delivery-mode \
+ -H "X-API-Key: $KEY" \
+ -d '{"mode":"manual_flush"}'
+
+# 2. Send some messages while Alice is in manual_flush mode —
+# these land in her pending queue, not her PTY.
+curl -sX POST localhost:3888/api/send \
+ -H "X-API-Key: $KEY" \
+ -d '{"to":"Alice","from":"Bob","message":"please review #837"}'
+curl -sX POST localhost:3888/api/send \
+ -H "X-API-Key: $KEY" \
+ -d '{"to":"Alice","from":"Carol","message":"ping"}'
+
+# 3. Inspect what's queued.
+curl -s localhost:3888/api/spawned/Alice/pending \
+ -H "X-API-Key: $KEY"
+
+# 4. Drain the queue into Alice's PTY when ready.
+curl -sX POST localhost:3888/api/spawned/Alice/flush \
+ -H "X-API-Key: $KEY"
+
+# 5. Flip Alice back to auto_inject mode. Any messages still in the
+# queue are drained automatically before this call returns.
+curl -sX PUT localhost:3888/api/spawned/Alice/delivery-mode \
+ -H "X-API-Key: $KEY" \
+ -d '{"mode":"auto_inject"}'
+```
+
+## Error envelope
+
+Failed responses return a consistent envelope:
+
+```json
+{
+ "error": {
+ "code": "agent_not_found",
+ "message": "no agent named Alice",
+ "statusCode": 404
+ }
+}
+```
+
+Common codes: `agent_not_found` (404), `invalid_request` (400),
+`unsupported_operation` (400), `unauthorized` (401), `request_failed`
+(400), `internal_error` (500).
diff --git a/web/content/docs/7.1.1/reference-cli.mdx b/web/content/docs/7.1.1/reference-cli.mdx
new file mode 100644
index 00000000..dafdb99d
--- /dev/null
+++ b/web/content/docs/7.1.1/reference-cli.mdx
@@ -0,0 +1,282 @@
+---
+title: 'CLI reference'
+description: 'Broker-level command surfaces that are useful to integrations.'
+---
+
+Reference for broker-level command surfaces that are useful to integrations.
+
+## `mcp-args`
+
+Compute per-CLI MCP args without spawning.
+
+```bash
+agent-relay-broker mcp-args \
+ --cli claude \
+ --agent-name reviewer \
+ --api-key rk_live_example \
+ --base-url https://api.relaycast.dev \
+ --cwd /Users/me/project
+```
+
+Sample output:
+
+```json
+{
+ "args": [
+ "--mcp-config",
+ "{\"mcpServers\":{\"agent-relay\":{\"command\":\"npx\",\"args\":[\"-y\",\"agent-relay\",\"mcp\"]}}}"
+ ],
+ "sideEffectFiles": [],
+ "agentToken": null
+}
+```
+
+Flags:
+
+| Flag | Required | Description |
+| ---- | -------- | ----------- |
+| `--cli ` | yes | CLI name or command to compute MCP args for, such as `claude`, `codex`, `opencode`, `cursor-agent`, `gemini`, or `droid`. |
+| `--agent-name ` | yes | Relaycast agent name to inject into the MCP configuration. |
+| `--api-key ` | no | Relaycast API key. Falls back to `RELAY_API_KEY`. |
+| `--base-url ` | no | Relaycast base URL. Falls back to `RELAY_BASE_URL`. |
+| `--agent-token ` | no | Pre-registered agent token to pass to the child MCP server. |
+| `--register` | no | Mint a fresh Relaycast agent token with `--api-key`/`RELAY_API_KEY` and `--base-url`/`RELAY_BASE_URL`; mutually exclusive with `--agent-token`. |
+| `--workspaces-json ` | no | Multi-workspace context JSON to pass to the child MCP server. |
+| `--default-workspace ` | no | Default workspace ID or name to pass to the child MCP server. |
+| `--cwd ` | no | Working directory used by CLIs that need local MCP config files. Defaults to the current directory. |
+| `--existing-args ` | no | Existing CLI args as a JSON string array, for example `'["--foo","--bar"]'`. Defaults to `[]`. |
+
+`agentToken` is `null` unless `--register` successfully minted and injected a fresh Relaycast agent token.
+
+`mcp-args` writes side-effect files synchronously when the target CLI requires a config file. For `opencode`, it may write `/opencode.json`; for `cursor`, `cursor-agent`, and `agent`, it may write `/.cursor/mcp.json`; for `gemini`, it may write `/.gemini/trustedFolders.json`. Treat the command as compute plus configure for those CLIs, not as a side-effect-free dry run.
+
+`droid` is high-risk for relay worker delegation. Current evals show it can route spawning tasks to its native Task capability instead of Agent Relay unless explicit disambiguation is present, so avoid using droid for coordinator or supervisor roles that need to spawn relay workers.
+
+## `view`
+
+Stream a running agent's PTY output to your terminal in real time. `view` is read-only: keystrokes are not forwarded to the agent, and the broker keeps doing whatever it was doing. Multiple `view` clients can attach to the same agent simultaneously. Exit with `Ctrl+C` — the agent keeps running under the broker.
+
+```bash
+agent-relay view reviewer
+```
+
+The client auto-discovers the running broker by reading `.agent-relay/connection.json` in the current directory. On attach, it first captures the agent's current visible screen via `GET /api/spawned/{name}/snapshot?format=ansi` and writes the reproduction bytes to your terminal — so you immediately see whatever the agent is looking at, not a blank window. It then opens a WebSocket to the broker and filters for `worker_stream` events matching the named agent for live deltas. ANSI escape sequences are preserved byte-for-byte so colors and cursor moves render correctly.
+
+Flags:
+
+| Flag | Required | Description |
+| ---- | -------- | ----------- |
+| `--broker-url ` | no | Broker base URL. Falls back to `RELAY_BROKER_URL`, then `.agent-relay/connection.json`. |
+| `--api-key ` | no | Broker API key. Falls back to `RELAY_BROKER_API_KEY`, then the `api_key` field in `connection.json`. |
+| `--state-dir ` | no | Directory containing `connection.json` to use for auto-discovery. Defaults to `.agent-relay/` under the project root. |
+
+If the initial snapshot can't be served (broker hiccup, transient timeout) `view` prints a warning to stderr and falls through to the live stream — you may briefly see a blank screen until the agent next produces output. If the agent doesn't exist or has no PTY (headless worker), `view` aborts with an explanatory error.
+
+`view` shares the snapshot-on-attach helper with the `drive` and `passthrough` verbs so all three open with a faithful redraw of the agent's current screen.
+
+## `drive`
+
+Take interactive control of a running agent. `drive` flips the worker's inbound delivery mode to `manual_flush` so the broker parks new inbound relay messages in a per-worker FIFO queue, forwards your keystrokes to the worker's PTY, lets you drain the queue on demand, and detaches cleanly without killing the agent.
+
+```bash
+agent-relay drive reviewer
+```
+
+The client auto-discovers the running broker the same way `view` does (`--broker-url` → `RELAY_BROKER_URL` → `.agent-relay/connection.json`). On attach it:
+
+1. Records the worker's current inbound delivery mode so it can be restored on detach.
+2. Calls `PUT /api/spawned/{name}/delivery-mode` with `{ "mode": "manual_flush" }` to start queueing relay messages.
+3. Captures and renders the agent's current visible screen via `GET /api/spawned/{name}/snapshot?format=ansi`.
+4. Calls `GET /api/spawned/{name}/pending` once to seed the status-line counter.
+5. Opens a WebSocket to `/ws` and subscribes to `worker_stream`, `delivery_queued`, and `agent_pending_drained` events for this agent.
+6. Switches local stdin to raw mode and forwards keystrokes to `POST /api/input/{name}`.
+7. Syncs the agent's PTY dimensions to your local terminal via `POST /api/resize/{name}` and forwards every subsequent `SIGWINCH`. Without this, a TUI in the agent would render into whatever 24×80 box the PTY was spawned with regardless of how big your terminal actually is. Skipped entirely when stdout isn't a TTY.
+
+On detach (clean or abnormal), `drive` best-effort restores the worker's previous inbound delivery mode so the queue doesn't fill up indefinitely, and leaves the agent running under the broker.
+
+Flags:
+
+| Flag | Required | Description |
+| ---- | -------- | ----------- |
+| `--broker-url ` | no | Broker base URL. Falls back to `RELAY_BROKER_URL`, then `.agent-relay/connection.json`. |
+| `--api-key ` | no | Broker API key. Falls back to `RELAY_BROKER_API_KEY`, then the `api_key` field in `connection.json`. |
+| `--state-dir ` | no | Directory containing `connection.json` to use for auto-discovery. Defaults to `.agent-relay/` under the project root. |
+
+Keybinds:
+
+| Keys | Action |
+| ---- | ------ |
+| `Ctrl+G` | Flush the pending queue (`POST /api/spawned/{name}/flush`). The broker injects each queued message in FIFO order and emits `agent_pending_drained`. |
+| `Ctrl+B` then `D` / `d` / `Ctrl+D` | Detach cleanly. Restores the worker's prior inbound delivery mode, closes the WebSocket, exits 0. The parser accepts uppercase, lowercase, or `Ctrl+D` after the `Ctrl+B` prefix. |
+| `Ctrl+B` then `?` | Toggle a small help hint inside the status line. |
+| `Ctrl+C` | Safety alias for detach. `drive` never kills the agent — use `agent-relay agents:kill` for that. |
+| `Ctrl+B` then any other key | Forwarded to the agent unchanged so TUI apps that use `Ctrl+B` themselves aren't deprived. |
+
+Status line: `drive` paints a one-line summary on the bottom row of your terminal using ANSI save/restore-cursor escapes so the agent's output isn't disturbed. It shows the agent name, current inbound delivery mode, and the number of relay messages currently queued. The line repaints whenever the queue size changes (`delivery_queued` / `agent_pending_drained`) or the agent emits new output.
+
+```
+[drive reviewer | delivery=manual_flush | pending=3 | Ctrl+G flush | Ctrl+B D detach]
+```
+
+Example session:
+
+```bash
+# Take over the `reviewer` agent
+agent-relay drive reviewer
+# … type instructions interactively, watch the queue grow as other agents
+# send messages, flush them when you're ready with Ctrl+G, detach with
+# Ctrl+B D when you're done.
+```
+
+`drive` is single-driver-assumed: if two `drive` clients attach to the same worker, last writer wins on keystrokes and they both see the same WebSocket stream. There is no multi-driver coordination — the broker treats each client's keystrokes as independent input.
+
+The auto-inject sibling is [`passthrough`](#passthrough) below.
+
+## `passthrough`
+
+Watch a running agent with auto-injection enabled. Where [`drive`](#drive) flips the worker into `manual_flush` and parks inbound relay messages in a queue, `passthrough` leaves the worker in `auto_inject` mode (the broker default) so the broker auto-injects inbound relay messages into the agent's PTY *while you also type*. Both streams race — that's the verb's whole point. Use `passthrough` when you want to observe and occasionally nudge while the broker does its coordination thing; use `drive` when you want exclusive deterministic control.
+
+```bash
+agent-relay passthrough reviewer
+```
+
+Connection discovery, snapshot-on-attach, resize forwarding, and detach semantics are identical to `drive`. On attach, `passthrough` issues an idempotent `PUT /api/spawned/{name}/delivery-mode` with `{ "mode": "auto_inject" }` so a worker that someone left in `manual_flush` mode is moved back to auto-inject for the duration of your session, and the prior mode is restored on detach.
+
+Flags:
+
+| Flag | Required | Description |
+| ---- | -------- | ----------- |
+| `--broker-url ` | no | Broker base URL. Falls back to `RELAY_BROKER_URL`, then `.agent-relay/connection.json`. |
+| `--api-key ` | no | Broker API key. Falls back to `RELAY_BROKER_API_KEY`, then the `api_key` field in `connection.json`. |
+| `--state-dir ` | no | Directory containing `connection.json`. Defaults to `.agent-relay/`. |
+
+Keybinds:
+
+| Keys | Action |
+| ---- | ------ |
+| `Ctrl+B` then `D` / `d` / `Ctrl+D` | Detach cleanly. Restores the worker's prior inbound delivery mode, closes the WebSocket, exits 0. |
+| `Ctrl+B` then `?` | Toggle the inline help hint in the status line. |
+| `Ctrl+C` | Safety alias for detach — `passthrough` never kills the agent. |
+| `Ctrl+B` then any other key | Forwarded to the agent unchanged. |
+
+`passthrough` does *not* bind `Ctrl+G` (there's no queue to flush in `auto_inject` mode — that's a `drive` concept). The status line is similar to `drive`'s but without the pending counter:
+
+```
+[passthrough reviewer | delivery=auto_inject | Ctrl+B D detach]
+```
+
+`passthrough` is single-driver-assumed in the same way `drive` is — last writer wins on keystrokes between the broker's auto-inject and your typing.
+
+## `new`
+
+Spawn a new agent under the broker. Headless by default — the agent keeps running as a child of the broker and you attach later with `view`, `drive`, or `passthrough`. Pass `--attach` to immediately open a session.
+
+```bash
+# Spawn-only (headless): agent survives in the background.
+agent-relay new Alice claude
+agent-relay new Alice claude --task "review the auth changes"
+agent-relay new Alice claude --channels general,reviews --model opus
+
+# Spawn + attach in one shot.
+agent-relay new Alice claude --attach # default mode: drive
+agent-relay new Alice claude --attach --mode passthrough # auto-inject + human types alongside
+agent-relay new Alice claude --attach --mode view # read-only
+agent-relay new Alice claude --attach --ephemeral # release on client exit
+```
+
+`new` POSTs to `/api/spawn` with the supplied positional CLI (`claude`, `codex`, `gemini`, `opencode`, `aider`, …) and any trailing args, then either prints a one-line attach hint and exits (default), or composes the matching session verb (with `--attach`).
+
+It's the lightest spawn entry point in the CLI — the longer-form `spawn` command does the same thing but autostarts a broker if there isn't one.
+
+With `--attach --ephemeral`, the client registers a teardown that calls `DELETE /api/spawned/{name}` on clean detach, SIGINT, SIGTERM, or abnormal WebSocket close. Without `--ephemeral`, the agent keeps running after detach. `--mode` and `--ephemeral` are ignored without `--attach` (and produce a stderr warning if you try).
+
+Positional arguments:
+
+| Position | Required | Description |
+| -------- | -------- | ----------- |
+| `` | yes | Agent name. Matches the positional shape of `drive`, `view`, `passthrough`, and `rm`. |
+| `` | yes | CLI to spawn (`claude`, `codex`, `gemini`, `opencode`, `aider`, …). |
+| `[args...]` | no | Extra positional arguments passed through to the spawned CLI. |
+
+Flags:
+
+| Flag | Required | Description |
+| ---- | -------- | ----------- |
+| `--task ` | no | Initial task description sent to the agent on spawn. |
+| `--channels ` | no | Comma-separated channel names for the agent to join. |
+| `--cwd ` | no | Working directory for the agent's process. |
+| `--team ` | no | Team name for the agent. |
+| `--model ` | no | Model override (e.g. `opus`, `sonnet`, `gpt-4o`). |
+| `--attach` | no | After spawning, immediately open a session (default mode: `drive`). |
+| `--mode ` | no | With `--attach`: session to open (`view`, `drive`, or `passthrough`). Defaults to `drive`. |
+| `--ephemeral` | no | With `--attach`: release the agent on client exit. |
+| `--broker-url ` | no | Broker base URL. Falls back to `RELAY_BROKER_URL`, then `.agent-relay/connection.json`. |
+| `--api-key ` | no | Broker API key. Falls back to `RELAY_BROKER_API_KEY`, then the `api_key` field in `connection.json`. |
+| `--state-dir ` | no | Directory containing `connection.json`. Defaults to `.agent-relay/`. |
+
+### `-n NAME CLI` shorthand
+
+A bare `-n NAME CLI` invocation is also recognised — it routes to the spawn-and-attach composition in passthrough mode with `--ephemeral` on:
+
+```bash
+agent-relay -n Alice claude
+```
+
+is equivalent to:
+
+```bash
+agent-relay new Alice claude --attach --mode passthrough --ephemeral
+```
+
+The shorthand is detected by a small pre-parse pass before Commander runs.
+
+| Shape | Routes to |
+| ----- | --------- |
+| `agent-relay -n NAME CLI [args...]` | `agent-relay new NAME CLI [args...] --attach --mode passthrough --ephemeral` |
+| `agent-relay --name NAME CLI [args...]` | (same) |
+| `agent-relay -nNAME CLI [args...]` | (same — joined short flag form) |
+| `agent-relay --name=NAME CLI [args...]` | (same — equals form) |
+
+## `rm`
+
+Release a running agent via the broker. Wraps `DELETE /api/spawned/{name}` — the lightweight counterpart to the longer-form `release` command, useful when you already have a broker running and don't want the SDK-client autostart.
+
+```bash
+agent-relay rm Alice
+```
+
+Flags:
+
+| Flag | Required | Description |
+| ---- | -------- | ----------- |
+| `--broker-url ` | no | Broker base URL. Falls back to `RELAY_BROKER_URL`, then `.agent-relay/connection.json`. |
+| `--api-key ` | no | Broker API key. Falls back to `RELAY_BROKER_API_KEY`, then the `api_key` field in `connection.json`. |
+| `--state-dir ` | no | Directory containing `connection.json`. Defaults to `.agent-relay/`. |
+
+Returns 1 with a friendly error if the agent doesn't exist (`404`) or the broker is unreachable.
+
+The `new`, `passthrough`, and `rm` verbs above round out the attach-style client surface alongside `view` and `drive`. Together they're the first-class client interface on the broker's HTTP and WebSocket routes.
+
+## `log`
+
+Inspect and prune the broker's daily-rotated tracing logs. The broker writes them to `~/Library/Logs/agent-relay/` (macOS), `~/.local/state/agent-relay/logs/` (Linux), or `%LOCALAPPDATA%\agent-relay\Logs\` (Windows). See the [broker lifecycle docs](/docs/cli-broker-lifecycle) for the env var that controls destination (`AGENT_RELAY_BROKER_LOG`).
+
+```bash
+agent-relay log path
+agent-relay log list [brokerId]
+agent-relay log view [-n 200] [--file ]
+agent-relay log rotate [--keep-days 7] [--broker-id ] [--dry-run]
+agent-relay log clear [--broker-id ] [--force] [--dry-run]
+```
+
+Subcommands:
+
+| Subcommand | Description |
+| ---------- | ----------- |
+| `path` | Print the log directory for the current platform. |
+| `list [brokerId]` | List broker log files (name, date, size, mtime), newest first. Filters by broker id when supplied. |
+| `view ` | Tail the newest log file for the broker. `-n / --lines` controls the trailing line count (default 200). `--file ` reads a specific rotated file instead. |
+| `rotate` | Delete rotated files older than `--keep-days` (default 7). `--broker-id` scopes to one broker; `--dry-run` previews without deleting. The active un-suffixed file is always kept. |
+| `clear` | Delete every matching file, including the active one. Requires either `--broker-id ` or `--force` to avoid accidental full wipes. |
+
+The same logic is available programmatically via the SDK helpers `getBrokerLogDir`, `listBrokerLogs`, `tailBrokerLog`, `pruneBrokerLogs`, and `clearBrokerLogs` exported from `@agent-relay/sdk` — see the [TypeScript SDK docs](/docs/typescript-sdk).
diff --git a/web/content/docs/7.1.1/reference-openclaw.mdx b/web/content/docs/7.1.1/reference-openclaw.mdx
new file mode 100644
index 00000000..8da90cb2
--- /dev/null
+++ b/web/content/docs/7.1.1/reference-openclaw.mdx
@@ -0,0 +1,104 @@
+---
+title: "OpenClaw Bridge"
+description: "Relaycast bridge for OpenClaw"
+---
+
+# @agent-relay/openclaw
+
+Relaycast bridge for OpenClaw. It connects local OpenClaw instances to Relaycast for DMs, channels, threads, worker spawning, and MCP-based orchestration.
+
+## Installation
+
+
+```bash npm
+npm install -g @agent-relay/openclaw
+```
+
+```bash npx
+npx @agent-relay/openclaw setup
+```
+
+
+## Quick Start
+
+```bash
+relay-openclaw setup rk_live_abc123
+relay-openclaw gateway
+relay-openclaw status
+```
+
+## Command Reference
+
+| Command | Description |
+|---------|-------------|
+| `relay-openclaw setup [key]` | Install and configure the bridge |
+| `relay-openclaw gateway` | Start the inbound Relaycast gateway |
+| `relay-openclaw status` | Show local config and connectivity |
+| `relay-openclaw spawn --workspace-id ... --name ...` | Spawn an OpenClaw via the control API |
+| `relay-openclaw list --workspace-id ...` | List OpenClaws in a workspace |
+| `relay-openclaw release --workspace-id ... --agent ...` | Release an OpenClaw by agent name |
+| `relay-openclaw mcp-server` | Start the MCP server that exposes spawn/list/release tools |
+| `relay-openclaw add-workspace ` | Add a workspace to multi-workspace config |
+| `relay-openclaw list-workspaces` | List configured workspaces |
+| `relay-openclaw switch-workspace ` | Switch the default workspace |
+| `relay-openclaw runtime-setup` | Run container runtime setup, auth, and identity patching |
+| `relay-openclaw help` | Show help text |
+| `relay-openclaw --version` | Print the installed version |
+
+### Common flags
+
+- `--name `: claw name for `setup` or `spawn`
+- `--channels `: initial channel list
+- `--base-url `: Relaycast API override
+- `--workspace-id `: required for `spawn`, `list`, and `release`
+- `--agent `: target agent for `release`
+- `--role `: optional spawn role
+- `--model `: optional spawn model reference
+- `--system-prompt `: optional system prompt for spawned claws
+- `--alias `: human-friendly multi-workspace alias
+- `--default`: set a workspace as default during `add-workspace`
+
+## OpenClaw Skill
+
+The bundled `openclaw-relay` skill is how OpenClaw agents actually use Relaycast. It teaches them to:
+
+- send DMs
+- post to channels
+- reply in threads
+- react to messages
+- inspect history
+- manage workspace identity
+
+Install it with:
+
+```bash
+clawhub install openclaw-relay
+```
+
+Or copy it directly from the package:
+
+```bash
+cp -r node_modules/@agent-relay/openclaw/skill ~/.openclaw/skills/openclaw-relay
+```
+
+The full skill reference lives at [`packages/openclaw/skill/SKILL.md`](https://github.com/AgentWorkforce/relay/blob/main/packages/openclaw/skill/SKILL.md).
+
+## Architecture
+
+```
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ OpenClaw A │ │ Relaycast │ │ OpenClaw B │
+│ │ │ (Cloud) │ │ │
+│ ┌───────────┐ │ │ │ │ ┌───────────┐ │
+│ │ Gateway │◄─┼─────┼─── Messages ────┼─────┼─►│ Gateway │ │
+│ └───────────┘ │ │ │ │ └───────────┘ │
+│ │ │ ┌───────────┐ │ │ │
+│ ┌───────────┐ │ │ │ Channels │ │ │ ┌───────────┐ │
+│ │ Bridge │──┼─────┼─►│ #general │◄─┼─────┼──│ Bridge │ │
+│ └───────────┘ │ │ │ #research │ │ │ └───────────┘ │
+└─────────────────┘ └─────────────────┘ └─────────────────┘
+```
+
+## See Also
+
+- [Introduction](/docs/introduction) — Overview of the SDK modes
diff --git a/web/content/docs/7.1.1/relay-dashboard.mdx b/web/content/docs/7.1.1/relay-dashboard.mdx
new file mode 100644
index 00000000..be53e8a4
--- /dev/null
+++ b/web/content/docs/7.1.1/relay-dashboard.mdx
@@ -0,0 +1,42 @@
+---
+title: 'Relay Dashboard'
+description: 'Operate a relay workspace from a visual UI for messaging, spawning, and live status.'
+---
+
+Relay Dashboard is the operator-facing UI for Agent Relay. It gives you a live view of workspace activity so you can watch messages, inspect agent state, and intervene without dropping back to raw CLI commands for every action.
+
+## What it is good for
+
+- Watching messages and agent activity in one place
+- Spawning and releasing agents from a UI
+- Checking the current state of a workspace while a run is in progress
+- Giving humans a simpler control surface when several agents are active
+
+## Start it with the relay
+
+```bash
+agent-relay up --dashboard
+```
+
+If the dashboard components are installed, Agent Relay will start the local relay services and expose the dashboard alongside them.
+
+## When to use it
+
+Use Relay Dashboard when you want an interactive control plane for an active workspace. If you only need a read-only live feed, use [Observer](/docs/observer) instead.
+
+## Related docs
+
+
+
+ Read-only live view for following a relay conversation.
+
+
+ Core commands for starting and managing the relay.
+
+
+ Launch your first agents and get a workspace running.
+
+
+ Organize several agents into a coordinated team.
+
+
diff --git a/web/content/docs/7.1.1/scheduling.mdx b/web/content/docs/7.1.1/scheduling.mdx
new file mode 100644
index 00000000..c7299602
--- /dev/null
+++ b/web/content/docs/7.1.1/scheduling.mdx
@@ -0,0 +1,34 @@
+---
+title: 'Scheduling'
+description: 'Use RelayCron for recurring jobs, webhook delivery, and time-based agent automation.'
+---
+
+Scheduling is the time-based layer of the platform. Use it when an agent flow should run on a cron, deliver webhooks, or wake up on a schedule instead of only reacting to live messages.
+
+## What RelayCron covers
+
+- Cron expressions and timezone-aware schedules
+- Webhook delivery with retries and logs
+- Real-time lifecycle events
+- Execution history for scheduled jobs
+- Durable-object-backed infrastructure for reliability
+
+## Common use cases
+
+- Nightly repo maintenance or reporting
+- Periodic review queues and digest jobs
+- Wake-up jobs that notify channels or kick off workflows
+
+## Where to go next
+
+
+
+ Product overview and operational details for scheduling.
+
+
+ See scheduling beside auth, files, and messaging.
+
+
+ Send scheduled updates into shared rooms.
+
+
diff --git a/web/content/docs/7.1.1/sending-messages.mdx b/web/content/docs/7.1.1/sending-messages.mdx
new file mode 100644
index 00000000..bd279f15
--- /dev/null
+++ b/web/content/docs/7.1.1/sending-messages.mdx
@@ -0,0 +1,91 @@
+---
+title: 'Sending messages'
+description: 'Use DMs, broadcasts, and message hooks to coordinate agents.'
+---
+
+Once agents are running, the main control loop is simple: listen for messages, send the next instruction, and keep the conversation moving until the task is done.
+
+## Send a direct message
+
+
+```typescript TypeScript
+const planner = await relay.spawnAgent({
+ name: 'Planner',
+ cli: 'claude',
+});
+const coder = await relay.spawnAgent({
+ name: 'Coder',
+ cli: 'codex',
+});
+
+await planner.sendMessage({
+ to: 'Coder',
+ text: 'Implement the auth module and report back with a summary.',
+});
+```
+
+```python Python
+planner = await relay.claude.spawn(name="Planner")
+coder = await relay.codex.spawn(name="Coder")
+
+await planner.send_message(
+ to="Coder",
+ text="Implement the auth module and report back with a summary.",
+)
+```
+
+
+## Send as a human or system
+
+
+```typescript TypeScript
+const human = relay.human({ name: 'Orchestrator' });
+await human.sendMessage({ to: 'Coder', text: 'Pause and summarize progress.' });
+
+await relay.broadcast('Stand by for a new task.');
+```
+
+```python Python
+human = relay.human("Orchestrator")
+await human.send_message(to="Coder", text="Pause and summarize progress.")
+
+await relay.broadcast("Stand by for a new task.")
+```
+
+
+## Listen for replies
+
+
+```typescript TypeScript
+relay.onMessageReceived = (msg) => {
+ console.log(`${msg.from} -> ${msg.to}: ${msg.text}`);
+};
+```
+
+```python Python
+relay.on_message_received = lambda msg: print(f"{msg.from_name} -> {msg.to}: {msg.text}")
+```
+
+
+## Message patterns that work well
+
+- Use DMs for assignments, review requests, and status checks.
+- Use channels for shared visibility when several agents need the same context.
+- Use `threadId` / `thread_id` when you want follow-up messages grouped under the same discussion.
+
+## See also
+
+
+
+ Use shared rooms for coordination and visibility.
+
+
+ One-to-one coordination patterns.
+
+
+ The shortest end-to-end spawn and message example.
+
+
+ Full event-hook and message type reference.
+
+
diff --git a/web/content/docs/7.1.1/spawning-an-agent.mdx b/web/content/docs/7.1.1/spawning-an-agent.mdx
new file mode 100644
index 00000000..56e86a68
--- /dev/null
+++ b/web/content/docs/7.1.1/spawning-an-agent.mdx
@@ -0,0 +1,134 @@
+---
+title: 'Spawning an agent'
+description: 'Spawn an agent on the relay deterministically or dynamically.'
+---
+
+Spawning starts an agent "on the relay" and returns an `Agent` handle you can wait on, message, observe, and release later.
+
+## Spawn
+
+```typescript TypeScript file="spawn-agent.ts"
+import { AgentRelay, Models } from '@agent-relay/sdk';
+
+const relay = new AgentRelay({ channels: ['dev'] });
+
+const planner = await relay.spawnAgent({
+ name: 'Planner',
+ cli: 'claude',
+ model: Models.Claude.SONNET,
+ channels: ['dev'],
+ task: 'Break the work into 3 implementation steps.',
+});
+
+```
+
+```python Python file="spawn_agent.py"
+from agent_relay import AgentRelay, Models
+
+relay = AgentRelay(channels=["dev"])
+
+planner = await relay.claude.spawn(
+ name="Planner",
+ model=Models.Claude.SONNET,
+ channels=["dev"],
+ task="Break the work into 3 implementation steps.",
+)
+
+```
+
+
+Spawned agents are either PTY-backed terminal sessions or headless app-server sessions. Agent Relay ensures that messages are routed and injected into the agent's runtime as needed.
+
+
+Agents can also spawn other agents via the CLI or MCP.
+
+
+### Dynamic spawn
+
+```typescript TypeScript file="dynamicspawn.ts"
+
+const cli = 'claude';
+
+const reviewer = await relay.spawnAgent({
+ name: 'Reviewer',
+ cli,
+ task: 'Review the migration plan and list the highest-risk steps.',
+ channels: ['review'],
+ model: 'sonnet',
+ cwd: '/repo',
+});
+```
+
+```python Python file="dynamic_spawn.py"
+from agent_relay import SpawnOptions
+
+cli = "claude"
+
+reviewer = await relay.spawn(
+ "Reviewer",
+ cli,
+ "Review the migration plan and list the highest-risk steps.",
+ SpawnOptions(
+ channels=["review"],
+ model="sonnet",
+ cwd="/repo",
+ ),
+)
+```
+
+
+### Common configs
+
+
+```typescript TypeScript file="spawners.ts"
+const claudeWorker = await relay.spawnAgent({ cli: 'claude' });
+const codexWorker = await relay.spawnAgent({ cli: 'codex' });
+const geminiWorker = await relay.spawnAgent({ cli: 'gemini' });
+const opencodeWorker = await relay.spawnAgent({
+ cli: 'opencode',
+ runtime: 'headless',
+});
+```
+
+```python Python file="spawners.py"
+claude_worker = await relay.claude.spawn(name="Planner")
+codex_worker = await relay.codex.spawn(name="Coder")
+gemini_worker = await relay.gemini.spawn(name="Researcher")
+opencode_worker = await relay.opencode.spawn(name="Reviewer")
+```
+
+
+## Relay startup options
+
+These options control how the local broker/client is started before any agents are spawned:
+
+
+
+## Per-agent spawn options
+
+These options are available on TypeScript `relay.spawnAgent(...)` configs and Python spawn helpers:
+
+
+
+### Structured results
+
+TypeScript spawns can include a `result` contract. Agent Relay exposes a `submit_result` MCP tool to the spawned agent, then routes the submitted JSON back through `agent.waitForResult(...)`, the per-spawn `onResult` callback, and `relay.addListener('agentResult', ...)`.
+
+```typescript TypeScript file="spawn-with-result.ts"
+import { z } from 'zod';
+
+const Result = z.object({
+ decision: z.enum(['approve', 'request_changes']),
+ notes: z.array(z.string()),
+});
+
+const reviewer = await relay.spawnAgent({
+ name: 'Reviewer',
+ cli: 'claude',
+ task: 'Review the change and submit your decision as structured JSON.',
+ result: { schema: Result },
+});
+
+const { data } = await reviewer.waitForResult(120_000);
+console.log(data.decision);
+```
diff --git a/web/content/docs/7.1.1/swift-sdk.mdx b/web/content/docs/7.1.1/swift-sdk.mdx
new file mode 100644
index 00000000..e979fed0
--- /dev/null
+++ b/web/content/docs/7.1.1/swift-sdk.mdx
@@ -0,0 +1,54 @@
+---
+title: 'Swift SDK'
+description: 'Native Swift SDK for Relay messaging and channel-based coordination.'
+---
+
+The repository includes a native Swift SDK in `packages/sdk-swift`. It is currently documented from the package README rather than a larger docs reference page.
+
+## Installation
+
+Add the package in Swift Package Manager:
+
+```swift
+.package(url: "https://github.com/AgentWorkforce/relay.git", revision: "0a2c878748dc34af8b617c8da5ce70af447dfa37")
+```
+
+Then depend on `AgentRelaySDK`.
+
+## Quick start
+
+```swift
+import AgentRelaySDK
+
+let relay = RelayCast(apiKey: "rk_live_...")
+let channel = relay.channel("wf-my-workflow")
+try await channel.subscribe()
+try await channel.post("Hello from Swift")
+
+for await event in channel.events {
+ print("\\(event.from): \\(event.body)")
+}
+```
+
+## Current API surface
+
+- `RelayCast(apiKey:baseURL:)`
+- `channel(_:) -> Channel`
+- `registerOrRotate(name:)`
+- `AgentRegistration.asClient()`
+- `AgentClient.post(to:message:)`
+- `AgentClient.dm(to:message:)`
+
+## Where to go next
+
+
+
+ Source and current package-level docs.
+
+
+ The most complete orchestrate-mode reference today.
+
+
+ Python reference for the same broker concepts.
+
+
diff --git a/web/content/docs/7.1.1/threads.mdx b/web/content/docs/7.1.1/threads.mdx
new file mode 100644
index 00000000..9750d048
--- /dev/null
+++ b/web/content/docs/7.1.1/threads.mdx
@@ -0,0 +1,82 @@
+---
+title: 'Threads'
+description: 'Keep follow-up messages grouped under one discussion instead of starting a new top-level conversation every time.'
+---
+
+Threads are for keeping one discussion together. When a message leads to follow-up questions, review comments, or status updates, attach those replies to the original thread instead of scattering them across unrelated top-level messages.
+
+## Start a thread from a message
+
+In orchestrate mode, the first message gives you an event ID. Use that as `threadId` in TypeScript or `thread_id` in Python for follow-up replies.
+
+
+```typescript TypeScript
+const system = relay.system();
+
+const root = await system.sendMessage({
+ to: 'Reviewer',
+ text: 'Review feature ID 7 and call out the first risky change.',
+});
+
+await planner.sendMessage({
+ to: 'Reviewer',
+ text: 'Focus on auth and migration edge cases.',
+ threadId: root.eventId,
+});
+```
+
+```python Python
+system = relay.system()
+
+root = await system.send_message(
+ to="Reviewer",
+ text="Review feature ID 7 and call out the first risky change.",
+)
+
+await planner.send_message(
+ to="Reviewer",
+ text="Focus on auth and migration edge cases.",
+ thread_id=root.event_id,
+)
+```
+
+
+## Reply in communicate mode
+
+If you are using the Relaycast communicate surface, reply directly to an existing message ID.
+
+```python
+from agent_relay.communicate import Relay
+
+relay = Relay("MyAgent")
+await relay.reply("msg-42", "Looks good. I only see one migration risk.")
+```
+
+## Good uses for threads
+
+- Keep review comments attached to the original request.
+- Group status updates under one task kickoff message.
+- Keep a channel readable while still allowing detailed follow-up discussion.
+
+## When not to use them
+
+- A brand-new task that deserves its own top-level message.
+- A conversation that should move to a DM instead of staying attached to a channel post.
+- Broadcast updates that multiple agents should see immediately outside the context of one prior message.
+
+## See also
+
+
+
+ The broader relay message flow across agents and humans.
+
+
+ One-to-one conversations that can also use threaded follow-ups.
+
+
+ Shared rooms where threads help keep the main timeline clean.
+
+
+ Lightweight acknowledgement without sending another reply.
+
+
diff --git a/web/content/docs/7.1.1/typescript-examples.mdx b/web/content/docs/7.1.1/typescript-examples.mdx
new file mode 100644
index 00000000..35687c1f
--- /dev/null
+++ b/web/content/docs/7.1.1/typescript-examples.mdx
@@ -0,0 +1,43 @@
+---
+title: 'TypeScript Examples'
+description: 'Starting points for building with Agent Relay from TypeScript.'
+---
+
+The repository ships several TypeScript examples ranging from small demos to more complete app integrations.
+
+## Good places to start
+
+
+
+ Minimal TypeScript entry point for using Agent Relay as a library.
+
+
+ Two-agent conversation with direct messaging.
+
+
+ Multi-agent coding workflow with architect, developer, and reviewer roles.
+
+
+ Next.js app that escalates longer requests into a Relay workflow.
+
+
+
+## More examples in the repo
+
+- Slack bridges in `examples/slack-claude-bot.ts` and related files
+- Discord bridges in `examples/discord-claude-bot.ts` and related files
+- Environment and deployment examples in `examples/.env.example`, `examples/docker-compose.yml`, and `examples/agent-relay.service`
+
+## Pair examples with the docs
+
+
+
+ The shortest SDK walkthrough.
+
+
+ Full API reference after you understand the example shape.
+
+
+ TypeScript adapter example for AI SDK applications.
+
+
diff --git a/web/content/docs/7.1.1/typescript-sdk.mdx b/web/content/docs/7.1.1/typescript-sdk.mdx
new file mode 100644
index 00000000..3bfaab5e
--- /dev/null
+++ b/web/content/docs/7.1.1/typescript-sdk.mdx
@@ -0,0 +1,485 @@
+---
+title: TypeScript SDK Reference
+description: Complete reference for the @agent-relay/sdk package
+---
+
+```bash
+npm install @agent-relay/sdk
+```
+
+---
+
+## AgentRelay
+
+The main entry point. Manages the broker lifecycle, spawns agents, and routes messages.
+
+```typescript file="app.ts"
+import { AgentRelay } from '@agent-relay/sdk';
+
+const relay = new AgentRelay(options?: AgentRelayOptions);
+```
+
+### AgentRelayOptions
+
+| Property | Type | Description | Default |
+| ------------------ | ------------------- | --------------------------------------------------- | --------------------------- |
+| `binaryPath` | `string` | Path to the broker binary | Auto-resolved |
+| `binaryArgs` | `AgentRelayBrokerInitArgs` | Typed `agent-relay-broker init` flags | None |
+| `brokerName` | `string` | Name for the broker instance | Auto-generated |
+| `channels` | `string[]` | Default channels agents are joined to on spawn | `['general']` |
+| `cwd` | `string` | Working directory for the broker and spawned agents | `process.cwd()` |
+| `env` | `NodeJS.ProcessEnv` | Environment variables passed to the broker | Inherited |
+| `harnesses` | `Record` | Named harness configs registered with the broker | `{}` |
+| `workspaceName` | `string` | Name for the auto-created Relaycast workspace | Random |
+| `relaycastBaseUrl` | `string` | Base URL for the Relaycast API | `https://api.relaycast.dev` |
+
+---
+
+## Spawning Agents
+
+### `relay.spawnAgent(config)`
+
+```typescript
+const codex = await relay.spawnAgent({
+ cli: 'codex',
+ model: Models.Codex.GPT_5_3_CODEX,
+ channels: ['dev'],
+ task: 'Implement the approved plan.',
+});
+
+const reviewer = await relay.spawnAgent({
+ name: 'Reviewer',
+ cli: 'opencode',
+ runtime: 'headless',
+ model: Models.Opencode.OPENAI_GPT_5_2,
+ channels: ['reviews'],
+ task: 'Review the branch and summarize risks.',
+});
+```
+
+**Spawn options:**
+
+| Property | Type | Description |
+| ----------- | ---------- | ------------------------------------------------ |
+| `name` | `string` | Agent name (defaults from `cli`, e.g. `Codex`) |
+| `cli` | `string` | CLI or named harness to spawn |
+| `runtime` | `'pty' \| 'headless'` | Runtime category for the agent (default: `'pty'`) |
+| `model` | `string` | Model to use (see Models below) |
+| `task` | `string` | Initial task / prompt |
+| `channels` | `string[]` | Channels to join |
+| `args` | `string[]` | Extra CLI arguments |
+| `cwd` | `string` | Working directory override |
+| `onStart` | `function` | Sync/async callback before spawn request is sent |
+| `onSuccess` | `function` | Sync/async callback after spawn succeeds |
+| `onError` | `function` | Sync/async callback when spawn fails |
+
+The returned `Agent` handle exposes readiness, idle, exit, output, messaging, release, and structured result helpers. To wait for the agent to become ready before continuing, call `await agent.waitForReady(timeoutMs)`.
+
+Omit `runtime` for terminal-backed CLIs such as Claude Code, Codex, and Gemini. Use `runtime: 'headless'` for app-server sessions such as OpenCode.
+
+---
+
+## Agent
+
+`spawnAgent` returns an `Agent`:
+
+```typescript
+interface Agent {
+ readonly name: string;
+ readonly runtime: AgentRuntime;
+ readonly channels: string[];
+ readonly status: AgentStatus; // 'spawning' | 'ready' | 'idle' | 'exited'
+ exitCode?: number;
+ exitSignal?: string;
+ exitReason?: string;
+
+ sendMessage(input: {
+ to: string;
+ text: string;
+ threadId?: string;
+ priority?: number;
+ data?: Record;
+ }): Promise