├── .env.example ├── .gitignore ├── README.md ├── biome.json ├── components.json ├── next.config.ts ├── package.json ├── pnpm-lock.yaml ├── postcss.config.mjs ├── public ├── file.svg ├── globe.svg ├── next.svg ├── vercel.svg └── window.svg ├── src ├── actions │ └── session.ts ├── app │ ├── api │ │ └── chat │ │ │ ├── lib │ │ │ ├── prompts.ts │ │ │ ├── script.js │ │ │ └── vision.ts │ │ │ └── route.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── components │ ├── browser.tsx │ ├── chat-input.tsx │ ├── chat-message.tsx │ ├── chat-panel.tsx │ └── ui │ │ ├── accordion.tsx │ │ ├── button.tsx │ │ └── resizable.tsx └── lib │ ├── operator │ ├── actions.ts │ ├── browser.ts │ ├── dom.ts │ └── session.ts │ ├── upstash │ └── upstash.ts │ ├── use-mobile.ts │ └── utils.ts ├── tailwind.config.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | BROWSERBASE_PROJECT_ID= 2 | BROWSERBASE_API_KEY= 3 | ANTHROPIC_API_KEY= 4 | 5 | # optional, for rate limiting 6 | UPSTASH_REDIS_REST_URL= 7 | UPSTASH_REDIS_REST_TOKEN= 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI Web Operator 2 | 3 | An open-source version of operator. Uses Browserbase and Vercel AI SDK + vision via claude 4 | 5 | 6 | ## Setup Requirements 7 | 8 | 1. API Keys needed: 9 | - Browserbase API key 10 | - Browserbase Project Id 11 | - Anthropic API key 12 | - Upstash Redis credentials (optional, for rate limiting) 13 | 14 | Note: it requires a paid Browserbase plan. (Keep alive sessions are not supported on the FREE plan). 15 | 16 | ## Getting Started 17 | 18 | First, run the development server: 19 | 20 | ```bash 21 | pnpm run dev 22 | ``` 23 | 24 | Open [http://localhost:3000](http://localhost:3000) with your browser 25 | 26 | ## Environment Variables 27 | 28 | Create a `.env.local` file with: 29 | 30 | ```env 31 | BROWSERBASE_API_KEY=your_key_here 32 | BROWSERBASE_PROJECT_ID=your-key-here 33 | ANTHROPIC_API_KEY=your_key_here 34 | UPSTASH_REDIS_REST_URL=optional_redis_url 35 | UPSTASH_REDIS_REST_TOKEN=optional_redis_token 36 | ``` 37 | 38 | 39 | ## Learn More 40 | 41 | - [Browserbase Documentation](https://Browserbase.com/docs) 42 | - [Vercel AI SDK](https://sdk.vercel.ai/docs) 43 | - [Anthropic Claude API](https://docs.anthropic.com/claude/docs) 44 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "formatter": { 7 | "enabled": true, 8 | "indentWidth": 2, 9 | "indentStyle": "space", 10 | "ignore": [ 11 | "**/node_modules", 12 | "**/dist", 13 | "**/build", 14 | "**/public", 15 | "**/.turbo", 16 | "**/.next" 17 | ] 18 | }, 19 | "linter": { 20 | "enabled": true, 21 | "rules": { 22 | "recommended": true, 23 | "a11y": { 24 | "noSvgWithoutTitle": "off", 25 | "useButtonType": "off", 26 | "useAltText": "off", 27 | "useKeyWithClickEvents": "off", 28 | "useSemanticElements": "off", 29 | "noLabelWithoutControl": "off" 30 | }, 31 | "correctness": { 32 | "noUnusedVariables": "warn", 33 | "useExhaustiveDependencies": "warn" 34 | }, 35 | "complexity": { 36 | "noBannedTypes": "off" 37 | }, 38 | "style": { 39 | "useImportType": "warn", 40 | "noNonNullAssertion": "off" 41 | }, 42 | "security": { 43 | "noDangerouslySetInnerHtml": "off" 44 | }, 45 | "suspicious": { 46 | "noAssignInExpressions": "off" 47 | }, 48 | "nursery": { 49 | "useSortedClasses": { 50 | "level": "warn", 51 | "options": { 52 | "attributes": ["classList"], 53 | "functions": ["clsx", "cva", "tw"] 54 | } 55 | } 56 | } 57 | }, 58 | "ignore": [ 59 | "**/node_modules", 60 | "**/dist", 61 | "**/build", 62 | "**/public", 63 | "**/.turbo", 64 | "**/.next" 65 | ] 66 | }, 67 | "overrides": [ 68 | { 69 | "include": ["**/*.test.ts"] 70 | } 71 | ], 72 | "vcs": { 73 | "enabled": true, 74 | "clientKind": "git", 75 | "useIgnoreFile": true 76 | }, 77 | "files": { 78 | "ignore": [ 79 | "**/node_modules", 80 | "**/dist", 81 | "**/build", 82 | "**/public", 83 | "**/.turbo", 84 | "**/.next", 85 | "**/src/lib/operator/dom.ts", 86 | "**/src/app/api/chat/lib/script.js" 87 | ] 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browser", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "biome check .", 10 | "lint:fix": "biome check . --write", 11 | "typecheck": "tsc --noEmit", 12 | "check": "pnpm lint && pnpm typecheck" 13 | }, 14 | "dependencies": { 15 | "@ai-sdk/anthropic": "^1.1.5", 16 | "@ai-sdk/google": "^1.1.5", 17 | "@ai-sdk/openai": "^1.1.5", 18 | "@ai-sdk/provider": "^1.0.6", 19 | "@browserbasehq/sdk": "^2.0.0", 20 | "@radix-ui/react-accordion": "^1.2.2", 21 | "@radix-ui/react-slot": "^1.1.1", 22 | "@upstash/ratelimit": "^2.0.5", 23 | "@upstash/redis": "^1.34.3", 24 | "@vercel/analytics": "^1.4.1", 25 | "ai": "^4.1.9", 26 | "class-variance-authority": "^0.7.1", 27 | "clsx": "^2.1.1", 28 | "lucide-react": "^0.474.0", 29 | "motion": "^12.0.6", 30 | "next": "15.1.6", 31 | "openai": "^4.80.1", 32 | "playwright-core": "^1.50.0", 33 | "react": "^19.0.0", 34 | "react-dom": "^19.0.0", 35 | "react-intersection-observer": "^9.15.1", 36 | "react-resizable-panels": "^2.1.7", 37 | "react-textarea-autosize": "^8.5.7", 38 | "tailwind-merge": "^2.6.0", 39 | "tailwindcss-animate": "^1.0.7", 40 | "zod": "^3.24.1" 41 | }, 42 | "devDependencies": { 43 | "@biomejs/biome": "^1.9.4", 44 | "@types/node": "^20", 45 | "@types/react": "^19", 46 | "@types/react-dom": "^19", 47 | "postcss": "^8", 48 | "tailwindcss": "^3.4.1", 49 | "typescript": "^5" 50 | }, 51 | "packageManager": "pnpm@9.15.4" 52 | } 53 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@ai-sdk/anthropic': 12 | specifier: ^1.1.5 13 | version: 1.1.5(zod@3.24.1) 14 | '@ai-sdk/google': 15 | specifier: ^1.1.5 16 | version: 1.1.5(zod@3.24.1) 17 | '@ai-sdk/openai': 18 | specifier: ^1.1.5 19 | version: 1.1.5(zod@3.24.1) 20 | '@ai-sdk/provider': 21 | specifier: ^1.0.6 22 | version: 1.0.6 23 | '@browserbasehq/sdk': 24 | specifier: ^2.0.0 25 | version: 2.0.0 26 | '@radix-ui/react-accordion': 27 | specifier: ^1.2.2 28 | version: 1.2.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 29 | '@radix-ui/react-slot': 30 | specifier: ^1.1.1 31 | version: 1.1.1(@types/react@19.0.8)(react@19.0.0) 32 | '@upstash/ratelimit': 33 | specifier: ^2.0.5 34 | version: 2.0.5(@upstash/redis@1.34.3) 35 | '@upstash/redis': 36 | specifier: ^1.34.3 37 | version: 1.34.3 38 | '@vercel/analytics': 39 | specifier: ^1.4.1 40 | version: 1.4.1(next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) 41 | ai: 42 | specifier: ^4.1.9 43 | version: 4.1.9(react@19.0.0)(zod@3.24.1) 44 | class-variance-authority: 45 | specifier: ^0.7.1 46 | version: 0.7.1 47 | clsx: 48 | specifier: ^2.1.1 49 | version: 2.1.1 50 | lucide-react: 51 | specifier: ^0.474.0 52 | version: 0.474.0(react@19.0.0) 53 | motion: 54 | specifier: ^12.0.6 55 | version: 12.0.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 56 | next: 57 | specifier: 15.1.6 58 | version: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 59 | openai: 60 | specifier: ^4.80.1 61 | version: 4.80.1(zod@3.24.1) 62 | playwright-core: 63 | specifier: ^1.50.0 64 | version: 1.50.0 65 | react: 66 | specifier: ^19.0.0 67 | version: 19.0.0 68 | react-dom: 69 | specifier: ^19.0.0 70 | version: 19.0.0(react@19.0.0) 71 | react-intersection-observer: 72 | specifier: ^9.15.1 73 | version: 9.15.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 74 | react-resizable-panels: 75 | specifier: ^2.1.7 76 | version: 2.1.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 77 | react-textarea-autosize: 78 | specifier: ^8.5.7 79 | version: 8.5.7(@types/react@19.0.8)(react@19.0.0) 80 | tailwind-merge: 81 | specifier: ^2.6.0 82 | version: 2.6.0 83 | tailwindcss-animate: 84 | specifier: ^1.0.7 85 | version: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.1(@types/node@20.17.16)(typescript@5.7.3))) 86 | zod: 87 | specifier: ^3.24.1 88 | version: 3.24.1 89 | devDependencies: 90 | '@biomejs/biome': 91 | specifier: ^1.9.4 92 | version: 1.9.4 93 | '@types/node': 94 | specifier: ^20 95 | version: 20.17.16 96 | '@types/react': 97 | specifier: ^19 98 | version: 19.0.8 99 | '@types/react-dom': 100 | specifier: ^19 101 | version: 19.0.3(@types/react@19.0.8) 102 | postcss: 103 | specifier: ^8 104 | version: 8.5.1 105 | tailwindcss: 106 | specifier: ^3.4.1 107 | version: 3.4.17(ts-node@10.9.1(@types/node@20.17.16)(typescript@5.7.3)) 108 | typescript: 109 | specifier: ^5 110 | version: 5.7.3 111 | 112 | packages: 113 | 114 | '@ai-sdk/anthropic@1.1.5': 115 | resolution: {integrity: sha512-HGs69t2dsHlt+c4nCeUcdx5ojXs25dH8xly88TZZoyjfiCRWjmyjQxiPHZL2ME864a+vEpQSmvn8DgvMFA4p2g==} 116 | engines: {node: '>=18'} 117 | peerDependencies: 118 | zod: ^3.0.0 119 | 120 | '@ai-sdk/google@1.1.5': 121 | resolution: {integrity: sha512-hj3v7xi0Gq0KXy1w0hdm+VsNJ9HPdtJ2rEpAjy/sbhY9QTkPdoSBKgRs89Jq5ivQQnnoHumq1Q8RZW/WQq3ZKQ==} 122 | engines: {node: '>=18'} 123 | peerDependencies: 124 | zod: ^3.0.0 125 | 126 | '@ai-sdk/openai@1.1.5': 127 | resolution: {integrity: sha512-pmZPeb99oWlV3PqMH5DVWtMGZgwWznInjdF0Bi4q1mKoEjomDXSYw+imvzMoSbpCrcGB9uFFTVx2VeR/jvcppg==} 128 | engines: {node: '>=18'} 129 | peerDependencies: 130 | zod: ^3.0.0 131 | 132 | '@ai-sdk/provider-utils@2.1.5': 133 | resolution: {integrity: sha512-PcNR7E4ovZGV/J47gUqaFlvzorgca6uUfN5WzfXJSFWeOeLunN+oxRVwgUOwj0zbmO0yGQTHQD+FHVw8s3Rz8w==} 134 | engines: {node: '>=18'} 135 | peerDependencies: 136 | zod: ^3.0.0 137 | peerDependenciesMeta: 138 | zod: 139 | optional: true 140 | 141 | '@ai-sdk/provider@1.0.6': 142 | resolution: {integrity: sha512-hwj/gFNxpDgEfTaYzCYoslmw01IY9kWLKl/wf8xuPvHtQIzlfXWmmUwc8PnCwxyt8cKzIuV0dfUghCf68HQ0SA==} 143 | engines: {node: '>=18'} 144 | 145 | '@ai-sdk/react@1.1.6': 146 | resolution: {integrity: sha512-kP5pimLyNWldw8+0j3ym+AACFEXcQHdELNtk45wDJA3HoH486x/zffdn7yLc3c1DOu5apew+COl8CNL4A+2E4g==} 147 | engines: {node: '>=18'} 148 | peerDependencies: 149 | react: ^18 || ^19 || ^19.0.0-rc 150 | zod: ^3.0.0 151 | peerDependenciesMeta: 152 | react: 153 | optional: true 154 | zod: 155 | optional: true 156 | 157 | '@ai-sdk/ui-utils@1.1.6': 158 | resolution: {integrity: sha512-YAwZhFwpIcvWERIjkET2o2MAwMFfJG18WdtcIjtxxMW7hA0bt5cliOV78DVcwRrxqJ2IKBlxaFmwUjW6M4SdOQ==} 159 | engines: {node: '>=18'} 160 | peerDependencies: 161 | zod: ^3.0.0 162 | peerDependenciesMeta: 163 | zod: 164 | optional: true 165 | 166 | '@alloc/quick-lru@5.2.0': 167 | resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} 168 | engines: {node: '>=10'} 169 | 170 | '@babel/runtime@7.26.7': 171 | resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} 172 | engines: {node: '>=6.9.0'} 173 | 174 | '@biomejs/biome@1.9.4': 175 | resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} 176 | engines: {node: '>=14.21.3'} 177 | hasBin: true 178 | 179 | '@biomejs/cli-darwin-arm64@1.9.4': 180 | resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} 181 | engines: {node: '>=14.21.3'} 182 | cpu: [arm64] 183 | os: [darwin] 184 | 185 | '@biomejs/cli-darwin-x64@1.9.4': 186 | resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} 187 | engines: {node: '>=14.21.3'} 188 | cpu: [x64] 189 | os: [darwin] 190 | 191 | '@biomejs/cli-linux-arm64-musl@1.9.4': 192 | resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} 193 | engines: {node: '>=14.21.3'} 194 | cpu: [arm64] 195 | os: [linux] 196 | 197 | '@biomejs/cli-linux-arm64@1.9.4': 198 | resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} 199 | engines: {node: '>=14.21.3'} 200 | cpu: [arm64] 201 | os: [linux] 202 | 203 | '@biomejs/cli-linux-x64-musl@1.9.4': 204 | resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} 205 | engines: {node: '>=14.21.3'} 206 | cpu: [x64] 207 | os: [linux] 208 | 209 | '@biomejs/cli-linux-x64@1.9.4': 210 | resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} 211 | engines: {node: '>=14.21.3'} 212 | cpu: [x64] 213 | os: [linux] 214 | 215 | '@biomejs/cli-win32-arm64@1.9.4': 216 | resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} 217 | engines: {node: '>=14.21.3'} 218 | cpu: [arm64] 219 | os: [win32] 220 | 221 | '@biomejs/cli-win32-x64@1.9.4': 222 | resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} 223 | engines: {node: '>=14.21.3'} 224 | cpu: [x64] 225 | os: [win32] 226 | 227 | '@browserbasehq/sdk@2.0.0': 228 | resolution: {integrity: sha512-BdPlZyn0dpXlL70gNK4acpqWIRB+edo2z0/GalQdWghRq8iQjySd9fVIF3evKH1p2wCYekZJRK6tm29YfXB67g==} 229 | 230 | '@cspotcode/source-map-support@0.8.1': 231 | resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 232 | engines: {node: '>=12'} 233 | 234 | '@emnapi/runtime@1.3.1': 235 | resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} 236 | 237 | '@img/sharp-darwin-arm64@0.33.5': 238 | resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} 239 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 240 | cpu: [arm64] 241 | os: [darwin] 242 | 243 | '@img/sharp-darwin-x64@0.33.5': 244 | resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} 245 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 246 | cpu: [x64] 247 | os: [darwin] 248 | 249 | '@img/sharp-libvips-darwin-arm64@1.0.4': 250 | resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} 251 | cpu: [arm64] 252 | os: [darwin] 253 | 254 | '@img/sharp-libvips-darwin-x64@1.0.4': 255 | resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} 256 | cpu: [x64] 257 | os: [darwin] 258 | 259 | '@img/sharp-libvips-linux-arm64@1.0.4': 260 | resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} 261 | cpu: [arm64] 262 | os: [linux] 263 | 264 | '@img/sharp-libvips-linux-arm@1.0.5': 265 | resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} 266 | cpu: [arm] 267 | os: [linux] 268 | 269 | '@img/sharp-libvips-linux-s390x@1.0.4': 270 | resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} 271 | cpu: [s390x] 272 | os: [linux] 273 | 274 | '@img/sharp-libvips-linux-x64@1.0.4': 275 | resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} 276 | cpu: [x64] 277 | os: [linux] 278 | 279 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 280 | resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} 281 | cpu: [arm64] 282 | os: [linux] 283 | 284 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 285 | resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} 286 | cpu: [x64] 287 | os: [linux] 288 | 289 | '@img/sharp-linux-arm64@0.33.5': 290 | resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} 291 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 292 | cpu: [arm64] 293 | os: [linux] 294 | 295 | '@img/sharp-linux-arm@0.33.5': 296 | resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} 297 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 298 | cpu: [arm] 299 | os: [linux] 300 | 301 | '@img/sharp-linux-s390x@0.33.5': 302 | resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} 303 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 304 | cpu: [s390x] 305 | os: [linux] 306 | 307 | '@img/sharp-linux-x64@0.33.5': 308 | resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} 309 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 310 | cpu: [x64] 311 | os: [linux] 312 | 313 | '@img/sharp-linuxmusl-arm64@0.33.5': 314 | resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} 315 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 316 | cpu: [arm64] 317 | os: [linux] 318 | 319 | '@img/sharp-linuxmusl-x64@0.33.5': 320 | resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} 321 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 322 | cpu: [x64] 323 | os: [linux] 324 | 325 | '@img/sharp-wasm32@0.33.5': 326 | resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} 327 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 328 | cpu: [wasm32] 329 | 330 | '@img/sharp-win32-ia32@0.33.5': 331 | resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} 332 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 333 | cpu: [ia32] 334 | os: [win32] 335 | 336 | '@img/sharp-win32-x64@0.33.5': 337 | resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} 338 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 339 | cpu: [x64] 340 | os: [win32] 341 | 342 | '@isaacs/cliui@8.0.2': 343 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 344 | engines: {node: '>=12'} 345 | 346 | '@jridgewell/gen-mapping@0.3.8': 347 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 348 | engines: {node: '>=6.0.0'} 349 | 350 | '@jridgewell/resolve-uri@3.1.2': 351 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 352 | engines: {node: '>=6.0.0'} 353 | 354 | '@jridgewell/set-array@1.2.1': 355 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 356 | engines: {node: '>=6.0.0'} 357 | 358 | '@jridgewell/sourcemap-codec@1.5.0': 359 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 360 | 361 | '@jridgewell/trace-mapping@0.3.25': 362 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 363 | 364 | '@jridgewell/trace-mapping@0.3.9': 365 | resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 366 | 367 | '@next/env@15.1.6': 368 | resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==} 369 | 370 | '@next/swc-darwin-arm64@15.1.6': 371 | resolution: {integrity: sha512-u7lg4Mpl9qWpKgy6NzEkz/w0/keEHtOybmIl0ykgItBxEM5mYotS5PmqTpo+Rhg8FiOiWgwr8USxmKQkqLBCrw==} 372 | engines: {node: '>= 10'} 373 | cpu: [arm64] 374 | os: [darwin] 375 | 376 | '@next/swc-darwin-x64@15.1.6': 377 | resolution: {integrity: sha512-x1jGpbHbZoZ69nRuogGL2MYPLqohlhnT9OCU6E6QFewwup+z+M6r8oU47BTeJcWsF2sdBahp5cKiAcDbwwK/lg==} 378 | engines: {node: '>= 10'} 379 | cpu: [x64] 380 | os: [darwin] 381 | 382 | '@next/swc-linux-arm64-gnu@15.1.6': 383 | resolution: {integrity: sha512-jar9sFw0XewXsBzPf9runGzoivajeWJUc/JkfbLTC4it9EhU8v7tCRLH7l5Y1ReTMN6zKJO0kKAGqDk8YSO2bg==} 384 | engines: {node: '>= 10'} 385 | cpu: [arm64] 386 | os: [linux] 387 | 388 | '@next/swc-linux-arm64-musl@15.1.6': 389 | resolution: {integrity: sha512-+n3u//bfsrIaZch4cgOJ3tXCTbSxz0s6brJtU3SzLOvkJlPQMJ+eHVRi6qM2kKKKLuMY+tcau8XD9CJ1OjeSQQ==} 390 | engines: {node: '>= 10'} 391 | cpu: [arm64] 392 | os: [linux] 393 | 394 | '@next/swc-linux-x64-gnu@15.1.6': 395 | resolution: {integrity: sha512-SpuDEXixM3PycniL4iVCLyUyvcl6Lt0mtv3am08sucskpG0tYkW1KlRhTgj4LI5ehyxriVVcfdoxuuP8csi3kQ==} 396 | engines: {node: '>= 10'} 397 | cpu: [x64] 398 | os: [linux] 399 | 400 | '@next/swc-linux-x64-musl@15.1.6': 401 | resolution: {integrity: sha512-L4druWmdFSZIIRhF+G60API5sFB7suTbDRhYWSjiw0RbE+15igQvE2g2+S973pMGvwN3guw7cJUjA/TmbPWTHQ==} 402 | engines: {node: '>= 10'} 403 | cpu: [x64] 404 | os: [linux] 405 | 406 | '@next/swc-win32-arm64-msvc@15.1.6': 407 | resolution: {integrity: sha512-s8w6EeqNmi6gdvM19tqKKWbCyOBvXFbndkGHl+c9YrzsLARRdCHsD9S1fMj8gsXm9v8vhC8s3N8rjuC/XrtkEg==} 408 | engines: {node: '>= 10'} 409 | cpu: [arm64] 410 | os: [win32] 411 | 412 | '@next/swc-win32-x64-msvc@15.1.6': 413 | resolution: {integrity: sha512-6xomMuu54FAFxttYr5PJbEfu96godcxBTRk1OhAvJq0/EnmFU/Ybiax30Snis4vdWZ9LGpf7Roy5fSs7v/5ROQ==} 414 | engines: {node: '>= 10'} 415 | cpu: [x64] 416 | os: [win32] 417 | 418 | '@nodelib/fs.scandir@2.1.5': 419 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 420 | engines: {node: '>= 8'} 421 | 422 | '@nodelib/fs.stat@2.0.5': 423 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 424 | engines: {node: '>= 8'} 425 | 426 | '@nodelib/fs.walk@1.2.8': 427 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 428 | engines: {node: '>= 8'} 429 | 430 | '@opentelemetry/api@1.9.0': 431 | resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} 432 | engines: {node: '>=8.0.0'} 433 | 434 | '@pkgjs/parseargs@0.11.0': 435 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 436 | engines: {node: '>=14'} 437 | 438 | '@radix-ui/primitive@1.1.1': 439 | resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} 440 | 441 | '@radix-ui/react-accordion@1.2.2': 442 | resolution: {integrity: sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g==} 443 | peerDependencies: 444 | '@types/react': '*' 445 | '@types/react-dom': '*' 446 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 447 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 448 | peerDependenciesMeta: 449 | '@types/react': 450 | optional: true 451 | '@types/react-dom': 452 | optional: true 453 | 454 | '@radix-ui/react-collapsible@1.1.2': 455 | resolution: {integrity: sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==} 456 | peerDependencies: 457 | '@types/react': '*' 458 | '@types/react-dom': '*' 459 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 460 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 461 | peerDependenciesMeta: 462 | '@types/react': 463 | optional: true 464 | '@types/react-dom': 465 | optional: true 466 | 467 | '@radix-ui/react-collection@1.1.1': 468 | resolution: {integrity: sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==} 469 | peerDependencies: 470 | '@types/react': '*' 471 | '@types/react-dom': '*' 472 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 473 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 474 | peerDependenciesMeta: 475 | '@types/react': 476 | optional: true 477 | '@types/react-dom': 478 | optional: true 479 | 480 | '@radix-ui/react-compose-refs@1.1.1': 481 | resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} 482 | peerDependencies: 483 | '@types/react': '*' 484 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 485 | peerDependenciesMeta: 486 | '@types/react': 487 | optional: true 488 | 489 | '@radix-ui/react-context@1.1.1': 490 | resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} 491 | peerDependencies: 492 | '@types/react': '*' 493 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 494 | peerDependenciesMeta: 495 | '@types/react': 496 | optional: true 497 | 498 | '@radix-ui/react-direction@1.1.0': 499 | resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} 500 | peerDependencies: 501 | '@types/react': '*' 502 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 503 | peerDependenciesMeta: 504 | '@types/react': 505 | optional: true 506 | 507 | '@radix-ui/react-id@1.1.0': 508 | resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} 509 | peerDependencies: 510 | '@types/react': '*' 511 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 512 | peerDependenciesMeta: 513 | '@types/react': 514 | optional: true 515 | 516 | '@radix-ui/react-presence@1.1.2': 517 | resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} 518 | peerDependencies: 519 | '@types/react': '*' 520 | '@types/react-dom': '*' 521 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 522 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 523 | peerDependenciesMeta: 524 | '@types/react': 525 | optional: true 526 | '@types/react-dom': 527 | optional: true 528 | 529 | '@radix-ui/react-primitive@2.0.1': 530 | resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} 531 | peerDependencies: 532 | '@types/react': '*' 533 | '@types/react-dom': '*' 534 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 535 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 536 | peerDependenciesMeta: 537 | '@types/react': 538 | optional: true 539 | '@types/react-dom': 540 | optional: true 541 | 542 | '@radix-ui/react-slot@1.1.1': 543 | resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} 544 | peerDependencies: 545 | '@types/react': '*' 546 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 547 | peerDependenciesMeta: 548 | '@types/react': 549 | optional: true 550 | 551 | '@radix-ui/react-use-callback-ref@1.1.0': 552 | resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} 553 | peerDependencies: 554 | '@types/react': '*' 555 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 556 | peerDependenciesMeta: 557 | '@types/react': 558 | optional: true 559 | 560 | '@radix-ui/react-use-controllable-state@1.1.0': 561 | resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} 562 | peerDependencies: 563 | '@types/react': '*' 564 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 565 | peerDependenciesMeta: 566 | '@types/react': 567 | optional: true 568 | 569 | '@radix-ui/react-use-layout-effect@1.1.0': 570 | resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} 571 | peerDependencies: 572 | '@types/react': '*' 573 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 574 | peerDependenciesMeta: 575 | '@types/react': 576 | optional: true 577 | 578 | '@swc/counter@0.1.3': 579 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 580 | 581 | '@swc/helpers@0.5.15': 582 | resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} 583 | 584 | '@tsconfig/node10@1.0.11': 585 | resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} 586 | 587 | '@tsconfig/node12@1.0.11': 588 | resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} 589 | 590 | '@tsconfig/node14@1.0.3': 591 | resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} 592 | 593 | '@tsconfig/node16@1.0.4': 594 | resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} 595 | 596 | '@types/diff-match-patch@1.0.36': 597 | resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} 598 | 599 | '@types/node-fetch@2.6.12': 600 | resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} 601 | 602 | '@types/node@18.19.74': 603 | resolution: {integrity: sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==} 604 | 605 | '@types/node@20.17.16': 606 | resolution: {integrity: sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==} 607 | 608 | '@types/react-dom@19.0.3': 609 | resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==} 610 | peerDependencies: 611 | '@types/react': ^19.0.0 612 | 613 | '@types/react@19.0.8': 614 | resolution: {integrity: sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==} 615 | 616 | '@upstash/core-analytics@0.0.10': 617 | resolution: {integrity: sha512-7qJHGxpQgQr9/vmeS1PktEwvNAF7TI4iJDi8Pu2CFZ9YUGHZH4fOP5TfYlZ4aVxfopnELiE4BS4FBjyK7V1/xQ==} 618 | engines: {node: '>=16.0.0'} 619 | 620 | '@upstash/ratelimit@2.0.5': 621 | resolution: {integrity: sha512-1FRv0cs3ZlBjCNOCpCmKYmt9BYGIJf0J0R3pucOPE88R21rL7jNjXG+I+rN/BVOvYJhI9niRAS/JaSNjiSICxA==} 622 | peerDependencies: 623 | '@upstash/redis': ^1.34.3 624 | 625 | '@upstash/redis@1.34.3': 626 | resolution: {integrity: sha512-VT25TyODGy/8ljl7GADnJoMmtmJ1F8d84UXfGonRRF8fWYJz7+2J6GzW+a6ETGtk4OyuRTt7FRSvFG5GvrfSdQ==} 627 | 628 | '@vercel/analytics@1.4.1': 629 | resolution: {integrity: sha512-ekpL4ReX2TH3LnrRZTUKjHHNpNy9S1I7QmS+g/RQXoSUQ8ienzosuX7T9djZ/s8zPhBx1mpHP/Rw5875N+zQIQ==} 630 | peerDependencies: 631 | '@remix-run/react': ^2 632 | '@sveltejs/kit': ^1 || ^2 633 | next: '>= 13' 634 | react: ^18 || ^19 || ^19.0.0-rc 635 | svelte: '>= 4' 636 | vue: ^3 637 | vue-router: ^4 638 | peerDependenciesMeta: 639 | '@remix-run/react': 640 | optional: true 641 | '@sveltejs/kit': 642 | optional: true 643 | next: 644 | optional: true 645 | react: 646 | optional: true 647 | svelte: 648 | optional: true 649 | vue: 650 | optional: true 651 | vue-router: 652 | optional: true 653 | 654 | abort-controller@3.0.0: 655 | resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} 656 | engines: {node: '>=6.5'} 657 | 658 | acorn-walk@8.3.4: 659 | resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} 660 | engines: {node: '>=0.4.0'} 661 | 662 | acorn@8.14.0: 663 | resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} 664 | engines: {node: '>=0.4.0'} 665 | hasBin: true 666 | 667 | agentkeepalive@4.6.0: 668 | resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} 669 | engines: {node: '>= 8.0.0'} 670 | 671 | ai@4.1.9: 672 | resolution: {integrity: sha512-EUc21jyV/2Fv0hEd4toLxQMxjTXBWjKnw16tpto12Vrg/EvkmfVSEvtwXDa+J70iPDmASxL10VKmJk/wnb6bZA==} 673 | engines: {node: '>=18'} 674 | peerDependencies: 675 | react: ^18 || ^19 || ^19.0.0-rc 676 | zod: ^3.0.0 677 | peerDependenciesMeta: 678 | react: 679 | optional: true 680 | zod: 681 | optional: true 682 | 683 | ansi-regex@5.0.1: 684 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 685 | engines: {node: '>=8'} 686 | 687 | ansi-regex@6.1.0: 688 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 689 | engines: {node: '>=12'} 690 | 691 | ansi-styles@4.3.0: 692 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 693 | engines: {node: '>=8'} 694 | 695 | ansi-styles@6.2.1: 696 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 697 | engines: {node: '>=12'} 698 | 699 | any-promise@1.3.0: 700 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 701 | 702 | anymatch@3.1.3: 703 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 704 | engines: {node: '>= 8'} 705 | 706 | arg@4.1.3: 707 | resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} 708 | 709 | arg@5.0.2: 710 | resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} 711 | 712 | asynckit@0.4.0: 713 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 714 | 715 | balanced-match@1.0.2: 716 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 717 | 718 | binary-extensions@2.3.0: 719 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} 720 | engines: {node: '>=8'} 721 | 722 | brace-expansion@2.0.1: 723 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 724 | 725 | braces@3.0.3: 726 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 727 | engines: {node: '>=8'} 728 | 729 | busboy@1.6.0: 730 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 731 | engines: {node: '>=10.16.0'} 732 | 733 | camelcase-css@2.0.1: 734 | resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} 735 | engines: {node: '>= 6'} 736 | 737 | caniuse-lite@1.0.30001695: 738 | resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} 739 | 740 | chalk@5.4.1: 741 | resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} 742 | engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} 743 | 744 | chokidar@3.6.0: 745 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 746 | engines: {node: '>= 8.10.0'} 747 | 748 | class-variance-authority@0.7.1: 749 | resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} 750 | 751 | client-only@0.0.1: 752 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 753 | 754 | clsx@2.1.1: 755 | resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} 756 | engines: {node: '>=6'} 757 | 758 | color-convert@2.0.1: 759 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 760 | engines: {node: '>=7.0.0'} 761 | 762 | color-name@1.1.4: 763 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 764 | 765 | color-string@1.9.1: 766 | resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} 767 | 768 | color@4.2.3: 769 | resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} 770 | engines: {node: '>=12.5.0'} 771 | 772 | combined-stream@1.0.8: 773 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 774 | engines: {node: '>= 0.8'} 775 | 776 | commander@4.1.1: 777 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 778 | engines: {node: '>= 6'} 779 | 780 | create-require@1.1.1: 781 | resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} 782 | 783 | cross-spawn@7.0.6: 784 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 785 | engines: {node: '>= 8'} 786 | 787 | crypto-js@4.2.0: 788 | resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} 789 | 790 | cssesc@3.0.0: 791 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 792 | engines: {node: '>=4'} 793 | hasBin: true 794 | 795 | csstype@3.1.3: 796 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 797 | 798 | delayed-stream@1.0.0: 799 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 800 | engines: {node: '>=0.4.0'} 801 | 802 | dequal@2.0.3: 803 | resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} 804 | engines: {node: '>=6'} 805 | 806 | detect-libc@2.0.3: 807 | resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} 808 | engines: {node: '>=8'} 809 | 810 | didyoumean@1.2.2: 811 | resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} 812 | 813 | diff-match-patch@1.0.5: 814 | resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} 815 | 816 | diff@4.0.2: 817 | resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} 818 | engines: {node: '>=0.3.1'} 819 | 820 | dlv@1.1.3: 821 | resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} 822 | 823 | eastasianwidth@0.2.0: 824 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 825 | 826 | emoji-regex@8.0.0: 827 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 828 | 829 | emoji-regex@9.2.2: 830 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 831 | 832 | event-target-shim@5.0.1: 833 | resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} 834 | engines: {node: '>=6'} 835 | 836 | eventsource-parser@3.0.0: 837 | resolution: {integrity: sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==} 838 | engines: {node: '>=18.0.0'} 839 | 840 | fast-glob@3.3.3: 841 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 842 | engines: {node: '>=8.6.0'} 843 | 844 | fastq@1.18.0: 845 | resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} 846 | 847 | fill-range@7.1.1: 848 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 849 | engines: {node: '>=8'} 850 | 851 | foreground-child@3.3.0: 852 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} 853 | engines: {node: '>=14'} 854 | 855 | form-data-encoder@1.7.2: 856 | resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} 857 | 858 | form-data@4.0.1: 859 | resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} 860 | engines: {node: '>= 6'} 861 | 862 | formdata-node@4.4.1: 863 | resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} 864 | engines: {node: '>= 12.20'} 865 | 866 | framer-motion@12.0.6: 867 | resolution: {integrity: sha512-LmrXbXF6Vv5WCNmb+O/zn891VPZrH7XbsZgRLBROw6kFiP+iTK49gxTv2Ur3F0Tbw6+sy9BVtSqnWfMUpH+6nA==} 868 | peerDependencies: 869 | '@emotion/is-prop-valid': '*' 870 | react: ^18.0.0 || ^19.0.0 871 | react-dom: ^18.0.0 || ^19.0.0 872 | peerDependenciesMeta: 873 | '@emotion/is-prop-valid': 874 | optional: true 875 | react: 876 | optional: true 877 | react-dom: 878 | optional: true 879 | 880 | fsevents@2.3.3: 881 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 882 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 883 | os: [darwin] 884 | 885 | function-bind@1.1.2: 886 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 887 | 888 | glob-parent@5.1.2: 889 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 890 | engines: {node: '>= 6'} 891 | 892 | glob-parent@6.0.2: 893 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 894 | engines: {node: '>=10.13.0'} 895 | 896 | glob@10.4.5: 897 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 898 | hasBin: true 899 | 900 | hasown@2.0.2: 901 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 902 | engines: {node: '>= 0.4'} 903 | 904 | humanize-ms@1.2.1: 905 | resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} 906 | 907 | is-arrayish@0.3.2: 908 | resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} 909 | 910 | is-binary-path@2.1.0: 911 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 912 | engines: {node: '>=8'} 913 | 914 | is-core-module@2.16.1: 915 | resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} 916 | engines: {node: '>= 0.4'} 917 | 918 | is-extglob@2.1.1: 919 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 920 | engines: {node: '>=0.10.0'} 921 | 922 | is-fullwidth-code-point@3.0.0: 923 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 924 | engines: {node: '>=8'} 925 | 926 | is-glob@4.0.3: 927 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 928 | engines: {node: '>=0.10.0'} 929 | 930 | is-number@7.0.0: 931 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 932 | engines: {node: '>=0.12.0'} 933 | 934 | isexe@2.0.0: 935 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 936 | 937 | jackspeak@3.4.3: 938 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 939 | 940 | jiti@1.21.7: 941 | resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} 942 | hasBin: true 943 | 944 | json-schema@0.4.0: 945 | resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} 946 | 947 | jsondiffpatch@0.6.0: 948 | resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} 949 | engines: {node: ^18.0.0 || >=20.0.0} 950 | hasBin: true 951 | 952 | lilconfig@3.1.3: 953 | resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} 954 | engines: {node: '>=14'} 955 | 956 | lines-and-columns@1.2.4: 957 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 958 | 959 | lru-cache@10.4.3: 960 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 961 | 962 | lucide-react@0.474.0: 963 | resolution: {integrity: sha512-CmghgHkh0OJNmxGKWc0qfPJCYHASPMVSyGY8fj3xgk4v84ItqDg64JNKFZn5hC6E0vHi6gxnbCgwhyVB09wQtA==} 964 | peerDependencies: 965 | react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 966 | 967 | make-error@1.3.6: 968 | resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} 969 | 970 | merge2@1.4.1: 971 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 972 | engines: {node: '>= 8'} 973 | 974 | micromatch@4.0.8: 975 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 976 | engines: {node: '>=8.6'} 977 | 978 | mime-db@1.52.0: 979 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 980 | engines: {node: '>= 0.6'} 981 | 982 | mime-types@2.1.35: 983 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 984 | engines: {node: '>= 0.6'} 985 | 986 | minimatch@9.0.5: 987 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 988 | engines: {node: '>=16 || 14 >=14.17'} 989 | 990 | minipass@7.1.2: 991 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 992 | engines: {node: '>=16 || 14 >=14.17'} 993 | 994 | motion-dom@12.0.0: 995 | resolution: {integrity: sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg==} 996 | 997 | motion-utils@12.0.0: 998 | resolution: {integrity: sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==} 999 | 1000 | motion@12.0.6: 1001 | resolution: {integrity: sha512-AzCEO0+//mPlcGiL9JaVwjddHY1cbbnvz5upHL0toqQwsPCs+hiKJ0XG5jfG0XwDtBbiSXdEqW/UTmGLwkVQ6A==} 1002 | peerDependencies: 1003 | '@emotion/is-prop-valid': '*' 1004 | react: ^18.0.0 || ^19.0.0 1005 | react-dom: ^18.0.0 || ^19.0.0 1006 | peerDependenciesMeta: 1007 | '@emotion/is-prop-valid': 1008 | optional: true 1009 | react: 1010 | optional: true 1011 | react-dom: 1012 | optional: true 1013 | 1014 | ms@2.1.3: 1015 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1016 | 1017 | mz@2.7.0: 1018 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 1019 | 1020 | nanoid@3.3.8: 1021 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} 1022 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1023 | hasBin: true 1024 | 1025 | next@15.1.6: 1026 | resolution: {integrity: sha512-Hch4wzbaX0vKQtalpXvUiw5sYivBy4cm5rzUKrBnUB/y436LGrvOUqYvlSeNVCWFO/770gDlltR9gqZH62ct4Q==} 1027 | engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} 1028 | hasBin: true 1029 | peerDependencies: 1030 | '@opentelemetry/api': ^1.1.0 1031 | '@playwright/test': ^1.41.2 1032 | babel-plugin-react-compiler: '*' 1033 | react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 1034 | react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 1035 | sass: ^1.3.0 1036 | peerDependenciesMeta: 1037 | '@opentelemetry/api': 1038 | optional: true 1039 | '@playwright/test': 1040 | optional: true 1041 | babel-plugin-react-compiler: 1042 | optional: true 1043 | sass: 1044 | optional: true 1045 | 1046 | node-domexception@1.0.0: 1047 | resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} 1048 | engines: {node: '>=10.5.0'} 1049 | 1050 | node-fetch@2.7.0: 1051 | resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} 1052 | engines: {node: 4.x || >=6.0.0} 1053 | peerDependencies: 1054 | encoding: ^0.1.0 1055 | peerDependenciesMeta: 1056 | encoding: 1057 | optional: true 1058 | 1059 | normalize-path@3.0.0: 1060 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 1061 | engines: {node: '>=0.10.0'} 1062 | 1063 | object-assign@4.1.1: 1064 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 1065 | engines: {node: '>=0.10.0'} 1066 | 1067 | object-hash@3.0.0: 1068 | resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} 1069 | engines: {node: '>= 6'} 1070 | 1071 | openai@4.80.1: 1072 | resolution: {integrity: sha512-+6+bbXFwbIE88foZsBEt36bPkgZPdyFN82clAXG61gnHb2gXdZApDyRrcAHqEtpYICywpqaNo57kOm9dtnb7Cw==} 1073 | hasBin: true 1074 | peerDependencies: 1075 | ws: ^8.18.0 1076 | zod: ^3.23.8 1077 | peerDependenciesMeta: 1078 | ws: 1079 | optional: true 1080 | zod: 1081 | optional: true 1082 | 1083 | package-json-from-dist@1.0.1: 1084 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 1085 | 1086 | path-key@3.1.1: 1087 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1088 | engines: {node: '>=8'} 1089 | 1090 | path-parse@1.0.7: 1091 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1092 | 1093 | path-scurry@1.11.1: 1094 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 1095 | engines: {node: '>=16 || 14 >=14.18'} 1096 | 1097 | picocolors@1.1.1: 1098 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 1099 | 1100 | picomatch@2.3.1: 1101 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1102 | engines: {node: '>=8.6'} 1103 | 1104 | pify@2.3.0: 1105 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 1106 | engines: {node: '>=0.10.0'} 1107 | 1108 | pirates@4.0.6: 1109 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} 1110 | engines: {node: '>= 6'} 1111 | 1112 | playwright-core@1.50.0: 1113 | resolution: {integrity: sha512-CXkSSlr4JaZs2tZHI40DsZUN/NIwgaUPsyLuOAaIZp2CyF2sN5MM5NJsyB188lFSSozFxQ5fPT4qM+f0tH/6wQ==} 1114 | engines: {node: '>=18'} 1115 | hasBin: true 1116 | 1117 | postcss-import@15.1.0: 1118 | resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} 1119 | engines: {node: '>=14.0.0'} 1120 | peerDependencies: 1121 | postcss: ^8.0.0 1122 | 1123 | postcss-js@4.0.1: 1124 | resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} 1125 | engines: {node: ^12 || ^14 || >= 16} 1126 | peerDependencies: 1127 | postcss: ^8.4.21 1128 | 1129 | postcss-load-config@4.0.2: 1130 | resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} 1131 | engines: {node: '>= 14'} 1132 | peerDependencies: 1133 | postcss: '>=8.0.9' 1134 | ts-node: '>=9.0.0' 1135 | peerDependenciesMeta: 1136 | postcss: 1137 | optional: true 1138 | ts-node: 1139 | optional: true 1140 | 1141 | postcss-nested@6.2.0: 1142 | resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} 1143 | engines: {node: '>=12.0'} 1144 | peerDependencies: 1145 | postcss: ^8.2.14 1146 | 1147 | postcss-selector-parser@6.1.2: 1148 | resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} 1149 | engines: {node: '>=4'} 1150 | 1151 | postcss-value-parser@4.2.0: 1152 | resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} 1153 | 1154 | postcss@8.4.31: 1155 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 1156 | engines: {node: ^10 || ^12 || >=14} 1157 | 1158 | postcss@8.5.1: 1159 | resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} 1160 | engines: {node: ^10 || ^12 || >=14} 1161 | 1162 | queue-microtask@1.2.3: 1163 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1164 | 1165 | react-dom@19.0.0: 1166 | resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} 1167 | peerDependencies: 1168 | react: ^19.0.0 1169 | 1170 | react-intersection-observer@9.15.1: 1171 | resolution: {integrity: sha512-vGrqYEVWXfH+AGu241uzfUpNK4HAdhCkSAyFdkMb9VWWXs6mxzBLpWCxEy9YcnDNY2g9eO6z7qUtTBdA9hc8pA==} 1172 | peerDependencies: 1173 | react: ^17.0.0 || ^18.0.0 || ^19.0.0 1174 | react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 1175 | peerDependenciesMeta: 1176 | react-dom: 1177 | optional: true 1178 | 1179 | react-resizable-panels@2.1.7: 1180 | resolution: {integrity: sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==} 1181 | peerDependencies: 1182 | react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 1183 | react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 1184 | 1185 | react-textarea-autosize@8.5.7: 1186 | resolution: {integrity: sha512-2MqJ3p0Jh69yt9ktFIaZmORHXw4c4bxSIhCeWiFwmJ9EYKgLmuNII3e9c9b2UO+ijl4StnpZdqpxNIhTdHvqtQ==} 1187 | engines: {node: '>=10'} 1188 | peerDependencies: 1189 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 1190 | 1191 | react@19.0.0: 1192 | resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} 1193 | engines: {node: '>=0.10.0'} 1194 | 1195 | read-cache@1.0.0: 1196 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} 1197 | 1198 | readdirp@3.6.0: 1199 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 1200 | engines: {node: '>=8.10.0'} 1201 | 1202 | regenerator-runtime@0.14.1: 1203 | resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} 1204 | 1205 | resolve@1.22.10: 1206 | resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} 1207 | engines: {node: '>= 0.4'} 1208 | hasBin: true 1209 | 1210 | reusify@1.0.4: 1211 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 1212 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1213 | 1214 | run-parallel@1.2.0: 1215 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1216 | 1217 | scheduler@0.25.0: 1218 | resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} 1219 | 1220 | secure-json-parse@2.7.0: 1221 | resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} 1222 | 1223 | semver@7.6.3: 1224 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} 1225 | engines: {node: '>=10'} 1226 | hasBin: true 1227 | 1228 | sharp@0.33.5: 1229 | resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} 1230 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 1231 | 1232 | shebang-command@2.0.0: 1233 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1234 | engines: {node: '>=8'} 1235 | 1236 | shebang-regex@3.0.0: 1237 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1238 | engines: {node: '>=8'} 1239 | 1240 | signal-exit@4.1.0: 1241 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 1242 | engines: {node: '>=14'} 1243 | 1244 | simple-swizzle@0.2.2: 1245 | resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} 1246 | 1247 | source-map-js@1.2.1: 1248 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1249 | engines: {node: '>=0.10.0'} 1250 | 1251 | streamsearch@1.1.0: 1252 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 1253 | engines: {node: '>=10.0.0'} 1254 | 1255 | string-width@4.2.3: 1256 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 1257 | engines: {node: '>=8'} 1258 | 1259 | string-width@5.1.2: 1260 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 1261 | engines: {node: '>=12'} 1262 | 1263 | strip-ansi@6.0.1: 1264 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1265 | engines: {node: '>=8'} 1266 | 1267 | strip-ansi@7.1.0: 1268 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 1269 | engines: {node: '>=12'} 1270 | 1271 | styled-jsx@5.1.6: 1272 | resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} 1273 | engines: {node: '>= 12.0.0'} 1274 | peerDependencies: 1275 | '@babel/core': '*' 1276 | babel-plugin-macros: '*' 1277 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' 1278 | peerDependenciesMeta: 1279 | '@babel/core': 1280 | optional: true 1281 | babel-plugin-macros: 1282 | optional: true 1283 | 1284 | sucrase@3.35.0: 1285 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 1286 | engines: {node: '>=16 || 14 >=14.17'} 1287 | hasBin: true 1288 | 1289 | supports-preserve-symlinks-flag@1.0.0: 1290 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1291 | engines: {node: '>= 0.4'} 1292 | 1293 | swr@2.3.0: 1294 | resolution: {integrity: sha512-NyZ76wA4yElZWBHzSgEJc28a0u6QZvhb6w0azeL2k7+Q1gAzVK+IqQYXhVOC/mzi+HZIozrZvBVeSeOZNR2bqA==} 1295 | peerDependencies: 1296 | react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 1297 | 1298 | tailwind-merge@2.6.0: 1299 | resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} 1300 | 1301 | tailwindcss-animate@1.0.7: 1302 | resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} 1303 | peerDependencies: 1304 | tailwindcss: '>=3.0.0 || insiders' 1305 | 1306 | tailwindcss@3.4.17: 1307 | resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} 1308 | engines: {node: '>=14.0.0'} 1309 | hasBin: true 1310 | 1311 | thenify-all@1.6.0: 1312 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 1313 | engines: {node: '>=0.8'} 1314 | 1315 | thenify@3.3.1: 1316 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 1317 | 1318 | throttleit@2.1.0: 1319 | resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} 1320 | engines: {node: '>=18'} 1321 | 1322 | to-regex-range@5.0.1: 1323 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1324 | engines: {node: '>=8.0'} 1325 | 1326 | tr46@0.0.3: 1327 | resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} 1328 | 1329 | ts-interface-checker@0.1.13: 1330 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 1331 | 1332 | ts-node@10.9.1: 1333 | resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} 1334 | hasBin: true 1335 | peerDependencies: 1336 | '@swc/core': '>=1.2.50' 1337 | '@swc/wasm': '>=1.2.50' 1338 | '@types/node': '*' 1339 | typescript: '>=2.7' 1340 | peerDependenciesMeta: 1341 | '@swc/core': 1342 | optional: true 1343 | '@swc/wasm': 1344 | optional: true 1345 | 1346 | tslib@2.8.1: 1347 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1348 | 1349 | typescript@5.7.3: 1350 | resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} 1351 | engines: {node: '>=14.17'} 1352 | hasBin: true 1353 | 1354 | undici-types@5.26.5: 1355 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} 1356 | 1357 | undici-types@6.19.8: 1358 | resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 1359 | 1360 | use-composed-ref@1.4.0: 1361 | resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} 1362 | peerDependencies: 1363 | '@types/react': '*' 1364 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 1365 | peerDependenciesMeta: 1366 | '@types/react': 1367 | optional: true 1368 | 1369 | use-isomorphic-layout-effect@1.2.0: 1370 | resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} 1371 | peerDependencies: 1372 | '@types/react': '*' 1373 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 1374 | peerDependenciesMeta: 1375 | '@types/react': 1376 | optional: true 1377 | 1378 | use-latest@1.3.0: 1379 | resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} 1380 | peerDependencies: 1381 | '@types/react': '*' 1382 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 1383 | peerDependenciesMeta: 1384 | '@types/react': 1385 | optional: true 1386 | 1387 | use-sync-external-store@1.4.0: 1388 | resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} 1389 | peerDependencies: 1390 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 1391 | 1392 | util-deprecate@1.0.2: 1393 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 1394 | 1395 | v8-compile-cache-lib@3.0.1: 1396 | resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} 1397 | 1398 | web-streams-polyfill@4.0.0-beta.3: 1399 | resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} 1400 | engines: {node: '>= 14'} 1401 | 1402 | webidl-conversions@3.0.1: 1403 | resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} 1404 | 1405 | whatwg-url@5.0.0: 1406 | resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} 1407 | 1408 | which@2.0.2: 1409 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1410 | engines: {node: '>= 8'} 1411 | hasBin: true 1412 | 1413 | wrap-ansi@7.0.0: 1414 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1415 | engines: {node: '>=10'} 1416 | 1417 | wrap-ansi@8.1.0: 1418 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1419 | engines: {node: '>=12'} 1420 | 1421 | yaml@2.7.0: 1422 | resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} 1423 | engines: {node: '>= 14'} 1424 | hasBin: true 1425 | 1426 | yn@3.1.1: 1427 | resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} 1428 | engines: {node: '>=6'} 1429 | 1430 | zod-to-json-schema@3.24.1: 1431 | resolution: {integrity: sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==} 1432 | peerDependencies: 1433 | zod: ^3.24.1 1434 | 1435 | zod@3.24.1: 1436 | resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} 1437 | 1438 | snapshots: 1439 | 1440 | '@ai-sdk/anthropic@1.1.5(zod@3.24.1)': 1441 | dependencies: 1442 | '@ai-sdk/provider': 1.0.6 1443 | '@ai-sdk/provider-utils': 2.1.5(zod@3.24.1) 1444 | zod: 3.24.1 1445 | 1446 | '@ai-sdk/google@1.1.5(zod@3.24.1)': 1447 | dependencies: 1448 | '@ai-sdk/provider': 1.0.6 1449 | '@ai-sdk/provider-utils': 2.1.5(zod@3.24.1) 1450 | zod: 3.24.1 1451 | 1452 | '@ai-sdk/openai@1.1.5(zod@3.24.1)': 1453 | dependencies: 1454 | '@ai-sdk/provider': 1.0.6 1455 | '@ai-sdk/provider-utils': 2.1.5(zod@3.24.1) 1456 | zod: 3.24.1 1457 | 1458 | '@ai-sdk/provider-utils@2.1.5(zod@3.24.1)': 1459 | dependencies: 1460 | '@ai-sdk/provider': 1.0.6 1461 | eventsource-parser: 3.0.0 1462 | nanoid: 3.3.8 1463 | secure-json-parse: 2.7.0 1464 | optionalDependencies: 1465 | zod: 3.24.1 1466 | 1467 | '@ai-sdk/provider@1.0.6': 1468 | dependencies: 1469 | json-schema: 0.4.0 1470 | 1471 | '@ai-sdk/react@1.1.6(react@19.0.0)(zod@3.24.1)': 1472 | dependencies: 1473 | '@ai-sdk/provider-utils': 2.1.5(zod@3.24.1) 1474 | '@ai-sdk/ui-utils': 1.1.6(zod@3.24.1) 1475 | swr: 2.3.0(react@19.0.0) 1476 | throttleit: 2.1.0 1477 | optionalDependencies: 1478 | react: 19.0.0 1479 | zod: 3.24.1 1480 | 1481 | '@ai-sdk/ui-utils@1.1.6(zod@3.24.1)': 1482 | dependencies: 1483 | '@ai-sdk/provider': 1.0.6 1484 | '@ai-sdk/provider-utils': 2.1.5(zod@3.24.1) 1485 | zod-to-json-schema: 3.24.1(zod@3.24.1) 1486 | optionalDependencies: 1487 | zod: 3.24.1 1488 | 1489 | '@alloc/quick-lru@5.2.0': {} 1490 | 1491 | '@babel/runtime@7.26.7': 1492 | dependencies: 1493 | regenerator-runtime: 0.14.1 1494 | 1495 | '@biomejs/biome@1.9.4': 1496 | optionalDependencies: 1497 | '@biomejs/cli-darwin-arm64': 1.9.4 1498 | '@biomejs/cli-darwin-x64': 1.9.4 1499 | '@biomejs/cli-linux-arm64': 1.9.4 1500 | '@biomejs/cli-linux-arm64-musl': 1.9.4 1501 | '@biomejs/cli-linux-x64': 1.9.4 1502 | '@biomejs/cli-linux-x64-musl': 1.9.4 1503 | '@biomejs/cli-win32-arm64': 1.9.4 1504 | '@biomejs/cli-win32-x64': 1.9.4 1505 | 1506 | '@biomejs/cli-darwin-arm64@1.9.4': 1507 | optional: true 1508 | 1509 | '@biomejs/cli-darwin-x64@1.9.4': 1510 | optional: true 1511 | 1512 | '@biomejs/cli-linux-arm64-musl@1.9.4': 1513 | optional: true 1514 | 1515 | '@biomejs/cli-linux-arm64@1.9.4': 1516 | optional: true 1517 | 1518 | '@biomejs/cli-linux-x64-musl@1.9.4': 1519 | optional: true 1520 | 1521 | '@biomejs/cli-linux-x64@1.9.4': 1522 | optional: true 1523 | 1524 | '@biomejs/cli-win32-arm64@1.9.4': 1525 | optional: true 1526 | 1527 | '@biomejs/cli-win32-x64@1.9.4': 1528 | optional: true 1529 | 1530 | '@browserbasehq/sdk@2.0.0': 1531 | dependencies: 1532 | '@types/node': 18.19.74 1533 | '@types/node-fetch': 2.6.12 1534 | abort-controller: 3.0.0 1535 | agentkeepalive: 4.6.0 1536 | form-data-encoder: 1.7.2 1537 | formdata-node: 4.4.1 1538 | node-fetch: 2.7.0 1539 | transitivePeerDependencies: 1540 | - encoding 1541 | 1542 | '@cspotcode/source-map-support@0.8.1': 1543 | dependencies: 1544 | '@jridgewell/trace-mapping': 0.3.9 1545 | optional: true 1546 | 1547 | '@emnapi/runtime@1.3.1': 1548 | dependencies: 1549 | tslib: 2.8.1 1550 | optional: true 1551 | 1552 | '@img/sharp-darwin-arm64@0.33.5': 1553 | optionalDependencies: 1554 | '@img/sharp-libvips-darwin-arm64': 1.0.4 1555 | optional: true 1556 | 1557 | '@img/sharp-darwin-x64@0.33.5': 1558 | optionalDependencies: 1559 | '@img/sharp-libvips-darwin-x64': 1.0.4 1560 | optional: true 1561 | 1562 | '@img/sharp-libvips-darwin-arm64@1.0.4': 1563 | optional: true 1564 | 1565 | '@img/sharp-libvips-darwin-x64@1.0.4': 1566 | optional: true 1567 | 1568 | '@img/sharp-libvips-linux-arm64@1.0.4': 1569 | optional: true 1570 | 1571 | '@img/sharp-libvips-linux-arm@1.0.5': 1572 | optional: true 1573 | 1574 | '@img/sharp-libvips-linux-s390x@1.0.4': 1575 | optional: true 1576 | 1577 | '@img/sharp-libvips-linux-x64@1.0.4': 1578 | optional: true 1579 | 1580 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 1581 | optional: true 1582 | 1583 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 1584 | optional: true 1585 | 1586 | '@img/sharp-linux-arm64@0.33.5': 1587 | optionalDependencies: 1588 | '@img/sharp-libvips-linux-arm64': 1.0.4 1589 | optional: true 1590 | 1591 | '@img/sharp-linux-arm@0.33.5': 1592 | optionalDependencies: 1593 | '@img/sharp-libvips-linux-arm': 1.0.5 1594 | optional: true 1595 | 1596 | '@img/sharp-linux-s390x@0.33.5': 1597 | optionalDependencies: 1598 | '@img/sharp-libvips-linux-s390x': 1.0.4 1599 | optional: true 1600 | 1601 | '@img/sharp-linux-x64@0.33.5': 1602 | optionalDependencies: 1603 | '@img/sharp-libvips-linux-x64': 1.0.4 1604 | optional: true 1605 | 1606 | '@img/sharp-linuxmusl-arm64@0.33.5': 1607 | optionalDependencies: 1608 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 1609 | optional: true 1610 | 1611 | '@img/sharp-linuxmusl-x64@0.33.5': 1612 | optionalDependencies: 1613 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 1614 | optional: true 1615 | 1616 | '@img/sharp-wasm32@0.33.5': 1617 | dependencies: 1618 | '@emnapi/runtime': 1.3.1 1619 | optional: true 1620 | 1621 | '@img/sharp-win32-ia32@0.33.5': 1622 | optional: true 1623 | 1624 | '@img/sharp-win32-x64@0.33.5': 1625 | optional: true 1626 | 1627 | '@isaacs/cliui@8.0.2': 1628 | dependencies: 1629 | string-width: 5.1.2 1630 | string-width-cjs: string-width@4.2.3 1631 | strip-ansi: 7.1.0 1632 | strip-ansi-cjs: strip-ansi@6.0.1 1633 | wrap-ansi: 8.1.0 1634 | wrap-ansi-cjs: wrap-ansi@7.0.0 1635 | 1636 | '@jridgewell/gen-mapping@0.3.8': 1637 | dependencies: 1638 | '@jridgewell/set-array': 1.2.1 1639 | '@jridgewell/sourcemap-codec': 1.5.0 1640 | '@jridgewell/trace-mapping': 0.3.25 1641 | 1642 | '@jridgewell/resolve-uri@3.1.2': {} 1643 | 1644 | '@jridgewell/set-array@1.2.1': {} 1645 | 1646 | '@jridgewell/sourcemap-codec@1.5.0': {} 1647 | 1648 | '@jridgewell/trace-mapping@0.3.25': 1649 | dependencies: 1650 | '@jridgewell/resolve-uri': 3.1.2 1651 | '@jridgewell/sourcemap-codec': 1.5.0 1652 | 1653 | '@jridgewell/trace-mapping@0.3.9': 1654 | dependencies: 1655 | '@jridgewell/resolve-uri': 3.1.2 1656 | '@jridgewell/sourcemap-codec': 1.5.0 1657 | optional: true 1658 | 1659 | '@next/env@15.1.6': {} 1660 | 1661 | '@next/swc-darwin-arm64@15.1.6': 1662 | optional: true 1663 | 1664 | '@next/swc-darwin-x64@15.1.6': 1665 | optional: true 1666 | 1667 | '@next/swc-linux-arm64-gnu@15.1.6': 1668 | optional: true 1669 | 1670 | '@next/swc-linux-arm64-musl@15.1.6': 1671 | optional: true 1672 | 1673 | '@next/swc-linux-x64-gnu@15.1.6': 1674 | optional: true 1675 | 1676 | '@next/swc-linux-x64-musl@15.1.6': 1677 | optional: true 1678 | 1679 | '@next/swc-win32-arm64-msvc@15.1.6': 1680 | optional: true 1681 | 1682 | '@next/swc-win32-x64-msvc@15.1.6': 1683 | optional: true 1684 | 1685 | '@nodelib/fs.scandir@2.1.5': 1686 | dependencies: 1687 | '@nodelib/fs.stat': 2.0.5 1688 | run-parallel: 1.2.0 1689 | 1690 | '@nodelib/fs.stat@2.0.5': {} 1691 | 1692 | '@nodelib/fs.walk@1.2.8': 1693 | dependencies: 1694 | '@nodelib/fs.scandir': 2.1.5 1695 | fastq: 1.18.0 1696 | 1697 | '@opentelemetry/api@1.9.0': {} 1698 | 1699 | '@pkgjs/parseargs@0.11.0': 1700 | optional: true 1701 | 1702 | '@radix-ui/primitive@1.1.1': {} 1703 | 1704 | '@radix-ui/react-accordion@1.2.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 1705 | dependencies: 1706 | '@radix-ui/primitive': 1.1.1 1707 | '@radix-ui/react-collapsible': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 1708 | '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 1709 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1710 | '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1711 | '@radix-ui/react-direction': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1712 | '@radix-ui/react-id': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1713 | '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 1714 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1715 | react: 19.0.0 1716 | react-dom: 19.0.0(react@19.0.0) 1717 | optionalDependencies: 1718 | '@types/react': 19.0.8 1719 | '@types/react-dom': 19.0.3(@types/react@19.0.8) 1720 | 1721 | '@radix-ui/react-collapsible@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 1722 | dependencies: 1723 | '@radix-ui/primitive': 1.1.1 1724 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1725 | '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1726 | '@radix-ui/react-id': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1727 | '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 1728 | '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 1729 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1730 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1731 | react: 19.0.0 1732 | react-dom: 19.0.0(react@19.0.0) 1733 | optionalDependencies: 1734 | '@types/react': 19.0.8 1735 | '@types/react-dom': 19.0.3(@types/react@19.0.8) 1736 | 1737 | '@radix-ui/react-collection@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 1738 | dependencies: 1739 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1740 | '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1741 | '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 1742 | '@radix-ui/react-slot': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1743 | react: 19.0.0 1744 | react-dom: 19.0.0(react@19.0.0) 1745 | optionalDependencies: 1746 | '@types/react': 19.0.8 1747 | '@types/react-dom': 19.0.3(@types/react@19.0.8) 1748 | 1749 | '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.8)(react@19.0.0)': 1750 | dependencies: 1751 | react: 19.0.0 1752 | optionalDependencies: 1753 | '@types/react': 19.0.8 1754 | 1755 | '@radix-ui/react-context@1.1.1(@types/react@19.0.8)(react@19.0.0)': 1756 | dependencies: 1757 | react: 19.0.0 1758 | optionalDependencies: 1759 | '@types/react': 19.0.8 1760 | 1761 | '@radix-ui/react-direction@1.1.0(@types/react@19.0.8)(react@19.0.0)': 1762 | dependencies: 1763 | react: 19.0.0 1764 | optionalDependencies: 1765 | '@types/react': 19.0.8 1766 | 1767 | '@radix-ui/react-id@1.1.0(@types/react@19.0.8)(react@19.0.0)': 1768 | dependencies: 1769 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1770 | react: 19.0.0 1771 | optionalDependencies: 1772 | '@types/react': 19.0.8 1773 | 1774 | '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 1775 | dependencies: 1776 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1777 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1778 | react: 19.0.0 1779 | react-dom: 19.0.0(react@19.0.0) 1780 | optionalDependencies: 1781 | '@types/react': 19.0.8 1782 | '@types/react-dom': 19.0.3(@types/react@19.0.8) 1783 | 1784 | '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 1785 | dependencies: 1786 | '@radix-ui/react-slot': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1787 | react: 19.0.0 1788 | react-dom: 19.0.0(react@19.0.0) 1789 | optionalDependencies: 1790 | '@types/react': 19.0.8 1791 | '@types/react-dom': 19.0.3(@types/react@19.0.8) 1792 | 1793 | '@radix-ui/react-slot@1.1.1(@types/react@19.0.8)(react@19.0.0)': 1794 | dependencies: 1795 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) 1796 | react: 19.0.0 1797 | optionalDependencies: 1798 | '@types/react': 19.0.8 1799 | 1800 | '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.8)(react@19.0.0)': 1801 | dependencies: 1802 | react: 19.0.0 1803 | optionalDependencies: 1804 | '@types/react': 19.0.8 1805 | 1806 | '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.8)(react@19.0.0)': 1807 | dependencies: 1808 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) 1809 | react: 19.0.0 1810 | optionalDependencies: 1811 | '@types/react': 19.0.8 1812 | 1813 | '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.8)(react@19.0.0)': 1814 | dependencies: 1815 | react: 19.0.0 1816 | optionalDependencies: 1817 | '@types/react': 19.0.8 1818 | 1819 | '@swc/counter@0.1.3': {} 1820 | 1821 | '@swc/helpers@0.5.15': 1822 | dependencies: 1823 | tslib: 2.8.1 1824 | 1825 | '@tsconfig/node10@1.0.11': 1826 | optional: true 1827 | 1828 | '@tsconfig/node12@1.0.11': 1829 | optional: true 1830 | 1831 | '@tsconfig/node14@1.0.3': 1832 | optional: true 1833 | 1834 | '@tsconfig/node16@1.0.4': 1835 | optional: true 1836 | 1837 | '@types/diff-match-patch@1.0.36': {} 1838 | 1839 | '@types/node-fetch@2.6.12': 1840 | dependencies: 1841 | '@types/node': 20.17.16 1842 | form-data: 4.0.1 1843 | 1844 | '@types/node@18.19.74': 1845 | dependencies: 1846 | undici-types: 5.26.5 1847 | 1848 | '@types/node@20.17.16': 1849 | dependencies: 1850 | undici-types: 6.19.8 1851 | 1852 | '@types/react-dom@19.0.3(@types/react@19.0.8)': 1853 | dependencies: 1854 | '@types/react': 19.0.8 1855 | 1856 | '@types/react@19.0.8': 1857 | dependencies: 1858 | csstype: 3.1.3 1859 | 1860 | '@upstash/core-analytics@0.0.10': 1861 | dependencies: 1862 | '@upstash/redis': 1.34.3 1863 | 1864 | '@upstash/ratelimit@2.0.5(@upstash/redis@1.34.3)': 1865 | dependencies: 1866 | '@upstash/core-analytics': 0.0.10 1867 | '@upstash/redis': 1.34.3 1868 | 1869 | '@upstash/redis@1.34.3': 1870 | dependencies: 1871 | crypto-js: 4.2.0 1872 | 1873 | '@vercel/analytics@1.4.1(next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)': 1874 | optionalDependencies: 1875 | next: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 1876 | react: 19.0.0 1877 | 1878 | abort-controller@3.0.0: 1879 | dependencies: 1880 | event-target-shim: 5.0.1 1881 | 1882 | acorn-walk@8.3.4: 1883 | dependencies: 1884 | acorn: 8.14.0 1885 | optional: true 1886 | 1887 | acorn@8.14.0: 1888 | optional: true 1889 | 1890 | agentkeepalive@4.6.0: 1891 | dependencies: 1892 | humanize-ms: 1.2.1 1893 | 1894 | ai@4.1.9(react@19.0.0)(zod@3.24.1): 1895 | dependencies: 1896 | '@ai-sdk/provider': 1.0.6 1897 | '@ai-sdk/provider-utils': 2.1.5(zod@3.24.1) 1898 | '@ai-sdk/react': 1.1.6(react@19.0.0)(zod@3.24.1) 1899 | '@ai-sdk/ui-utils': 1.1.6(zod@3.24.1) 1900 | '@opentelemetry/api': 1.9.0 1901 | jsondiffpatch: 0.6.0 1902 | optionalDependencies: 1903 | react: 19.0.0 1904 | zod: 3.24.1 1905 | 1906 | ansi-regex@5.0.1: {} 1907 | 1908 | ansi-regex@6.1.0: {} 1909 | 1910 | ansi-styles@4.3.0: 1911 | dependencies: 1912 | color-convert: 2.0.1 1913 | 1914 | ansi-styles@6.2.1: {} 1915 | 1916 | any-promise@1.3.0: {} 1917 | 1918 | anymatch@3.1.3: 1919 | dependencies: 1920 | normalize-path: 3.0.0 1921 | picomatch: 2.3.1 1922 | 1923 | arg@4.1.3: 1924 | optional: true 1925 | 1926 | arg@5.0.2: {} 1927 | 1928 | asynckit@0.4.0: {} 1929 | 1930 | balanced-match@1.0.2: {} 1931 | 1932 | binary-extensions@2.3.0: {} 1933 | 1934 | brace-expansion@2.0.1: 1935 | dependencies: 1936 | balanced-match: 1.0.2 1937 | 1938 | braces@3.0.3: 1939 | dependencies: 1940 | fill-range: 7.1.1 1941 | 1942 | busboy@1.6.0: 1943 | dependencies: 1944 | streamsearch: 1.1.0 1945 | 1946 | camelcase-css@2.0.1: {} 1947 | 1948 | caniuse-lite@1.0.30001695: {} 1949 | 1950 | chalk@5.4.1: {} 1951 | 1952 | chokidar@3.6.0: 1953 | dependencies: 1954 | anymatch: 3.1.3 1955 | braces: 3.0.3 1956 | glob-parent: 5.1.2 1957 | is-binary-path: 2.1.0 1958 | is-glob: 4.0.3 1959 | normalize-path: 3.0.0 1960 | readdirp: 3.6.0 1961 | optionalDependencies: 1962 | fsevents: 2.3.3 1963 | 1964 | class-variance-authority@0.7.1: 1965 | dependencies: 1966 | clsx: 2.1.1 1967 | 1968 | client-only@0.0.1: {} 1969 | 1970 | clsx@2.1.1: {} 1971 | 1972 | color-convert@2.0.1: 1973 | dependencies: 1974 | color-name: 1.1.4 1975 | 1976 | color-name@1.1.4: {} 1977 | 1978 | color-string@1.9.1: 1979 | dependencies: 1980 | color-name: 1.1.4 1981 | simple-swizzle: 0.2.2 1982 | optional: true 1983 | 1984 | color@4.2.3: 1985 | dependencies: 1986 | color-convert: 2.0.1 1987 | color-string: 1.9.1 1988 | optional: true 1989 | 1990 | combined-stream@1.0.8: 1991 | dependencies: 1992 | delayed-stream: 1.0.0 1993 | 1994 | commander@4.1.1: {} 1995 | 1996 | create-require@1.1.1: 1997 | optional: true 1998 | 1999 | cross-spawn@7.0.6: 2000 | dependencies: 2001 | path-key: 3.1.1 2002 | shebang-command: 2.0.0 2003 | which: 2.0.2 2004 | 2005 | crypto-js@4.2.0: {} 2006 | 2007 | cssesc@3.0.0: {} 2008 | 2009 | csstype@3.1.3: {} 2010 | 2011 | delayed-stream@1.0.0: {} 2012 | 2013 | dequal@2.0.3: {} 2014 | 2015 | detect-libc@2.0.3: 2016 | optional: true 2017 | 2018 | didyoumean@1.2.2: {} 2019 | 2020 | diff-match-patch@1.0.5: {} 2021 | 2022 | diff@4.0.2: 2023 | optional: true 2024 | 2025 | dlv@1.1.3: {} 2026 | 2027 | eastasianwidth@0.2.0: {} 2028 | 2029 | emoji-regex@8.0.0: {} 2030 | 2031 | emoji-regex@9.2.2: {} 2032 | 2033 | event-target-shim@5.0.1: {} 2034 | 2035 | eventsource-parser@3.0.0: {} 2036 | 2037 | fast-glob@3.3.3: 2038 | dependencies: 2039 | '@nodelib/fs.stat': 2.0.5 2040 | '@nodelib/fs.walk': 1.2.8 2041 | glob-parent: 5.1.2 2042 | merge2: 1.4.1 2043 | micromatch: 4.0.8 2044 | 2045 | fastq@1.18.0: 2046 | dependencies: 2047 | reusify: 1.0.4 2048 | 2049 | fill-range@7.1.1: 2050 | dependencies: 2051 | to-regex-range: 5.0.1 2052 | 2053 | foreground-child@3.3.0: 2054 | dependencies: 2055 | cross-spawn: 7.0.6 2056 | signal-exit: 4.1.0 2057 | 2058 | form-data-encoder@1.7.2: {} 2059 | 2060 | form-data@4.0.1: 2061 | dependencies: 2062 | asynckit: 0.4.0 2063 | combined-stream: 1.0.8 2064 | mime-types: 2.1.35 2065 | 2066 | formdata-node@4.4.1: 2067 | dependencies: 2068 | node-domexception: 1.0.0 2069 | web-streams-polyfill: 4.0.0-beta.3 2070 | 2071 | framer-motion@12.0.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0): 2072 | dependencies: 2073 | motion-dom: 12.0.0 2074 | motion-utils: 12.0.0 2075 | tslib: 2.8.1 2076 | optionalDependencies: 2077 | react: 19.0.0 2078 | react-dom: 19.0.0(react@19.0.0) 2079 | 2080 | fsevents@2.3.3: 2081 | optional: true 2082 | 2083 | function-bind@1.1.2: {} 2084 | 2085 | glob-parent@5.1.2: 2086 | dependencies: 2087 | is-glob: 4.0.3 2088 | 2089 | glob-parent@6.0.2: 2090 | dependencies: 2091 | is-glob: 4.0.3 2092 | 2093 | glob@10.4.5: 2094 | dependencies: 2095 | foreground-child: 3.3.0 2096 | jackspeak: 3.4.3 2097 | minimatch: 9.0.5 2098 | minipass: 7.1.2 2099 | package-json-from-dist: 1.0.1 2100 | path-scurry: 1.11.1 2101 | 2102 | hasown@2.0.2: 2103 | dependencies: 2104 | function-bind: 1.1.2 2105 | 2106 | humanize-ms@1.2.1: 2107 | dependencies: 2108 | ms: 2.1.3 2109 | 2110 | is-arrayish@0.3.2: 2111 | optional: true 2112 | 2113 | is-binary-path@2.1.0: 2114 | dependencies: 2115 | binary-extensions: 2.3.0 2116 | 2117 | is-core-module@2.16.1: 2118 | dependencies: 2119 | hasown: 2.0.2 2120 | 2121 | is-extglob@2.1.1: {} 2122 | 2123 | is-fullwidth-code-point@3.0.0: {} 2124 | 2125 | is-glob@4.0.3: 2126 | dependencies: 2127 | is-extglob: 2.1.1 2128 | 2129 | is-number@7.0.0: {} 2130 | 2131 | isexe@2.0.0: {} 2132 | 2133 | jackspeak@3.4.3: 2134 | dependencies: 2135 | '@isaacs/cliui': 8.0.2 2136 | optionalDependencies: 2137 | '@pkgjs/parseargs': 0.11.0 2138 | 2139 | jiti@1.21.7: {} 2140 | 2141 | json-schema@0.4.0: {} 2142 | 2143 | jsondiffpatch@0.6.0: 2144 | dependencies: 2145 | '@types/diff-match-patch': 1.0.36 2146 | chalk: 5.4.1 2147 | diff-match-patch: 1.0.5 2148 | 2149 | lilconfig@3.1.3: {} 2150 | 2151 | lines-and-columns@1.2.4: {} 2152 | 2153 | lru-cache@10.4.3: {} 2154 | 2155 | lucide-react@0.474.0(react@19.0.0): 2156 | dependencies: 2157 | react: 19.0.0 2158 | 2159 | make-error@1.3.6: 2160 | optional: true 2161 | 2162 | merge2@1.4.1: {} 2163 | 2164 | micromatch@4.0.8: 2165 | dependencies: 2166 | braces: 3.0.3 2167 | picomatch: 2.3.1 2168 | 2169 | mime-db@1.52.0: {} 2170 | 2171 | mime-types@2.1.35: 2172 | dependencies: 2173 | mime-db: 1.52.0 2174 | 2175 | minimatch@9.0.5: 2176 | dependencies: 2177 | brace-expansion: 2.0.1 2178 | 2179 | minipass@7.1.2: {} 2180 | 2181 | motion-dom@12.0.0: 2182 | dependencies: 2183 | motion-utils: 12.0.0 2184 | 2185 | motion-utils@12.0.0: {} 2186 | 2187 | motion@12.0.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0): 2188 | dependencies: 2189 | framer-motion: 12.0.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2190 | tslib: 2.8.1 2191 | optionalDependencies: 2192 | react: 19.0.0 2193 | react-dom: 19.0.0(react@19.0.0) 2194 | 2195 | ms@2.1.3: {} 2196 | 2197 | mz@2.7.0: 2198 | dependencies: 2199 | any-promise: 1.3.0 2200 | object-assign: 4.1.1 2201 | thenify-all: 1.6.0 2202 | 2203 | nanoid@3.3.8: {} 2204 | 2205 | next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): 2206 | dependencies: 2207 | '@next/env': 15.1.6 2208 | '@swc/counter': 0.1.3 2209 | '@swc/helpers': 0.5.15 2210 | busboy: 1.6.0 2211 | caniuse-lite: 1.0.30001695 2212 | postcss: 8.4.31 2213 | react: 19.0.0 2214 | react-dom: 19.0.0(react@19.0.0) 2215 | styled-jsx: 5.1.6(react@19.0.0) 2216 | optionalDependencies: 2217 | '@next/swc-darwin-arm64': 15.1.6 2218 | '@next/swc-darwin-x64': 15.1.6 2219 | '@next/swc-linux-arm64-gnu': 15.1.6 2220 | '@next/swc-linux-arm64-musl': 15.1.6 2221 | '@next/swc-linux-x64-gnu': 15.1.6 2222 | '@next/swc-linux-x64-musl': 15.1.6 2223 | '@next/swc-win32-arm64-msvc': 15.1.6 2224 | '@next/swc-win32-x64-msvc': 15.1.6 2225 | '@opentelemetry/api': 1.9.0 2226 | sharp: 0.33.5 2227 | transitivePeerDependencies: 2228 | - '@babel/core' 2229 | - babel-plugin-macros 2230 | 2231 | node-domexception@1.0.0: {} 2232 | 2233 | node-fetch@2.7.0: 2234 | dependencies: 2235 | whatwg-url: 5.0.0 2236 | 2237 | normalize-path@3.0.0: {} 2238 | 2239 | object-assign@4.1.1: {} 2240 | 2241 | object-hash@3.0.0: {} 2242 | 2243 | openai@4.80.1(zod@3.24.1): 2244 | dependencies: 2245 | '@types/node': 18.19.74 2246 | '@types/node-fetch': 2.6.12 2247 | abort-controller: 3.0.0 2248 | agentkeepalive: 4.6.0 2249 | form-data-encoder: 1.7.2 2250 | formdata-node: 4.4.1 2251 | node-fetch: 2.7.0 2252 | optionalDependencies: 2253 | zod: 3.24.1 2254 | transitivePeerDependencies: 2255 | - encoding 2256 | 2257 | package-json-from-dist@1.0.1: {} 2258 | 2259 | path-key@3.1.1: {} 2260 | 2261 | path-parse@1.0.7: {} 2262 | 2263 | path-scurry@1.11.1: 2264 | dependencies: 2265 | lru-cache: 10.4.3 2266 | minipass: 7.1.2 2267 | 2268 | picocolors@1.1.1: {} 2269 | 2270 | picomatch@2.3.1: {} 2271 | 2272 | pify@2.3.0: {} 2273 | 2274 | pirates@4.0.6: {} 2275 | 2276 | playwright-core@1.50.0: {} 2277 | 2278 | postcss-import@15.1.0(postcss@8.5.1): 2279 | dependencies: 2280 | postcss: 8.5.1 2281 | postcss-value-parser: 4.2.0 2282 | read-cache: 1.0.0 2283 | resolve: 1.22.10 2284 | 2285 | postcss-js@4.0.1(postcss@8.5.1): 2286 | dependencies: 2287 | camelcase-css: 2.0.1 2288 | postcss: 8.5.1 2289 | 2290 | postcss-load-config@4.0.2(postcss@8.5.1)(ts-node@10.9.1(@types/node@20.17.16)(typescript@5.7.3)): 2291 | dependencies: 2292 | lilconfig: 3.1.3 2293 | yaml: 2.7.0 2294 | optionalDependencies: 2295 | postcss: 8.5.1 2296 | ts-node: 10.9.1(@types/node@20.17.16)(typescript@5.7.3) 2297 | 2298 | postcss-nested@6.2.0(postcss@8.5.1): 2299 | dependencies: 2300 | postcss: 8.5.1 2301 | postcss-selector-parser: 6.1.2 2302 | 2303 | postcss-selector-parser@6.1.2: 2304 | dependencies: 2305 | cssesc: 3.0.0 2306 | util-deprecate: 1.0.2 2307 | 2308 | postcss-value-parser@4.2.0: {} 2309 | 2310 | postcss@8.4.31: 2311 | dependencies: 2312 | nanoid: 3.3.8 2313 | picocolors: 1.1.1 2314 | source-map-js: 1.2.1 2315 | 2316 | postcss@8.5.1: 2317 | dependencies: 2318 | nanoid: 3.3.8 2319 | picocolors: 1.1.1 2320 | source-map-js: 1.2.1 2321 | 2322 | queue-microtask@1.2.3: {} 2323 | 2324 | react-dom@19.0.0(react@19.0.0): 2325 | dependencies: 2326 | react: 19.0.0 2327 | scheduler: 0.25.0 2328 | 2329 | react-intersection-observer@9.15.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): 2330 | dependencies: 2331 | react: 19.0.0 2332 | optionalDependencies: 2333 | react-dom: 19.0.0(react@19.0.0) 2334 | 2335 | react-resizable-panels@2.1.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0): 2336 | dependencies: 2337 | react: 19.0.0 2338 | react-dom: 19.0.0(react@19.0.0) 2339 | 2340 | react-textarea-autosize@8.5.7(@types/react@19.0.8)(react@19.0.0): 2341 | dependencies: 2342 | '@babel/runtime': 7.26.7 2343 | react: 19.0.0 2344 | use-composed-ref: 1.4.0(@types/react@19.0.8)(react@19.0.0) 2345 | use-latest: 1.3.0(@types/react@19.0.8)(react@19.0.0) 2346 | transitivePeerDependencies: 2347 | - '@types/react' 2348 | 2349 | react@19.0.0: {} 2350 | 2351 | read-cache@1.0.0: 2352 | dependencies: 2353 | pify: 2.3.0 2354 | 2355 | readdirp@3.6.0: 2356 | dependencies: 2357 | picomatch: 2.3.1 2358 | 2359 | regenerator-runtime@0.14.1: {} 2360 | 2361 | resolve@1.22.10: 2362 | dependencies: 2363 | is-core-module: 2.16.1 2364 | path-parse: 1.0.7 2365 | supports-preserve-symlinks-flag: 1.0.0 2366 | 2367 | reusify@1.0.4: {} 2368 | 2369 | run-parallel@1.2.0: 2370 | dependencies: 2371 | queue-microtask: 1.2.3 2372 | 2373 | scheduler@0.25.0: {} 2374 | 2375 | secure-json-parse@2.7.0: {} 2376 | 2377 | semver@7.6.3: 2378 | optional: true 2379 | 2380 | sharp@0.33.5: 2381 | dependencies: 2382 | color: 4.2.3 2383 | detect-libc: 2.0.3 2384 | semver: 7.6.3 2385 | optionalDependencies: 2386 | '@img/sharp-darwin-arm64': 0.33.5 2387 | '@img/sharp-darwin-x64': 0.33.5 2388 | '@img/sharp-libvips-darwin-arm64': 1.0.4 2389 | '@img/sharp-libvips-darwin-x64': 1.0.4 2390 | '@img/sharp-libvips-linux-arm': 1.0.5 2391 | '@img/sharp-libvips-linux-arm64': 1.0.4 2392 | '@img/sharp-libvips-linux-s390x': 1.0.4 2393 | '@img/sharp-libvips-linux-x64': 1.0.4 2394 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 2395 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 2396 | '@img/sharp-linux-arm': 0.33.5 2397 | '@img/sharp-linux-arm64': 0.33.5 2398 | '@img/sharp-linux-s390x': 0.33.5 2399 | '@img/sharp-linux-x64': 0.33.5 2400 | '@img/sharp-linuxmusl-arm64': 0.33.5 2401 | '@img/sharp-linuxmusl-x64': 0.33.5 2402 | '@img/sharp-wasm32': 0.33.5 2403 | '@img/sharp-win32-ia32': 0.33.5 2404 | '@img/sharp-win32-x64': 0.33.5 2405 | optional: true 2406 | 2407 | shebang-command@2.0.0: 2408 | dependencies: 2409 | shebang-regex: 3.0.0 2410 | 2411 | shebang-regex@3.0.0: {} 2412 | 2413 | signal-exit@4.1.0: {} 2414 | 2415 | simple-swizzle@0.2.2: 2416 | dependencies: 2417 | is-arrayish: 0.3.2 2418 | optional: true 2419 | 2420 | source-map-js@1.2.1: {} 2421 | 2422 | streamsearch@1.1.0: {} 2423 | 2424 | string-width@4.2.3: 2425 | dependencies: 2426 | emoji-regex: 8.0.0 2427 | is-fullwidth-code-point: 3.0.0 2428 | strip-ansi: 6.0.1 2429 | 2430 | string-width@5.1.2: 2431 | dependencies: 2432 | eastasianwidth: 0.2.0 2433 | emoji-regex: 9.2.2 2434 | strip-ansi: 7.1.0 2435 | 2436 | strip-ansi@6.0.1: 2437 | dependencies: 2438 | ansi-regex: 5.0.1 2439 | 2440 | strip-ansi@7.1.0: 2441 | dependencies: 2442 | ansi-regex: 6.1.0 2443 | 2444 | styled-jsx@5.1.6(react@19.0.0): 2445 | dependencies: 2446 | client-only: 0.0.1 2447 | react: 19.0.0 2448 | 2449 | sucrase@3.35.0: 2450 | dependencies: 2451 | '@jridgewell/gen-mapping': 0.3.8 2452 | commander: 4.1.1 2453 | glob: 10.4.5 2454 | lines-and-columns: 1.2.4 2455 | mz: 2.7.0 2456 | pirates: 4.0.6 2457 | ts-interface-checker: 0.1.13 2458 | 2459 | supports-preserve-symlinks-flag@1.0.0: {} 2460 | 2461 | swr@2.3.0(react@19.0.0): 2462 | dependencies: 2463 | dequal: 2.0.3 2464 | react: 19.0.0 2465 | use-sync-external-store: 1.4.0(react@19.0.0) 2466 | 2467 | tailwind-merge@2.6.0: {} 2468 | 2469 | tailwindcss-animate@1.0.7(tailwindcss@3.4.17(ts-node@10.9.1(@types/node@20.17.16)(typescript@5.7.3))): 2470 | dependencies: 2471 | tailwindcss: 3.4.17(ts-node@10.9.1(@types/node@20.17.16)(typescript@5.7.3)) 2472 | 2473 | tailwindcss@3.4.17(ts-node@10.9.1(@types/node@20.17.16)(typescript@5.7.3)): 2474 | dependencies: 2475 | '@alloc/quick-lru': 5.2.0 2476 | arg: 5.0.2 2477 | chokidar: 3.6.0 2478 | didyoumean: 1.2.2 2479 | dlv: 1.1.3 2480 | fast-glob: 3.3.3 2481 | glob-parent: 6.0.2 2482 | is-glob: 4.0.3 2483 | jiti: 1.21.7 2484 | lilconfig: 3.1.3 2485 | micromatch: 4.0.8 2486 | normalize-path: 3.0.0 2487 | object-hash: 3.0.0 2488 | picocolors: 1.1.1 2489 | postcss: 8.5.1 2490 | postcss-import: 15.1.0(postcss@8.5.1) 2491 | postcss-js: 4.0.1(postcss@8.5.1) 2492 | postcss-load-config: 4.0.2(postcss@8.5.1)(ts-node@10.9.1(@types/node@20.17.16)(typescript@5.7.3)) 2493 | postcss-nested: 6.2.0(postcss@8.5.1) 2494 | postcss-selector-parser: 6.1.2 2495 | resolve: 1.22.10 2496 | sucrase: 3.35.0 2497 | transitivePeerDependencies: 2498 | - ts-node 2499 | 2500 | thenify-all@1.6.0: 2501 | dependencies: 2502 | thenify: 3.3.1 2503 | 2504 | thenify@3.3.1: 2505 | dependencies: 2506 | any-promise: 1.3.0 2507 | 2508 | throttleit@2.1.0: {} 2509 | 2510 | to-regex-range@5.0.1: 2511 | dependencies: 2512 | is-number: 7.0.0 2513 | 2514 | tr46@0.0.3: {} 2515 | 2516 | ts-interface-checker@0.1.13: {} 2517 | 2518 | ts-node@10.9.1(@types/node@20.17.16)(typescript@5.7.3): 2519 | dependencies: 2520 | '@cspotcode/source-map-support': 0.8.1 2521 | '@tsconfig/node10': 1.0.11 2522 | '@tsconfig/node12': 1.0.11 2523 | '@tsconfig/node14': 1.0.3 2524 | '@tsconfig/node16': 1.0.4 2525 | '@types/node': 20.17.16 2526 | acorn: 8.14.0 2527 | acorn-walk: 8.3.4 2528 | arg: 4.1.3 2529 | create-require: 1.1.1 2530 | diff: 4.0.2 2531 | make-error: 1.3.6 2532 | typescript: 5.7.3 2533 | v8-compile-cache-lib: 3.0.1 2534 | yn: 3.1.1 2535 | optional: true 2536 | 2537 | tslib@2.8.1: {} 2538 | 2539 | typescript@5.7.3: {} 2540 | 2541 | undici-types@5.26.5: {} 2542 | 2543 | undici-types@6.19.8: {} 2544 | 2545 | use-composed-ref@1.4.0(@types/react@19.0.8)(react@19.0.0): 2546 | dependencies: 2547 | react: 19.0.0 2548 | optionalDependencies: 2549 | '@types/react': 19.0.8 2550 | 2551 | use-isomorphic-layout-effect@1.2.0(@types/react@19.0.8)(react@19.0.0): 2552 | dependencies: 2553 | react: 19.0.0 2554 | optionalDependencies: 2555 | '@types/react': 19.0.8 2556 | 2557 | use-latest@1.3.0(@types/react@19.0.8)(react@19.0.0): 2558 | dependencies: 2559 | react: 19.0.0 2560 | use-isomorphic-layout-effect: 1.2.0(@types/react@19.0.8)(react@19.0.0) 2561 | optionalDependencies: 2562 | '@types/react': 19.0.8 2563 | 2564 | use-sync-external-store@1.4.0(react@19.0.0): 2565 | dependencies: 2566 | react: 19.0.0 2567 | 2568 | util-deprecate@1.0.2: {} 2569 | 2570 | v8-compile-cache-lib@3.0.1: 2571 | optional: true 2572 | 2573 | web-streams-polyfill@4.0.0-beta.3: {} 2574 | 2575 | webidl-conversions@3.0.1: {} 2576 | 2577 | whatwg-url@5.0.0: 2578 | dependencies: 2579 | tr46: 0.0.3 2580 | webidl-conversions: 3.0.1 2581 | 2582 | which@2.0.2: 2583 | dependencies: 2584 | isexe: 2.0.0 2585 | 2586 | wrap-ansi@7.0.0: 2587 | dependencies: 2588 | ansi-styles: 4.3.0 2589 | string-width: 4.2.3 2590 | strip-ansi: 6.0.1 2591 | 2592 | wrap-ansi@8.1.0: 2593 | dependencies: 2594 | ansi-styles: 6.2.1 2595 | string-width: 5.1.2 2596 | strip-ansi: 7.1.0 2597 | 2598 | yaml@2.7.0: {} 2599 | 2600 | yn@3.1.1: 2601 | optional: true 2602 | 2603 | zod-to-json-schema@3.24.1(zod@3.24.1): 2604 | dependencies: 2605 | zod: 3.24.1 2606 | 2607 | zod@3.24.1: {} 2608 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/actions/session.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { 4 | closeBrowserSession, 5 | createSession, 6 | getSessionUrl, 7 | } from "@/lib/operator/session"; 8 | 9 | export async function createAndGetSessionUrl() { 10 | const session = await createSession(); 11 | const url = await getSessionUrl(session.id); 12 | 13 | return { sessionId: session.id, url }; 14 | } 15 | 16 | export async function closeSession(sessionId: string) { 17 | await closeBrowserSession(sessionId); 18 | } 19 | -------------------------------------------------------------------------------- /src/app/api/chat/lib/prompts.ts: -------------------------------------------------------------------------------- 1 | import { CoreMessage } from "ai"; 2 | 3 | export const systemPrompt = `You are a browser automation agent. 4 | You have these tools: searchGoogle, navigate, takeScreenshot, clickTarget, scrollDown, keyboardAction 5 | Your job is to execute actions automatically on the browser. 6 | The clickTarget tool uses Vision Language Model (VLM) capabilities to understand and locate elements on the page based on natural language descriptions. It can: 7 | - Find and click elements by analyzing the visual content of the page 8 | - Understand spatial relationships and visual context 9 | - Click elements based on their visual appearance and location 10 | 11 | When describing elements to click, be as specific and detailed as possible. Include: 12 | - The exact text content if visible 13 | - Visual appearance (color, shape, size) 14 | - Location relative to other elements 15 | - Any surrounding context or nearby elements 16 | 17 | For example, instead of "click the YouTube link", use more specific descriptions like: 18 | - "Click the YouTube link that says 'Getting Started Tutorial' below the header image" 19 | - "Click the red Subscribe button next to the channel name 'TechTips'" 20 | - "Click the 'Read More' link underneath the paragraph that starts with 'In this article'" 21 | - "Click the search icon (magnifying glass) in the top-right corner of the navigation bar" 22 | - "Click the blue 'Next' button at the bottom of the form, right after the email input field" 23 | 24 | For flight booking scenarios, use detailed descriptions like: 25 | - "Click the 'Departure' input field with the calendar icon on the left side of the search form" 26 | - "Click the 'Round Trip' radio button at the top of the flight search widget" 27 | - "Click the blue 'Search Flights' button located at the bottom of the search panel" 28 | - "Click the 'Add Passenger' dropdown menu showing '1 Adult' next to the passenger icon" 29 | - "Click the cheapest flight option that shows '$299' in the flight results list" 30 | - "Click the 'Select' button next to the 6:30 AM departure time in the outbound flight section" 31 | 32 | The keyboardAction tool can be used to type text into an input element and submit automatically, or press specific keys (like "Enter", "Tab", "ArrowDown" etc). 33 | So you can click an input element, then type text into it. It will automatically try to press enter for you. 34 | 35 | Or you can mention a key, like "Enter" or "Tab" or "ArrowDown" etc. And it will press that key. 36 | 37 | Generally you want to take a screenshot of the page after each action so you can see what's happening. 38 | 39 | When the user's request is satisfied, you can reply with the results, otherwise keep invoking tools in a loop. 40 | 41 | Note avoid doing multiple actions at the same time. I.e first navigate to the page, then take a screenshot, then click the element. etc. 42 | 43 | Try to use the various inputs fields like search bars on webapps to search for things. If you get stuck you can use the searchGoogle tool. 44 | 45 | Note: today's date is ${new Date().toISOString().split("T")[0]}.`; 46 | 47 | export const clickableElementsPrompt = `You are a browser automation agent. 48 | You have these tools: searchGoogle, navigate, takeScreenshot, viewAllClickableElements, browserAction 49 | 50 | Your primary method for interacting with pages is: 51 | 1. Use viewAllClickableElements to see all clickable elements on the page with their index numbers 52 | 2. Use browserAction with the "click" action and specify the index number to click the desired element 53 | 54 | For example, this workflow: 55 | 1. First call viewAllClickableElements() to see all clickable elements 56 | 2. Identify the index of the element you want to click from the screenshot 57 | 3. Use browserAction({ action: "click", clickIndex: X }) where X is the index number 58 | 59 | Important notes: 60 | - After any action that might change the page content (like clicking dropdowns, submitting forms, etc), 61 | you should call viewAllClickableElements() again to get the updated list of elements and their new index numbers, 62 | as the DOM structure may have changed. 63 | - On Google search results, avoid clicking the ellipsis (...) buttons next to results as these open side panels/menus. 64 | Always click the actual link elements (titles or URLs) to navigate to the websites. 65 | - Scroll the page if needed to ensure elements are in view before clicking. 66 | - For Google search results, prefer clicking the main title link or URL link directly rather than 67 | relying on any numbered shortcuts or badges. 68 | 69 | You can also: 70 | - Navigate to URLs using the navigate tool 71 | - Search Google using searchGoogle 72 | - Type text using browserAction with "type" action 73 | - Press keyboard keys using browserAction with "key" action 74 | - Scroll the page using browserAction with "scroll" action 75 | - Take screenshots using browserAction with "screenshot" action 76 | 77 | When the user's request is satisfied, you can reply with the results, otherwise keep invoking tools in a loop. 78 | 79 | Note: avoid doing multiple actions at the same time. Execute actions one at a time in sequence. 80 | 81 | Try to use the various input fields like search bars on webapps to search for things. If you get stuck you can use the searchGoogle tool. 82 | 83 | Note: today's date is ${new Date().toISOString().split("T")[0]}.`; 84 | 85 | export function getCachedMessages(messages: CoreMessage[]) { 86 | const systemMessage: CoreMessage = { 87 | role: "system", 88 | content: systemPrompt, 89 | experimental_providerMetadata: { 90 | anthropic: { cacheControl: { type: "ephemeral" } }, 91 | }, 92 | }; 93 | 94 | const cachedMessages = [systemMessage, ...messages]; 95 | 96 | return cachedMessages; 97 | } 98 | -------------------------------------------------------------------------------- /src/app/api/chat/lib/script.js: -------------------------------------------------------------------------------- 1 | // Self-executing function to avoid polluting global scope 2 | (function () { 3 | // Store references we'll need later 4 | let currentDomState = null; 5 | 6 | function isElementInteractive(el) { 7 | if (!el) return false; 8 | const tag = (el.tagName || "").toLowerCase(); 9 | const interactiveTags = new Set([ 10 | "a", 11 | "button", 12 | "details", 13 | "embed", 14 | "input", 15 | "label", 16 | "menu", 17 | "menuitem", 18 | "object", 19 | "select", 20 | "textarea", 21 | "summary", 22 | ]); 23 | if (interactiveTags.has(tag)) return true; 24 | 25 | const role = el.getAttribute && el.getAttribute("role"); 26 | if ( 27 | role && 28 | /^(button|menu|menuitem|link|checkbox|radio|tab|switch|treeitem)$/i.test( 29 | role 30 | ) 31 | ) { 32 | return true; 33 | } 34 | 35 | if ( 36 | el.hasAttribute && 37 | (el.hasAttribute("onclick") || 38 | el.hasAttribute("ng-click") || 39 | el.hasAttribute("@click")) 40 | ) { 41 | return true; 42 | } 43 | 44 | const tabIndex = el.getAttribute && el.getAttribute("tabindex"); 45 | if (tabIndex && tabIndex !== "-1") return true; 46 | 47 | if (el.getAttribute && el.getAttribute("data-action")) { 48 | return true; 49 | } 50 | return false; 51 | } 52 | 53 | function isVisible(el) { 54 | if (!el || !el.getBoundingClientRect) return false; 55 | const rect = el.getBoundingClientRect(); 56 | if (rect.width === 0 && rect.height === 0) return false; 57 | const style = window.getComputedStyle(el); 58 | if ( 59 | style.display === "none" || 60 | style.visibility === "hidden" || 61 | parseFloat(style.opacity) < 0.1 62 | ) { 63 | return false; 64 | } 65 | return true; 66 | } 67 | 68 | function computeXPath(el) { 69 | if (!el || el.nodeType !== Node.ELEMENT_NODE) return ""; 70 | let pathSegments = []; 71 | let current = el; 72 | while (current && current.nodeType === Node.ELEMENT_NODE) { 73 | const tagName = current.nodeName.toLowerCase(); 74 | let index = 1; 75 | let sibling = current.previousSibling; 76 | while (sibling) { 77 | if ( 78 | sibling.nodeType === Node.ELEMENT_NODE && 79 | sibling.nodeName.toLowerCase() === tagName 80 | ) { 81 | index++; 82 | } 83 | sibling = sibling.previousSibling; 84 | } 85 | const segment = index > 1 ? tagName + "[" + index + "]" : tagName; 86 | pathSegments.unshift(segment); 87 | current = current.parentNode; 88 | if (!current || !current.parentNode) break; 89 | if (current.nodeName.toLowerCase() === "html") { 90 | pathSegments.unshift("html"); 91 | break; 92 | } 93 | } 94 | return "/" + pathSegments.join("/"); 95 | } 96 | 97 | function gatherDomTree() { 98 | let highlightCounter = 1; 99 | const selectorMap = {}; 100 | 101 | function processNode(node) { 102 | if (node.nodeType === Node.TEXT_NODE) { 103 | const textContent = node.nodeValue.trim(); 104 | if ( 105 | !textContent || 106 | textContent.length < 2 || 107 | /^[\d\s./$@]+$/.test(textContent) 108 | ) { 109 | return null; 110 | } 111 | return { 112 | type: "TEXT_NODE", 113 | text: textContent, 114 | isVisible: isVisible(node.parentElement), 115 | }; 116 | } 117 | 118 | if (node.nodeType !== Node.ELEMENT_NODE) return null; 119 | 120 | const el = node; 121 | const tagName = el.tagName.toLowerCase(); 122 | 123 | if ( 124 | (tagName === "a" && 125 | !el.textContent.trim() && 126 | !el.querySelector("img")) || 127 | tagName === "script" || 128 | tagName === "style" 129 | ) { 130 | return null; 131 | } 132 | 133 | const attrs = {}; 134 | for (let attr of el.attributes) { 135 | attrs[attr.name] = attr.value; 136 | } 137 | 138 | const childNodes = []; 139 | for (let child of el.childNodes) { 140 | const processed = processNode(child); 141 | if (processed) childNodes.push(processed); 142 | } 143 | 144 | const elVisible = isVisible(el); 145 | const nodeData = { 146 | type: "ELEMENT_NODE", 147 | tagName, 148 | xpath: computeXPath(el), 149 | attributes: attrs, 150 | children: childNodes, 151 | isVisible: elVisible, 152 | isInteractive: false, 153 | isTopElement: false, 154 | element: el, // Store reference to actual DOM element 155 | }; 156 | 157 | if (isElementInteractive(el) && elVisible) { 158 | nodeData.isInteractive = true; 159 | nodeData.highlightIndex = highlightCounter++; 160 | selectorMap[nodeData.highlightIndex] = nodeData; 161 | } 162 | 163 | return nodeData; 164 | } 165 | 166 | const root = document.documentElement; 167 | const elementTree = processNode(root); 168 | if (elementTree) { 169 | elementTree.isTopElement = true; 170 | elementTree.xpath = "/html"; 171 | } 172 | 173 | return { elementTree, selectorMap }; 174 | } 175 | 176 | function addHighlightStyles() { 177 | const styleId = "dom-highlighter-style"; 178 | if (!document.getElementById(styleId)) { 179 | const styleEl = document.createElement("style"); 180 | styleEl.id = styleId; 181 | styleEl.textContent = ` 182 | .dom-highlighter-overlay { 183 | position: fixed; 184 | font-size: 14px; 185 | font-weight: bold; 186 | z-index: 999999; 187 | pointer-events: none; 188 | box-sizing: border-box; 189 | border: 2px solid transparent; 190 | } 191 | .dom-highlighter-number { 192 | position: absolute; 193 | top: -20px; 194 | left: 50%; 195 | transform: translateX(-50%); 196 | padding: 2px 6px; 197 | border-radius: 10px; 198 | font-size: 11px; 199 | font-weight: bold; 200 | white-space: nowrap; 201 | opacity: 0.9; 202 | border: 1px solid rgba(0,0,0,0.2); 203 | } 204 | `; 205 | document.head.appendChild(styleEl); 206 | } 207 | } 208 | 209 | function clearHighlights() { 210 | document 211 | .querySelectorAll(".dom-highlighter-overlay") 212 | .forEach((el) => el.remove()); 213 | } 214 | 215 | // Update the highlighting code to use matching colors 216 | function highlightElement(el, index, rect) { 217 | const colors = [ 218 | { border: "#FF5D5D", bg: "#FF5D5D", highlight: "rgba(255,93,93,0.08)" }, 219 | { border: "#4CAF50", bg: "#4CAF50", highlight: "rgba(76,175,80,0.08)" }, 220 | { border: "#2196F3", bg: "#2196F3", highlight: "rgba(33,150,243,0.08)" }, 221 | { border: "#FFC107", bg: "#FFC107", highlight: "rgba(255,193,7,0.08)" }, 222 | { border: "#9C27B0", bg: "#9C27B0", highlight: "rgba(156,39,176,0.08)" }, 223 | ]; 224 | 225 | const colorStyle = colors[index % colors.length]; 226 | const overlay = document.createElement("div"); 227 | overlay.className = "dom-highlighter-overlay"; 228 | 229 | const numberSpan = document.createElement("span"); 230 | numberSpan.className = "dom-highlighter-number"; 231 | numberSpan.textContent = String(index); 232 | numberSpan.style.backgroundColor = colorStyle.bg; 233 | numberSpan.style.color = "white"; 234 | overlay.appendChild(numberSpan); 235 | 236 | overlay.style.top = rect.top + "px"; 237 | overlay.style.left = rect.left + "px"; 238 | overlay.style.width = rect.width + "px"; 239 | overlay.style.height = rect.height + "px"; 240 | overlay.style.borderColor = colorStyle.border; 241 | overlay.style.backgroundColor = colorStyle.highlight; 242 | 243 | document.body.appendChild(overlay); 244 | } 245 | 246 | // Exposed global functions 247 | window.highlightInteractiveElements = function () { 248 | clearHighlights(); 249 | currentDomState = gatherDomTree(); 250 | addHighlightStyles(); 251 | 252 | const highlightColors = [ 253 | { border: "#FF5D5D", background: "rgba(255,93,93,0.2)" }, 254 | { border: "#5DFF5D", background: "rgba(93,255,93,0.2)" }, 255 | { border: "#5D5DFF", background: "rgba(93,93,255,0.2)" }, 256 | { border: "#FFB85D", background: "rgba(255,184,93,0.2)" }, 257 | { border: "#FF5DCB", background: "rgba(255,93,203,0.2)" }, 258 | ]; 259 | 260 | Object.values(currentDomState.selectorMap).forEach((nodeObj) => { 261 | const el = nodeObj.element; 262 | const highlightIndex = nodeObj.highlightIndex; 263 | 264 | const rect = el.getBoundingClientRect(); 265 | if (rect.width === 0 && rect.height === 0) return; 266 | 267 | highlightElement(el, highlightIndex, rect); 268 | }); 269 | 270 | console.log( 271 | "Interactive elements highlighted! Use clickHighlightedElement(index) to click an element." 272 | ); 273 | }; 274 | 275 | window.clickHighlightedElement = function (index) { 276 | if (!currentDomState) { 277 | console.error("Please run highlightInteractiveElements() first!"); 278 | return; 279 | } 280 | 281 | const nodeData = currentDomState.selectorMap[index]; 282 | if (!nodeData) { 283 | console.error(`No element found with index ${index}`); 284 | return; 285 | } 286 | 287 | nodeData.element.click(); 288 | console.log(`Clicked element with index ${index}`); 289 | }; 290 | 291 | console.log( 292 | "DOM highlighter loaded! Call highlightInteractiveElements() to begin." 293 | ); 294 | })(); 295 | -------------------------------------------------------------------------------- /src/app/api/chat/lib/vision.ts: -------------------------------------------------------------------------------- 1 | import { takeScreenshot } from "@/lib/operator/actions"; 2 | import { 3 | clearDomHighlights, 4 | clickElementByHighlightIndex, 5 | getDomState, 6 | highlightDomElements, 7 | } from "@/lib/operator/dom"; 8 | import { google } from "@ai-sdk/google"; 9 | import { generateObject, generateText } from "ai"; 10 | import type { Page } from "playwright-core"; 11 | import z from "zod"; 12 | 13 | /** 14 | * Gets coordinates for a target object in an image using vlm, doesn't work well atm. 15 | */ 16 | export async function getTargetCoordinates( 17 | imageBuffer: Buffer, 18 | targetDescription: string, 19 | ) { 20 | const model = google("gemini-1.5-flash-latest"); 21 | 22 | const data = await generateObject({ 23 | model, 24 | schema: z.object({ 25 | coordinates: z.array(z.number().min(0).max(1000)).length(4), 26 | }), 27 | system: `You are a vision model that locates objects in images. The user will give you an image and a description of the object you need to find. 28 | You will need to return the bounding box coordinates of the object in the image as an array of 4 numbers: [xmin, xmax, ymin, ymax]. Use coordinates between 0 and 1000.`, 29 | messages: [ 30 | { 31 | role: "user", 32 | content: [ 33 | { 34 | type: "text", 35 | text: `Find the bounding box coordinates of this object: ${targetDescription}. Return the coordinates as an array [xmin, xmax, ymin, ymax]. All values should be between 0 and 1000.`, 36 | }, 37 | { 38 | type: "image", 39 | image: imageBuffer, 40 | }, 41 | ], 42 | }, 43 | ], 44 | }); 45 | 46 | if (!data) { 47 | throw new Error("No coordinates returned from vision model"); 48 | } 49 | 50 | const [xmin, xmax, ymin, ymax] = data.object.coordinates; 51 | 52 | const centerPointX = (xmin + xmax) / 2; 53 | const centerPointY = (ymin + ymax) / 2; 54 | 55 | const centerPoint = { 56 | x: centerPointX / 1000, 57 | y: centerPointY / 1000, 58 | }; 59 | 60 | return { 61 | xmin: xmin / 1000, 62 | xmax: xmax / 1000, 63 | ymin: ymin / 1000, 64 | ymax: ymax / 1000, 65 | centerPoint, 66 | }; 67 | } 68 | 69 | export async function viewAllClickableElements(page: Page) { 70 | const { domState, rawDom } = await getDomState(page, true); 71 | await highlightDomElements(page, rawDom); 72 | const { screenshot } = await takeScreenshot(page); 73 | await clearDomHighlights(page); 74 | return { 75 | screenshot, 76 | domState, 77 | }; 78 | } 79 | export async function clickElementByIndex(page: Page, index: number) { 80 | const { domState, rawDom } = await getDomState(page, true); 81 | const result = await clickElementByHighlightIndex(page, domState, index); 82 | return result; 83 | } 84 | 85 | /** 86 | * Gets the element to click based on a natural language instruction 87 | */ 88 | export async function clickElementByVision(page: Page, instruction: string) { 89 | // First highlight all interactive elements 90 | const { domState, rawDom } = await getDomState(page, true); 91 | // 2) Actually draw the highlight overlays so the screenshot has numbered boxes 92 | await highlightDomElements(page, rawDom); 93 | const { screenshot } = await takeScreenshot(page); 94 | 95 | const model = google("gemini-1.5-flash-latest"); 96 | 97 | const data = await generateObject({ 98 | model, 99 | schema: z.object({ 100 | reasoning: z.string(), 101 | clickIndex: z.number(), 102 | }), 103 | system: `You are a vision model that helps users interact with web pages. 104 | The image will show a webpage with numbered highlights on clickable elements. 105 | Your task is to identify which highlighted element best matches the user's instruction. 106 | Start with your reasoning and then return the index of the element to click. 107 | Sometimes there will be no clickable element that matches the instruction. In that case, return -1, but explain why. 108 | `, 109 | messages: [ 110 | { 111 | role: "user", 112 | content: [ 113 | { 114 | type: "text", 115 | text: `Looking at the numbered highlights in this screenshot, which element best matches this instruction: "${instruction}"?`, 116 | }, 117 | { 118 | type: "image", 119 | image: screenshot.data, 120 | mimeType: screenshot.mimeType, 121 | }, 122 | ], 123 | }, 124 | ], 125 | }); 126 | 127 | if (!data) { 128 | return { 129 | reasoning: "No element selection returned from vision model", 130 | clickIndex: -1, 131 | }; 132 | } 133 | await clearDomHighlights(page); 134 | 135 | if (data.object.clickIndex === -1) { 136 | return { 137 | reasoning: data.object.reasoning, 138 | clickIndex: -1, 139 | }; 140 | } 141 | 142 | const success = await clickElementByHighlightIndex( 143 | page, 144 | domState, 145 | data.object.clickIndex, 146 | ); 147 | 148 | if (!success) { 149 | return { 150 | reasoning: `Failed to click element ${data.object.clickIndex}`, 151 | clickIndex: -1, 152 | }; 153 | } 154 | console.log("success", data.object); 155 | 156 | return { 157 | reasoning: data.object.reasoning, 158 | clickIndex: data.object.clickIndex, 159 | }; 160 | } 161 | -------------------------------------------------------------------------------- /src/app/api/chat/route.ts: -------------------------------------------------------------------------------- 1 | import { 2 | clickableElementsPrompt, 3 | getCachedMessages, 4 | } from "@/app/api/chat/lib/prompts"; 5 | import { 6 | clickElementByIndex, 7 | viewAllClickableElements, 8 | } from "@/app/api/chat/lib/vision"; 9 | import { 10 | scrollDownPage, 11 | searchGooglePage, 12 | takeScreenshot, 13 | } from "@/lib/operator/actions"; 14 | import { getOrCreateBrowser } from "@/lib/operator/browser"; 15 | import { highlightDomElements } from "@/lib/operator/dom"; 16 | import { ratelimit } from "@/lib/upstash/upstash"; 17 | import { sleep } from "@/lib/utils"; 18 | import { anthropic } from "@ai-sdk/anthropic"; 19 | import { 20 | convertToCoreMessages, 21 | createDataStreamResponse, 22 | streamText, 23 | } from "ai"; 24 | import { z } from "zod"; 25 | 26 | // Allow streaming responses up to 30 seconds 27 | export const maxDuration = 120; 28 | 29 | export async function POST(req: Request) { 30 | const { messages, sessionId } = await req.json(); 31 | 32 | // Check if the last message is from the user 33 | const lastMessage = messages[messages.length - 1]; 34 | if (lastMessage?.role === "user" && process.env.NODE_ENV !== "development") { 35 | const ip = req.headers.get("x-forwarded-for") || "anonymous"; 36 | const { success, limit, reset, remaining } = await ratelimit.limit(ip); 37 | 38 | if (!success) { 39 | return new Response( 40 | JSON.stringify({ 41 | error: "Daily message limit exceeded", 42 | limit, 43 | reset, 44 | remaining, 45 | }), 46 | { 47 | status: 429, 48 | headers: { 49 | "Content-Type": "application/json", 50 | "X-RateLimit-Limit": limit.toString(), 51 | "X-RateLimit-Remaining": remaining.toString(), 52 | "X-RateLimit-Reset": reset.toString(), 53 | }, 54 | } 55 | ); 56 | } 57 | } 58 | 59 | const model = anthropic("claude-3-5-sonnet-latest"); 60 | 61 | // Write initial state 62 | let stepCount = 0; 63 | 64 | const coreMessages = convertToCoreMessages(messages); 65 | const cachedMessages = getCachedMessages(coreMessages); 66 | return createDataStreamResponse({ 67 | execute: async (dataStream) => { 68 | const result = streamText({ 69 | model, 70 | messages: cachedMessages, 71 | maxSteps: 15, 72 | onStepFinish: async (stepResult) => { 73 | stepCount++; 74 | dataStream.writeData({ 75 | type: "step", 76 | content: "step_complete", 77 | step: stepCount, 78 | }); 79 | }, 80 | 81 | onFinish: async (result) => { 82 | if (result.finishReason === "stop") { 83 | dataStream.writeData({ 84 | type: "status", 85 | content: "finished", 86 | step: -1, 87 | }); 88 | } 89 | }, 90 | tools: { 91 | searchGoogle: { 92 | description: 93 | 'Navigate to Google and search for a query, i.e. "searchGoogle(query=...)"', 94 | parameters: z.object({ query: z.string() }), 95 | execute: async ({ query }: { query: string }) => { 96 | try { 97 | const { page } = await getOrCreateBrowser(sessionId); 98 | await searchGooglePage(page, query); 99 | const { screenshot } = await takeScreenshot(page); 100 | return { 101 | data: screenshot.data, 102 | mimeType: screenshot.mimeType, 103 | }; 104 | } catch (error) { 105 | return `Error searching Google: ${ 106 | error instanceof Error ? error.message : String(error) 107 | }`; 108 | } 109 | }, 110 | experimental_toToolResultContent(result) { 111 | return typeof result === "string" 112 | ? [{ type: "text", text: result }] 113 | : [ 114 | { 115 | type: "image", 116 | data: result.data, 117 | mimeType: result.mimeType, 118 | }, 119 | ]; 120 | }, 121 | }, 122 | navigate: { 123 | description: `Navigate in the browser. Available actions: 124 | - url: Navigate to a specific URL (requires url parameter) 125 | - back: Go back one page in history 126 | - forward: Go forward one page in history`, 127 | parameters: z.object({ 128 | action: z.enum(["url", "back", "forward"]), 129 | url: z 130 | .string() 131 | .optional() 132 | .describe("URL to navigate to (required for url action)"), 133 | }), 134 | execute: async ({ 135 | action, 136 | url, 137 | }: { 138 | action: "url" | "back" | "forward"; 139 | url?: string; 140 | }) => { 141 | try { 142 | const { page } = await getOrCreateBrowser(sessionId); 143 | let text = ""; 144 | 145 | if (action === "url") { 146 | if (!url) { 147 | throw new Error("URL parameter required for url action"); 148 | } 149 | const urlToGoTo = url.startsWith("http") 150 | ? url 151 | : `https://${url}`; 152 | await page.goto(urlToGoTo); 153 | text = `Navigated to ${urlToGoTo}`; 154 | } else if (action === "back") { 155 | await page.goBack(); 156 | text = "Navigated back one page"; 157 | } else if (action === "forward") { 158 | await page.goForward(); 159 | text = "Navigated forward one page"; 160 | } else { 161 | throw new Error(`Unknown navigation action: ${action}`); 162 | } 163 | 164 | const { screenshot } = await takeScreenshot(page); 165 | return { 166 | data: screenshot.data, 167 | mimeType: screenshot.mimeType, 168 | }; 169 | } catch (error) { 170 | return `Error navigating: ${ 171 | error instanceof Error ? error.message : String(error) 172 | }`; 173 | } 174 | }, 175 | experimental_toToolResultContent(result) { 176 | return typeof result === "string" 177 | ? [{ type: "text", text: result }] 178 | : [ 179 | { 180 | type: "image", 181 | data: result.data, 182 | mimeType: result.mimeType, 183 | }, 184 | ]; 185 | }, 186 | }, 187 | viewAllClickableElements: { 188 | description: 189 | "Highlight all clickable elements on the page with indexs and bounding boxes. Use this to see what you can click on the page, then select the index of the element you want to click.", 190 | parameters: z.object({}), 191 | execute: async () => { 192 | const { page } = await getOrCreateBrowser(sessionId); 193 | const elements = await viewAllClickableElements(page); 194 | return { 195 | data: elements.screenshot.data, 196 | mimeType: elements.screenshot.mimeType, 197 | }; 198 | }, 199 | experimental_toToolResultContent(result) { 200 | return [ 201 | { type: "image", data: result.data, mimeType: result.mimeType }, 202 | ]; 203 | }, 204 | }, 205 | browserAction: { 206 | description: `Perform browser actions like keyboard input, clicking, scrolling, and screenshots. 207 | Available actions: 208 | - type: Type text (requires text parameter) 209 | - key: Press a specific key (requires text parameter, e.g. "Enter", "Tab", "ArrowDown") 210 | - scroll: Scroll the page (optional amount parameter, use -1 to scroll to bottom) 211 | - screenshot: Take a screenshot of the current page 212 | - click: Click on elements using natural language description`, 213 | parameters: z.object({ 214 | action: z.enum([ 215 | "type", 216 | "key", 217 | "scroll", 218 | "screenshot", 219 | "click", 220 | "wait", 221 | ]), 222 | text: z 223 | .string() 224 | .optional() 225 | .describe( 226 | 'Text to type or key to press (required for "type" and "key" actions)' 227 | ), 228 | amount: z 229 | .number() 230 | .optional() 231 | .describe( 232 | 'Amount to scroll in pixels. Use -1 to scroll to bottom of page (optional for "scroll" action)' 233 | ), 234 | clickIndex: z 235 | .number() 236 | .optional() 237 | .describe( 238 | "The index of the element to click. Use this when you have a list of clickable elements and you want to click a specific one." 239 | ), 240 | wait: z 241 | .number() 242 | .optional() 243 | .describe( 244 | "The amount of time to wait in milliseconds (optional for 'wait' action). Max 10,000ms (10 seconds)" 245 | ), 246 | // clickObject: z 247 | // .string() 248 | // .optional() 249 | // .describe( 250 | // 'The object to click. Use natural language and be as specific as possible, e.g. "the blue Submit button", "the link that says Learn More", "the search icon in the top right"' 251 | // ), 252 | }), 253 | execute: async ({ action, text, amount, clickIndex, wait }) => { 254 | try { 255 | const { page } = await getOrCreateBrowser(sessionId); 256 | 257 | if (action === "type") { 258 | if (!text) 259 | throw new Error("Text parameter required for type action"); 260 | const TYPING_DELAY = 5; 261 | await page.keyboard.type(text, { delay: TYPING_DELAY }); 262 | await page.waitForTimeout(50); 263 | await page.keyboard.press("Enter"); 264 | 265 | const { screenshot } = await takeScreenshot(page); 266 | return { 267 | data: screenshot.data, 268 | mimeType: screenshot.mimeType, 269 | }; 270 | } 271 | 272 | if (action === "key") { 273 | if (!text) return "Text parameter required for key action"; 274 | await page.keyboard.press(text); 275 | const { screenshot } = await takeScreenshot(page); 276 | return { 277 | data: screenshot.data, 278 | mimeType: screenshot.mimeType, 279 | }; 280 | } 281 | if (action === "wait") { 282 | if (!wait) return "Wait parameter required for wait action"; 283 | await sleep(wait); 284 | const { screenshot } = await takeScreenshot(page); 285 | return { 286 | data: screenshot.data, 287 | mimeType: screenshot.mimeType, 288 | }; 289 | } 290 | 291 | if (action === "click") { 292 | if (!clickIndex) 293 | return "Click index parameter required for click action"; 294 | const result = await clickElementByIndex(page, clickIndex); 295 | if (typeof result === "string") return result; 296 | const { screenshot } = await viewAllClickableElements(page); 297 | return { 298 | data: screenshot.data, 299 | mimeType: screenshot.mimeType, 300 | }; 301 | } 302 | 303 | if (action === "scroll") { 304 | await scrollDownPage(page, amount); 305 | const { screenshot } = await takeScreenshot(page); 306 | return { 307 | data: screenshot.data, 308 | mimeType: screenshot.mimeType, 309 | }; 310 | } 311 | 312 | if (action === "screenshot") { 313 | const { screenshot } = await takeScreenshot(page); 314 | return { 315 | data: screenshot.data, 316 | mimeType: screenshot.mimeType, 317 | }; 318 | } 319 | 320 | throw new Error(`Unknown action: ${action}`); 321 | } catch (error) { 322 | return `Error performing browser action: ${ 323 | error instanceof Error ? error.message : String(error) 324 | }`; 325 | } 326 | }, 327 | experimental_toToolResultContent(result) { 328 | if (typeof result === "string") { 329 | return [{ type: "text", text: result }]; 330 | } 331 | if (result.text) { 332 | return [ 333 | { type: "text", text: result.text }, 334 | { 335 | type: "image", 336 | data: result.data, 337 | mimeType: result.mimeType, 338 | }, 339 | ]; 340 | } 341 | return [ 342 | { type: "image", data: result.data, mimeType: result.mimeType }, 343 | ]; 344 | }, 345 | }, 346 | }, 347 | }); 348 | 349 | result.mergeIntoDataStream(dataStream); 350 | }, 351 | onError: (error) => { 352 | // Return error message as string since that's what the type expects 353 | return error instanceof Error ? error.message : String(error); 354 | }, 355 | }); 356 | } 357 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CognosysAI/browser/b0291b2911fb1d4bede5f24dd24f7d2903e5d50e/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 240 10% 3.9%; 9 | --card: 0 0% 100%; 10 | --card-foreground: 240 10% 3.9%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 240 10% 3.9%; 13 | --primary: 240 5.9% 10%; 14 | --primary-foreground: 0 0% 98%; 15 | --secondary: 240 4.8% 95.9%; 16 | --secondary-foreground: 240 5.9% 10%; 17 | --muted: 240 4.8% 95.9%; 18 | --muted-foreground: 240 3.8% 46.1%; 19 | --accent: 240 4.8% 95.9%; 20 | --accent-foreground: 240 5.9% 10%; 21 | --destructive: 0 84.2% 60.2%; 22 | --destructive-foreground: 0 0% 98%; 23 | --border: 240 5.9% 90%; 24 | --input: 240 5.9% 90%; 25 | --ring: 240 10% 3.9%; 26 | --chart-1: 12 76% 61%; 27 | --chart-2: 173 58% 39%; 28 | --chart-3: 197 37% 24%; 29 | --chart-4: 43 74% 66%; 30 | --chart-5: 27 87% 67%; 31 | --radius: 0.5rem; 32 | } 33 | .dark { 34 | --background: 240 10% 3.9%; 35 | --foreground: 0 0% 98%; 36 | --card: 240 10% 3.9%; 37 | --card-foreground: 0 0% 98%; 38 | --popover: 240 10% 3.9%; 39 | --popover-foreground: 0 0% 98%; 40 | --primary: 0 0% 98%; 41 | --primary-foreground: 240 5.9% 10%; 42 | --secondary: 240 3.7% 15.9%; 43 | --secondary-foreground: 0 0% 98%; 44 | --muted: 240 3.7% 15.9%; 45 | --muted-foreground: 240 5% 64.9%; 46 | --accent: 240 3.7% 15.9%; 47 | --accent-foreground: 0 0% 98%; 48 | --destructive: 0 62.8% 30.6%; 49 | --destructive-foreground: 0 0% 98%; 50 | --border: 240 3.7% 15.9%; 51 | --input: 240 3.7% 15.9%; 52 | --ring: 240 4.9% 83.9%; 53 | --chart-1: 220 70% 50%; 54 | --chart-2: 160 60% 45%; 55 | --chart-3: 30 80% 55%; 56 | --chart-4: 280 65% 60%; 57 | --chart-5: 340 75% 55%; 58 | } 59 | } 60 | 61 | @layer base { 62 | * { 63 | @apply border-border; 64 | } 65 | body { 66 | @apply bg-background text-foreground; 67 | } 68 | 69 | /* @see https://github.com/shadcn-ui/ui/blob/main/apps/www/styles/globals.css */ 70 | ::-webkit-scrollbar { 71 | width: 5px; 72 | } 73 | ::-webkit-scrollbar-track { 74 | background: transparent; 75 | } 76 | ::-webkit-scrollbar-thumb { 77 | background: hsl(var(--border)); 78 | border-radius: 5px; 79 | } 80 | * { 81 | scrollbar-width: thin; 82 | scrollbar-color: hsl(var(--border)) transparent; 83 | } 84 | } 85 | 86 | @layer utilities { 87 | .container { 88 | @apply px-4 mx-auto; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import type { Metadata } from "next"; 3 | import { Geist, Geist_Mono } from "next/font/google"; 4 | import "./globals.css"; 5 | import { Analytics } from "@vercel/analytics/next"; 6 | 7 | const geistSans = Geist({ 8 | variable: "--font-geist-sans", 9 | subsets: ["latin"], 10 | }); 11 | 12 | const geistMono = Geist_Mono({ 13 | variable: "--font-geist-mono", 14 | subsets: ["latin"], 15 | }); 16 | 17 | export const metadata: Metadata = { 18 | title: "Ottogrid Browser", 19 | description: "Browser for the web.", 20 | }; 21 | 22 | interface RootLayoutProps { 23 | children: React.ReactNode; 24 | } 25 | 26 | export default function RootLayout({ children }: RootLayoutProps) { 27 | return ( 28 | 29 | 36 | {children} 37 | 38 | 39 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { Browser } from "@/components/browser"; 2 | 3 | export default function IndexPage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/browser.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { useInView } from "react-intersection-observer"; 5 | import { createAndGetSessionUrl, closeSession } from "@/actions/session"; 6 | import { Button } from "@/components/ui/button"; 7 | import { ChatInput } from "@/components/chat-input"; 8 | import { ChatPanel } from "@/components/chat-panel"; 9 | 10 | export function Browser() { 11 | const initialInputRef = React.useRef(null); 12 | const [sessionUrl, setSessionUrl] = React.useState(null); 13 | const [sessionId, setSessionId] = React.useState(null); 14 | const [isInitializing, setIsInitializing] = React.useState(false); 15 | const [initialMessage, setInitialMessage] = React.useState( 16 | null 17 | ); 18 | const [isEnding, setIsEnding] = React.useState(false); 19 | 20 | const initializeSession = async () => { 21 | if (sessionId || isInitializing) return; 22 | setIsInitializing(true); 23 | try { 24 | const { url, sessionId: id } = await createAndGetSessionUrl(); 25 | setSessionUrl(url); 26 | setSessionId(id); 27 | } catch (error) { 28 | console.error("Failed to initialize session:", error); 29 | } finally { 30 | setIsInitializing(false); 31 | } 32 | }; 33 | 34 | const handleInitialSubmit = async (value: string) => { 35 | setInitialMessage(value); 36 | await initializeSession(); 37 | }; 38 | 39 | const handleExampleClick = async (prompt: string) => { 40 | setInitialMessage(prompt); 41 | await initializeSession(); 42 | }; 43 | 44 | const handleEndSession = async () => { 45 | if (!sessionId) return; 46 | setIsEnding(true); 47 | try { 48 | await closeSession(sessionId); 49 | setSessionId(null); 50 | setSessionUrl(null); 51 | window.location.reload(); 52 | } catch (error) { 53 | console.error("Failed to end session:", error); 54 | } finally { 55 | setIsEnding(false); 56 | } 57 | }; 58 | 59 | return ( 60 |
61 |
62 |
63 | 70 |
71 |
72 | 73 |
74 | {!initialMessage ? ( 75 |
76 |
77 |

Ottogrid Browsing Agent

78 | 79 | preview 80 | 81 |
82 | 83 |
84 | 93 | 94 |
95 |
96 | 107 | 117 | 127 | 137 |
138 |
139 |
140 |
141 | ) : ( 142 | 150 | )} 151 |
152 |
153 | ); 154 | } 155 | -------------------------------------------------------------------------------- /src/components/chat-input.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { cn } from "@/lib/utils"; 5 | import { Send } from "lucide-react"; 6 | import * as React from "react"; 7 | import TextareaAutosize, { 8 | type TextareaAutosizeProps, 9 | } from "react-textarea-autosize"; 10 | 11 | interface ChatInputProps 12 | extends Omit { 13 | onValueChange?: (value: string) => void; 14 | onSubmit?: (value: string) => Promise | void; 15 | } 16 | 17 | const ChatInput = React.forwardRef( 18 | (props, forwardedRef) => { 19 | const { 20 | value, 21 | onValueChange, 22 | onSubmit: onSubmitProp, 23 | disabled, 24 | className, 25 | ...chatInputProps 26 | } = props; 27 | 28 | async function onKeyDown(event: React.KeyboardEvent) { 29 | if (event.key === "Enter" && !event.shiftKey && !disabled) { 30 | event.preventDefault(); 31 | const textarea = event.currentTarget; 32 | const value = textarea.value.trim(); 33 | if (!value) return; 34 | 35 | if (onSubmitProp) { 36 | await onSubmitProp(value); 37 | if (onValueChange) onValueChange(""); 38 | } 39 | } 40 | } 41 | 42 | async function onSubmit() { 43 | if (disabled) return; 44 | const textValue = typeof value === "string" ? value.trim() : ""; 45 | if (!textValue) return; 46 | 47 | if (onSubmitProp) { 48 | await onSubmitProp(textValue); 49 | if (onValueChange) onValueChange(""); 50 | } 51 | } 52 | 53 | return ( 54 |
55 | onValueChange?.(event.target.value)} 64 | onKeyDown={onKeyDown} 65 | disabled={disabled} 66 | {...chatInputProps} 67 | /> 68 | 76 |
77 | ); 78 | }, 79 | ); 80 | 81 | export { ChatInput }; 82 | -------------------------------------------------------------------------------- /src/components/chat-message.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | import type { Message } from "ai"; 5 | import { 6 | CheckCircle2, 7 | Loader2, 8 | Mouse, 9 | Navigation, 10 | ScrollText, 11 | Search, 12 | } from "lucide-react"; 13 | import { motion } from "motion/react"; 14 | 15 | function getToolIcon(toolName: string) { 16 | switch (toolName) { 17 | case "searchGoogle": 18 | return ; 19 | case "navigate": 20 | return ; 21 | case "browserAction": 22 | return ; 23 | case "viewAllClickableElements": 24 | return ; 25 | default: 26 | return null; 27 | } 28 | } 29 | 30 | interface ChatMessageProps { 31 | message: Message & { 32 | status?: { 33 | type: string; 34 | content: string; 35 | step: number; 36 | }; 37 | }; 38 | } 39 | 40 | function getStatusIcon(type: string) { 41 | switch (type) { 42 | case "status": 43 | return ; 44 | case "step": 45 | return ; 46 | default: 47 | return null; 48 | } 49 | } 50 | 51 | export function ChatMessage({ message }: ChatMessageProps) { 52 | return ( 53 | 61 |
69 |
70 | {message.content} 71 |
72 | {/* {message.role === "assistant" && message.status && ( 73 | 78 | 83 | {getStatusIcon(message.status.type)} 84 | 85 | {message.status.content === "initialized" 86 | ? "Initializing..." 87 | : message.status.content === "step_complete" 88 | ? `Step ${message.status.step} completed` 89 | : message.status.content === "finished" 90 | ? "Finished" 91 | : message.status.content} 92 | 93 | 94 | 95 | )} */} 96 | {message.role === "assistant" && 97 | message.toolInvocations && 98 | message.toolInvocations.length > 0 && ( 99 | 104 | {message.toolInvocations.map((toolInvocation) => { 105 | const { toolName, toolCallId, args, state } = toolInvocation; 106 | 107 | return ( 108 | 114 |
115 |
116 | {getToolIcon(toolName)} 117 |
118 | 119 | {toolName 120 | .replace(/([A-Z])/g, " $1") 121 | .replace(/^./, (str) => str.toUpperCase())} 122 | 123 | {state === "partial-call" && ( 124 | 125 | )} 126 |
127 |
128 | ); 129 | })} 130 |
131 | )} 132 |
133 |
134 | ); 135 | } 136 | -------------------------------------------------------------------------------- /src/components/chat-panel.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { useChat } from "ai/react"; 5 | import { useInView } from "react-intersection-observer"; 6 | import { ChatMessage } from "@/components/chat-message"; 7 | import { ChatInput } from "@/components/chat-input"; 8 | import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"; 9 | import { useIsMobile } from "@/lib/use-mobile"; 10 | 11 | interface ChatPanelProps { 12 | sessionId: string | null; 13 | sessionUrl: string | null; 14 | initialMessage: string | null; 15 | onEndSession: () => void; 16 | isEnding: boolean; 17 | isInitializing: boolean; 18 | } 19 | 20 | export function ChatPanel({ 21 | sessionId, 22 | sessionUrl, 23 | initialMessage, 24 | onEndSession, 25 | isEnding, 26 | isInitializing, 27 | }: ChatPanelProps) { 28 | const scrollRef = React.useRef(null); 29 | const chatInputRef = React.useRef(null); 30 | const [shouldAutoScroll, setShouldAutoScroll] = React.useState(true); 31 | 32 | const { messages, input, setInput, handleSubmit, isLoading, data, append } = 33 | useChat({ 34 | body: { sessionId }, 35 | id: sessionId || undefined, 36 | }); 37 | 38 | const [inViewRef, inView] = useInView({ threshold: 0 }); 39 | 40 | const composedScrollRef = React.useCallback( 41 | (node: HTMLDivElement | null) => { 42 | scrollRef.current = node; 43 | inViewRef(node); 44 | }, 45 | [inViewRef] 46 | ); 47 | 48 | const isMobile = useIsMobile(); 49 | 50 | // Merge `data` into the last assistant message as a status, if any 51 | const messagesWithStatus = React.useMemo(() => { 52 | if (!data || !messages) return messages; 53 | 54 | const lastData = data[data.length - 1]; 55 | if (!lastData) return messages; 56 | 57 | const lastMessage = messages[messages.length - 1]; 58 | if (!lastMessage || lastMessage.role !== "assistant") return messages; 59 | 60 | return messages.map((message, index) => { 61 | if (index === messages.length - 1 && message.role === "assistant") { 62 | return { ...message, status: lastData }; 63 | } 64 | return message; 65 | }); 66 | }, [messages, data]); 67 | 68 | // Send initial message when component mounts 69 | React.useEffect(() => { 70 | if (initialMessage && sessionId) { 71 | append({ 72 | content: initialMessage, 73 | role: "user", 74 | }); 75 | } 76 | }, [sessionId, initialMessage, append]); 77 | 78 | React.useEffect(() => { 79 | setShouldAutoScroll(inView); 80 | }, [inView]); 81 | 82 | React.useEffect(() => { 83 | if (shouldAutoScroll) { 84 | scrollRef.current?.scrollIntoView({ behavior: "smooth" }); 85 | } 86 | }, [messages, shouldAutoScroll]); 87 | 88 | return ( 89 | 90 | 95 |
96 |

Chat Feed

97 |
98 | {isInitializing ? ( 99 |
100 | Initializing browser session... 101 |
102 | ) : ( 103 | <> 104 | {messagesWithStatus?.map((message) => ( 105 | 106 | ))} 107 |
108 | 109 | )} 110 |
111 | 112 |
113 | handleSubmit(new Event("submit"))} 121 | disabled={isLoading || isInitializing || !sessionId} 122 | /> 123 |
124 |
125 | 126 | 127 | 132 |
133 |
134 |

Browser Panel

135 | 142 |
143 |
148 | {isInitializing ? ( 149 |
150 |

Loading browser...

151 |
152 | ) : !sessionUrl ? ( 153 |
154 |

Initializing

155 |
156 | ) : ( 157 |