diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 400723b..802b640 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,7 +14,7 @@ Purpose: Help agents operate the benchmark harness quickly. Stay repo-specific; 3. Prompts load from `suites//prompts//-*.md`; absence logs a warning and skips the agent. 4. Scenario `validation.commands` run in install→test→lint→typecheck order via `runValidationCommands`, capturing exit codes and logs. 5. `buildDiffArtifacts` compares workspace vs fixture to populate `diff_summary` and package deltas. -6. `runEvaluators` applies current metrics, then `computeWeightedTotals` rescales weighted averages to a 0–10 score before writing `results/summary.json`. +6. `runEvaluators` applies current metrics, then `computeWeightedTotals` rescales weighted averages to a 0–10 score before saving to `benchmarks.db`. ### Evaluators & scoring (all in `packages/evaluators/src/evaluators/`) - `InstallEvaluator`: expects an install command result with exit code 0. @@ -43,7 +43,7 @@ npm -w packages/harness run build node packages/harness/dist/cli.js run update-deps nx-pnpm-monorepo --tier L1 --agent claude-code --model sonnet --max-turns 15 ``` - `npm -w packages/harness run dev` launches `tsc --watch` while editing the CLI. -- Each `run` overwrites `results/summary.json`; archive outputs manually if you need history. +- All benchmark results are stored in `benchmarks.db` with full history. ### Guardrails & troubleshooting - Work inside the generated workspace (`results/workspaces/...`), not the repository root; evaluators read only that directory. diff --git a/.gitignore b/.gitignore index 1540694..6943476 100644 --- a/.gitignore +++ b/.gitignore @@ -104,10 +104,8 @@ dist .idea -results/summary.json results/**/* results/workspaces/**/* -results/summary.json # Snapshot files snapshot.json5 diff --git a/benchmark-report/pnpm-lock.yaml b/benchmark-report/pnpm-lock.yaml new file mode 100644 index 0000000..3827ab6 --- /dev/null +++ b/benchmark-report/pnpm-lock.yaml @@ -0,0 +1,4539 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@radix-ui/react-accordion': + specifier: ^1.2.12 + version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-dialog': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-progress': + specifier: ^1.1.7 + version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-separator': + specifier: ^1.1.7 + version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.4(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-tooltip': + specifier: ^1.2.8 + version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@rsbuild/plugin-node-polyfill': + specifier: ^1.4.2 + version: 1.4.2(@rsbuild/core@1.6.6) + '@tailwindcss/postcss': + specifier: ^4.1.16 + version: 4.1.17 + '@tanstack/react-router': + specifier: ^1.133.25 + version: 1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@tanstack/router-devtools': + specifier: ^1.133.25 + version: 1.135.2(@tanstack/react-router@1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.135.2)(@types/node@24.10.1)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.10)(tiny-invariant@1.3.3)(tsx@4.20.6) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.546.0 + version: 0.546.0(react@19.2.0) + postcss: + specifier: ^8.5.6 + version: 8.5.6 + react: + specifier: ^19.2.0 + version: 19.2.0 + react-dom: + specifier: ^19.2.0 + version: 19.2.0(react@19.2.0) + recharts: + specifier: 2.15.4 + version: 2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + sql.js: + specifier: ^1.13.0 + version: 1.13.0 + tailwind-merge: + specifier: ^3.3.1 + version: 3.4.0 + tailwindcss: + specifier: ^4.1.16 + version: 4.1.17 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + devDependencies: + '@rsbuild/core': + specifier: ^1.5.14 + version: 1.6.6 + '@rsbuild/plugin-react': + specifier: ^1.4.1 + version: 1.4.2(@rsbuild/core@1.6.6) + '@tanstack/router-plugin': + specifier: ^1.133.25 + version: 1.135.2(@rsbuild/core@1.6.6)(@tanstack/react-router@1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) + '@types/react': + specifier: ^19.2.2 + version: 19.2.4 + '@types/react-dom': + specifier: ^19.2.1 + version: 19.2.3(@types/react@19.2.4) + '@types/sql.js': + specifier: ^1.4.9 + version: 1.4.9 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.5': + resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.5': + resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.7.0': + resolution: {integrity: sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==} + + '@emnapi/runtime@1.7.0': + resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@module-federation/error-codes@0.21.4': + resolution: {integrity: sha512-ClpL5MereWNXh+EgDjz7w4RrC1JlisQTvXDa1gLxpviHafzNDfdViVmuhi9xXVuj+EYo8KU70Y999KHhk9424Q==} + + '@module-federation/runtime-core@0.21.4': + resolution: {integrity: sha512-SGpmoOLGNxZofpTOk6Lxb2ewaoz5wMi93AFYuuJB04HTVcngEK+baNeUZ2D/xewrqNIJoMY6f5maUjVfIIBPUA==} + + '@module-federation/runtime-tools@0.21.4': + resolution: {integrity: sha512-RzFKaL0DIjSmkn76KZRfzfB6dD07cvID84950jlNQgdyoQFUGkqD80L6rIpVCJTY/R7LzR3aQjHnoqmq4JPo3w==} + + '@module-federation/runtime@0.21.4': + resolution: {integrity: sha512-wgvGqryurVEvkicufJmTG0ZehynCeNLklv8kIk5BLIsWYSddZAE+xe4xov1kgH5fIJQAoQNkRauFFjVNlHoAkA==} + + '@module-federation/sdk@0.21.4': + resolution: {integrity: sha512-tzvhOh/oAfX++6zCDDxuvioHY4Jurf8vcfoCbKFxusjmyKr32GPbwFDazUP+OPhYCc3dvaa9oWU6X/qpUBLfJw==} + + '@module-federation/webpack-bundler-runtime@0.21.4': + resolution: {integrity: sha512-dusmR3uPnQh9u9ChQo3M+GLOuGFthfvnh7WitF/a1eoeTfRmXqnMFsXtZCUK+f/uXf+64874Zj/bhAgbBcVHZA==} + + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-accordion@1.2.12': + resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.12': + resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.3': + resolution: {integrity: sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.4': + resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-progress@1.1.8': + resolution: {integrity: sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-separator@1.1.8': + resolution: {integrity: sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@rollup/rollup-android-arm-eabi@4.53.2': + resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.2': + resolution: {integrity: sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.2': + resolution: {integrity: sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.2': + resolution: {integrity: sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.2': + resolution: {integrity: sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.2': + resolution: {integrity: sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.2': + resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.2': + resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.2': + resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.2': + resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.2': + resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.2': + resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.2': + resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.2': + resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.2': + resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.2': + resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.2': + resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.2': + resolution: {integrity: sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.2': + resolution: {integrity: sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.2': + resolution: {integrity: sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.2': + resolution: {integrity: sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==} + cpu: [x64] + os: [win32] + + '@rsbuild/core@1.6.6': + resolution: {integrity: sha512-QE1MvRFKDeeQUAwZrCPhEHgvy/XieYQj0aPho1SkkL/M4ruonp/p8ymhUJZE5wFQxIhBHaOvE2gwKnME0XQgKg==} + engines: {node: '>=18.12.0'} + hasBin: true + + '@rsbuild/plugin-node-polyfill@1.4.2': + resolution: {integrity: sha512-Vq1So9ZQa0nz9scgDfAa/t7bLPl7lYBQw6n3Qhd8hGUpPXacSjr0xLdJ2c20EDihOO4qJKN4zlomBYVjCHPy0A==} + peerDependencies: + '@rsbuild/core': 1.x + peerDependenciesMeta: + '@rsbuild/core': + optional: true + + '@rsbuild/plugin-react@1.4.2': + resolution: {integrity: sha512-2rJb5mOuqVof2aDq4SbB1E65+0n1vjhAADipC88jvZRNuTOulg79fh7R4tsCiBMI4VWq46gSpwekiK8G5bq6jg==} + peerDependencies: + '@rsbuild/core': 1.x + + '@rspack/binding-darwin-arm64@1.6.3': + resolution: {integrity: sha512-GxjrB5RhxlEoX3uoWtzNPcINPOn6hzqhn00Y164gofwQ6KgvtEJU7DeYXgCq4TQDD1aQbF/lsV1wpzb2LMkQdg==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-x64@1.6.3': + resolution: {integrity: sha512-X6TEPwc+FeApTgnzBefc/viuUP7LkqTY1GxltRYuabs8E7bExlmYoyB8KhIlC66NWtgjmcNWvZIkUlr9ZalBkQ==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-linux-arm64-gnu@1.6.3': + resolution: {integrity: sha512-uid2GjLzRnYNzNuTTS/hUZdYO6bNATWfaeuhGBU8RWrRgB+clJwhZskSwhfVrvmyTXYbHI95CJIPt4TbZ1FRTg==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-musl@1.6.3': + resolution: {integrity: sha512-ZJqqyEARBAnv9Gj3+0/PGIw87r8Vg0ZEKiRT9u5tPKK01dptF+xGv4xywAlahOeFUik4Dni5aHixbarStzN9Cw==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-x64-gnu@1.6.3': + resolution: {integrity: sha512-/W8/X3CBGVY7plii5eUzyIEyCKiYx1lqrSVuD1HLlVHvzC4H2Kpk0EwvY2gUhnQRLU0Ym77Sh4PRd1ZOOzP4LQ==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@1.6.3': + resolution: {integrity: sha512-h0Q3aM0fkRCd330DfRGZ9O3nk/rfRyXRX4dEIoLcLAq34VOmp3HZUP7rEy7feiJbuU4Atcvd0MD7U6RLwa1umQ==} + cpu: [x64] + os: [linux] + + '@rspack/binding-wasm32-wasi@1.6.3': + resolution: {integrity: sha512-XLCDe+b52kAajlHutsyfh9o+uKQvgis+rLFb3XIJ9FfCcL8opTWVyeGLNHBUBn7cGPXGEYWd0EU9CZJrjV+iVw==} + cpu: [wasm32] + + '@rspack/binding-win32-arm64-msvc@1.6.3': + resolution: {integrity: sha512-BU3VjyzAf8noYqb7NPuUZu9VVHRH2b+x4Q5A2oqQwEq4JzW/Mrhcd//vnRpSE9HHuezxTpQTtSSsB/YqV7BkDg==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@1.6.3': + resolution: {integrity: sha512-W2yHUFra9N8QbBKQC6PcyOwOJbj8qrmechK97XVQAwo0GWGnQKMphivJrbxHOxCz89FGn9kLGRakTH04bHT4MQ==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-x64-msvc@1.6.3': + resolution: {integrity: sha512-mxep+BqhySoWweQSXnUaYAHx+C8IzOTNMJYuAVchXn9bMG6SPAXvZqAF8X/Q+kNg8X7won8Sjz+O+OUw3OTyOQ==} + cpu: [x64] + os: [win32] + + '@rspack/binding@1.6.3': + resolution: {integrity: sha512-liRgxMjHWDL225c41pH4ZcFtPN48LM0+St3iylwavF5JFSqBv86R/Cv5+M+WLrhcihCQsxDwBofipyosJIFmmA==} + + '@rspack/core@1.6.3': + resolution: {integrity: sha512-03pyxRtpZ9SNwuA4XHLcFG/jmmWqSd4NaXQGrwOHU0UoPKpVPTqkxtQYZLCfeNtDfAA9v2KPqgJ3b40x8nJGeA==} + engines: {node: '>=18.12.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@rspack/lite-tapable@1.1.0': + resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} + + '@rspack/plugin-react-refresh@1.5.3': + resolution: {integrity: sha512-VOnQMf3YOHkTqJ0+BJbrYga4tQAWNwoAnkgwRauXB4HOyCc5wLfBs9DcOFla/2usnRT3Sq6CMVhXmdPobwAoTA==} + peerDependencies: + react-refresh: '>=0.10.0 <1.0.0' + webpack-hot-middleware: 2.x + peerDependenciesMeta: + webpack-hot-middleware: + optional: true + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@tailwindcss/node@4.1.17': + resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} + + '@tailwindcss/oxide-android-arm64@4.1.17': + resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.17': + resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.17': + resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.17': + resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.17': + resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.17': + resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.17': + resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.17': + resolution: {integrity: sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==} + + '@tanstack/history@1.133.28': + resolution: {integrity: sha512-B7+x7eP2FFvi3fgd3rNH9o/Eixt+pp0zCIdGhnQbAJjFrlwIKGjGnwyJjhWJ5fMQlGks/E2LdDTqEV4W9Plx7g==} + engines: {node: '>=12'} + + '@tanstack/react-router-devtools@1.135.2': + resolution: {integrity: sha512-8nG+twPfOvjaknnzLTTvnsXART9s6fQbY+Yj4nnNVOcF0FiUuK7TgJJQMKWHsmNa47X3fV1GZCTQV4cWhqKY0w==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': ^1.135.2 + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-router@1.135.2': + resolution: {integrity: sha512-IzvCJ5bZ4dTEh65J1NrILF3Ab+ajRgsHYQYl/3du1sptRfQkUSsRYQGXffQQU3JH++plmO/tJXtRTmgrAp4inA==} + engines: {node: '>=12'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-store@0.8.0': + resolution: {integrity: sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.135.2': + resolution: {integrity: sha512-fhJSGmbqE78Ou6e+cnJ9exmjCzCZ9IXT2rApiPAgeItKj2yy1qmTEoR11n0x0fiNkkBxHL1us+QyG8JfNELiQA==} + engines: {node: '>=12'} + + '@tanstack/router-devtools-core@1.135.2': + resolution: {integrity: sha512-VmLyG7M8rYyA4jleCBpwYc+bjODAfWIQfBZt/16/c8Fg2K6eeMuX5lMGXYWPZT6BNV4ylv+JrSmOX3WUhDRQeQ==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/router-core': ^1.135.2 + csstype: ^3.0.10 + solid-js: '>=1.9.5' + tiny-invariant: ^1.3.3 + peerDependenciesMeta: + csstype: + optional: true + + '@tanstack/router-devtools@1.135.2': + resolution: {integrity: sha512-MrEuKIyQvMNYaqjOfM2tt3onsvBIVRCZ48I9KUaD8YiYxFJo7hHFkGuGkftuCdoBD8MyJmC4KcwGTe9KaiqunQ==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': ^1.135.2 + csstype: ^3.0.10 + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + peerDependenciesMeta: + csstype: + optional: true + + '@tanstack/router-generator@1.135.2': + resolution: {integrity: sha512-YaTr1qrV2ysSllKu9FjCjaSjRFiX6SLKVGkQLJJ+SzoCsMco+zqhmtBjiw3YHC0jWBRs21iQieBzNR/PvT7JkA==} + engines: {node: '>=12'} + + '@tanstack/router-plugin@1.135.2': + resolution: {integrity: sha512-iB//HEGIX7Rn4390O4xM3+5LMSmtphRoCPoq3jpE6dGnAIPWEJJ/O1r95OR1LFAe5MhdciJPhsNgYHCIj+PeZw==} + engines: {node: '>=12'} + peerDependencies: + '@rsbuild/core': '>=1.0.2' + '@tanstack/react-router': ^1.135.2 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' + vite-plugin-solid: ^2.11.10 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.133.19': + resolution: {integrity: sha512-WEp5D2gPxvlLDRXwD/fV7RXjYtqaqJNXKB/L6OyZEbT+9BG/Ib2d7oG9GSUZNNMGPGYAlhBUOi3xutySsk6rxA==} + engines: {node: '>=12'} + + '@tanstack/store@0.8.0': + resolution: {integrity: sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ==} + + '@tanstack/virtual-file-routes@1.133.19': + resolution: {integrity: sha512-IKwZENsK7owmW1Lm5FhuHegY/SyQ8KqtL/7mTSnzoKJgfzhrrf9qwKB1rmkKkt+svUuy/Zw3uVEpZtUzQruWtA==} + engines: {node: '>=12'} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/emscripten@1.41.5': + resolution: {integrity: sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.4': + resolution: {integrity: sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A==} + + '@types/sql.js@1.4.9': + resolution: {integrity: sha512-ep8b36RKHlgWPqjNG9ToUrPiwkhwh0AEzy883mO5Xnd+cL6VBH1EvSjBAAuxLUFF2Vn/moE3Me6v9E1Lo+48GQ==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + + assert@2.1.0: + resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + babel-dead-code-elimination@1.0.10: + resolution: {integrity: sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.8.28: + resolution: {integrity: sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ==} + hasBin: true + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bn.js@4.12.2: + resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} + + bn.js@5.2.2: + resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.1: + resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} + engines: {node: '>= 0.10'} + + browserify-sign@4.2.5: + resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} + engines: {node: '>= 0.10'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserslist@4.28.0: + resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001754: + resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + cipher-base@1.0.7: + resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} + engines: {node: '>= 0.10'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@2.0.0: + resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + + core-js@3.46.0: + resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + + crypto-browserify@3.12.1: + resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} + engines: {node: '>= 0.10'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} + + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + domain-browser@5.7.0: + resolution: {integrity: sha512-edTFu0M/7wO1pXY6GDxVNVW086uqwWYIHP98txhcPyV995X21JIH2DtYp33sQJOupYoXKe9RwTw2Ya2vWaquTQ==} + engines: {node: '>=4'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.250: + resolution: {integrity: sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==} + + elliptic@6.6.1: + resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + + fast-equals@5.3.3: + resolution: {integrity: sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==} + engines: {node: '>=6.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + goober@2.1.18: + resolution: {integrity: sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==} + peerDependencies: + csstype: ^3.0.10 + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hash-base@3.0.5: + resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} + engines: {node: '>= 0.10'} + + hash-base@3.1.2: + resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} + engines: {node: '>= 0.8'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-nan@1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isbot@5.1.32: + resolution: {integrity: sha512-VNfjM73zz2IBZmdShMfAUg10prm6t7HFUQmNAEOAVS4YH92ZrZcvkMcGX6cIgBJAzWDzPent/EeAtYEHNPNPBQ==} + engines: {node: '>=18'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@0.546.0: + resolution: {integrity: sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parse-asn1@5.1.9: + resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} + engines: {node: '>= 0.10'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pbkdf2@3.1.5: + resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==} + engines: {node: '>= 0.10'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + peerDependencies: + react: ^19.2.0 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.1: + resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-smooth@4.0.4: + resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + engines: {node: '>=0.10.0'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + + recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + + recharts@2.15.4: + resolution: {integrity: sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + ripemd160@2.0.3: + resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} + engines: {node: '>= 0.8'} + + rollup@4.53.2: + resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + seroval-plugins@1.3.3: + resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.3.2: + resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} + engines: {node: '>=10'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + solid-js@1.9.10: + resolution: {integrity: sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + sql.js@1.13.0: + resolution: {integrity: sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA==} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + tailwind-merge@3.4.0: + resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} + + tailwindcss@4.1.17: + resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} + engines: {node: '>=18.0.0'} + hasBin: true + + tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + unplugin@2.3.10: + resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} + engines: {node: '>=18.12.0'} + + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + victory-vendor@36.9.2: + resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + + vite@7.2.2: + resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.5 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@emnapi/core@1.7.0': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.7.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + + '@floating-ui/utils@0.2.10': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@module-federation/error-codes@0.21.4': {} + + '@module-federation/runtime-core@0.21.4': + dependencies: + '@module-federation/error-codes': 0.21.4 + '@module-federation/sdk': 0.21.4 + + '@module-federation/runtime-tools@0.21.4': + dependencies: + '@module-federation/runtime': 0.21.4 + '@module-federation/webpack-bundler-runtime': 0.21.4 + + '@module-federation/runtime@0.21.4': + dependencies: + '@module-federation/error-codes': 0.21.4 + '@module-federation/runtime-core': 0.21.4 + '@module-federation/sdk': 0.21.4 + + '@module-federation/sdk@0.21.4': {} + + '@module-federation/webpack-bundler-runtime@0.21.4': + dependencies: + '@module-federation/runtime': 0.21.4 + '@module-federation/sdk': 0.21.4 + + '@napi-rs/wasm-runtime@1.0.7': + dependencies: + '@emnapi/core': 1.7.0 + '@emnapi/runtime': 1.7.0 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.4)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-context@1.1.2(@types/react@19.2.4)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-context@1.1.3(@types/react@19.2.4)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + aria-hidden: 1.2.6 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.7.1(@types/react@19.2.4)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-direction@1.1.1(@types/react@19.2.4)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.4)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-id@1.1.1(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/rect': 1.1.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-progress@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-context': 1.1.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-slot@1.2.3(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-slot@1.2.4(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.4)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.4)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/rect@1.1.1': {} + + '@rollup/rollup-android-arm-eabi@4.53.2': + optional: true + + '@rollup/rollup-android-arm64@4.53.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.2': + optional: true + + '@rollup/rollup-darwin-x64@4.53.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.2': + optional: true + + '@rsbuild/core@1.6.6': + dependencies: + '@rspack/core': 1.6.3(@swc/helpers@0.5.17) + '@rspack/lite-tapable': 1.1.0 + '@swc/helpers': 0.5.17 + core-js: 3.46.0 + jiti: 2.6.1 + + '@rsbuild/plugin-node-polyfill@1.4.2(@rsbuild/core@1.6.6)': + dependencies: + assert: 2.1.0 + browserify-zlib: 0.2.0 + buffer: 5.7.1 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + crypto-browserify: 3.12.1 + domain-browser: 5.7.0 + events: 3.3.0 + https-browserify: 1.0.0 + os-browserify: 0.3.0 + path-browserify: 1.0.1 + process: 0.11.10 + punycode: 2.3.1 + querystring-es3: 0.2.1 + readable-stream: 4.7.0 + stream-browserify: 3.0.0 + stream-http: 3.2.0 + string_decoder: 1.3.0 + timers-browserify: 2.0.12 + tty-browserify: 0.0.1 + url: 0.11.4 + util: 0.12.5 + vm-browserify: 1.1.2 + optionalDependencies: + '@rsbuild/core': 1.6.6 + + '@rsbuild/plugin-react@1.4.2(@rsbuild/core@1.6.6)': + dependencies: + '@rsbuild/core': 1.6.6 + '@rspack/plugin-react-refresh': 1.5.3(react-refresh@0.18.0) + react-refresh: 0.18.0 + transitivePeerDependencies: + - webpack-hot-middleware + + '@rspack/binding-darwin-arm64@1.6.3': + optional: true + + '@rspack/binding-darwin-x64@1.6.3': + optional: true + + '@rspack/binding-linux-arm64-gnu@1.6.3': + optional: true + + '@rspack/binding-linux-arm64-musl@1.6.3': + optional: true + + '@rspack/binding-linux-x64-gnu@1.6.3': + optional: true + + '@rspack/binding-linux-x64-musl@1.6.3': + optional: true + + '@rspack/binding-wasm32-wasi@1.6.3': + dependencies: + '@napi-rs/wasm-runtime': 1.0.7 + optional: true + + '@rspack/binding-win32-arm64-msvc@1.6.3': + optional: true + + '@rspack/binding-win32-ia32-msvc@1.6.3': + optional: true + + '@rspack/binding-win32-x64-msvc@1.6.3': + optional: true + + '@rspack/binding@1.6.3': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.6.3 + '@rspack/binding-darwin-x64': 1.6.3 + '@rspack/binding-linux-arm64-gnu': 1.6.3 + '@rspack/binding-linux-arm64-musl': 1.6.3 + '@rspack/binding-linux-x64-gnu': 1.6.3 + '@rspack/binding-linux-x64-musl': 1.6.3 + '@rspack/binding-wasm32-wasi': 1.6.3 + '@rspack/binding-win32-arm64-msvc': 1.6.3 + '@rspack/binding-win32-ia32-msvc': 1.6.3 + '@rspack/binding-win32-x64-msvc': 1.6.3 + + '@rspack/core@1.6.3(@swc/helpers@0.5.17)': + dependencies: + '@module-federation/runtime-tools': 0.21.4 + '@rspack/binding': 1.6.3 + '@rspack/lite-tapable': 1.1.0 + optionalDependencies: + '@swc/helpers': 0.5.17 + + '@rspack/lite-tapable@1.1.0': {} + + '@rspack/plugin-react-refresh@1.5.3(react-refresh@0.18.0)': + dependencies: + error-stack-parser: 2.1.4 + html-entities: 2.6.0 + react-refresh: 0.18.0 + + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.1.17': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.17 + + '@tailwindcss/oxide-android-arm64@4.1.17': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.17': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.17': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.17': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.17': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + optional: true + + '@tailwindcss/oxide@4.1.17': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-x64': 4.1.17 + '@tailwindcss/oxide-freebsd-x64': 4.1.17 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-x64-musl': 4.1.17 + '@tailwindcss/oxide-wasm32-wasi': 4.1.17 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 + + '@tailwindcss/postcss@4.1.17': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.17 + '@tailwindcss/oxide': 4.1.17 + postcss: 8.5.6 + tailwindcss: 4.1.17 + + '@tanstack/history@1.133.28': {} + + '@tanstack/react-router-devtools@1.135.2(@tanstack/react-router@1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.135.2)(@types/node@24.10.1)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.10)(tiny-invariant@1.3.3)(tsx@4.20.6)': + dependencies: + '@tanstack/react-router': 1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@tanstack/router-devtools-core': 1.135.2(@tanstack/router-core@1.135.2)(@types/node@24.10.1)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.10)(tiny-invariant@1.3.3)(tsx@4.20.6) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) + transitivePeerDependencies: + - '@tanstack/router-core' + - '@types/node' + - csstype + - jiti + - less + - lightningcss + - sass + - sass-embedded + - solid-js + - stylus + - sugarss + - terser + - tiny-invariant + - tsx + - yaml + + '@tanstack/react-router@1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@tanstack/history': 1.133.28 + '@tanstack/react-store': 0.8.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@tanstack/router-core': 1.135.2 + isbot: 5.1.32 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/react-store@0.8.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@tanstack/store': 0.8.0 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + use-sync-external-store: 1.6.0(react@19.2.0) + + '@tanstack/router-core@1.135.2': + dependencies: + '@tanstack/history': 1.133.28 + '@tanstack/store': 0.8.0 + cookie-es: 2.0.0 + seroval: 1.3.2 + seroval-plugins: 1.3.3(seroval@1.3.2) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/router-devtools-core@1.135.2(@tanstack/router-core@1.135.2)(@types/node@24.10.1)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.10)(tiny-invariant@1.3.3)(tsx@4.20.6)': + dependencies: + '@tanstack/router-core': 1.135.2 + clsx: 2.1.1 + goober: 2.1.18(csstype@3.1.3) + solid-js: 1.9.10 + tiny-invariant: 1.3.3 + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) + optionalDependencies: + csstype: 3.1.3 + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + '@tanstack/router-devtools@1.135.2(@tanstack/react-router@1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.135.2)(@types/node@24.10.1)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.10)(tiny-invariant@1.3.3)(tsx@4.20.6)': + dependencies: + '@tanstack/react-router': 1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@tanstack/react-router-devtools': 1.135.2(@tanstack/react-router@1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.135.2)(@types/node@24.10.1)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.10)(tiny-invariant@1.3.3)(tsx@4.20.6) + clsx: 2.1.1 + goober: 2.1.18(csstype@3.1.3) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) + optionalDependencies: + csstype: 3.1.3 + transitivePeerDependencies: + - '@tanstack/router-core' + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - solid-js + - stylus + - sugarss + - terser + - tiny-invariant + - tsx + - yaml + + '@tanstack/router-generator@1.135.2': + dependencies: + '@tanstack/router-core': 1.135.2 + '@tanstack/router-utils': 1.133.19 + '@tanstack/virtual-file-routes': 1.133.19 + prettier: 3.6.2 + recast: 0.23.11 + source-map: 0.7.6 + tsx: 4.20.6 + zod: 3.25.76 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.135.2(@rsbuild/core@1.6.6)(@tanstack/react-router@1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@tanstack/router-core': 1.135.2 + '@tanstack/router-generator': 1.135.2 + '@tanstack/router-utils': 1.133.19 + '@tanstack/virtual-file-routes': 1.133.19 + babel-dead-code-elimination: 1.0.10 + chokidar: 3.6.0 + unplugin: 2.3.10 + zod: 3.25.76 + optionalDependencies: + '@rsbuild/core': 1.6.6 + '@tanstack/react-router': 1.135.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) + transitivePeerDependencies: + - supports-color + + '@tanstack/router-utils@1.133.19': + dependencies: + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) + ansis: 4.2.0 + diff: 8.0.2 + pathe: 2.0.3 + tinyglobby: 0.2.15 + transitivePeerDependencies: + - supports-color + + '@tanstack/store@0.8.0': {} + + '@tanstack/virtual-file-routes@1.133.19': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/d3-array@3.2.2': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/emscripten@1.41.5': {} + + '@types/estree@1.0.8': {} + + '@types/node@24.10.1': + dependencies: + undici-types: 7.16.0 + + '@types/react-dom@19.2.3(@types/react@19.2.4)': + dependencies: + '@types/react': 19.2.4 + + '@types/react@19.2.4': + dependencies: + csstype: 3.1.3 + + '@types/sql.js@1.4.9': + dependencies: + '@types/emscripten': 1.41.5 + '@types/node': 24.10.1 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn@8.15.0: {} + + ansis@4.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + asn1.js@4.10.1: + dependencies: + bn.js: 4.12.2 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + assert@2.1.0: + dependencies: + call-bind: 1.0.8 + is-nan: 1.3.2 + object-is: 1.1.6 + object.assign: 4.1.7 + util: 0.12.5 + + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + babel-dead-code-elimination@1.0.10: + dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.8.28: {} + + binary-extensions@2.3.0: {} + + bn.js@4.12.2: {} + + bn.js@5.2.2: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brorand@1.1.0: {} + + browserify-aes@1.2.0: + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.7 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-cipher@1.0.1: + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + + browserify-des@1.0.2: + dependencies: + cipher-base: 1.0.7 + des.js: 1.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-rsa@4.1.1: + dependencies: + bn.js: 5.2.2 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + browserify-sign@4.2.5: + dependencies: + bn.js: 5.2.2 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.6.1 + inherits: 2.0.4 + parse-asn1: 5.1.9 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + + browserslist@4.28.0: + dependencies: + baseline-browser-mapping: 2.8.28 + caniuse-lite: 1.0.30001754 + electron-to-chromium: 1.5.250 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.28.0) + + buffer-xor@1.0.3: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + builtin-status-codes@3.0.0: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + caniuse-lite@1.0.30001754: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + cipher-base@1.0.7: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clsx@2.1.1: {} + + console-browserify@1.2.0: {} + + constants-browserify@1.0.0: {} + + convert-source-map@2.0.0: {} + + cookie-es@2.0.0: {} + + core-js@3.46.0: {} + + core-util-is@1.0.3: {} + + create-ecdh@4.0.4: + dependencies: + bn.js: 4.12.2 + elliptic: 6.6.1 + + create-hash@1.2.0: + dependencies: + cipher-base: 1.0.7 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.3 + sha.js: 2.4.12 + + create-hmac@1.1.7: + dependencies: + cipher-base: 1.0.7 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.3 + safe-buffer: 5.2.1 + sha.js: 2.4.12 + + crypto-browserify@3.12.1: + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.5 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + hash-base: 3.0.5 + inherits: 2.0.4 + pbkdf2: 3.1.5 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + + csstype@3.1.3: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.0: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js-light@2.5.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + diff@8.0.2: {} + + diffie-hellman@5.0.3: + dependencies: + bn.js: 4.12.2 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.28.4 + csstype: 3.1.3 + + domain-browser@5.7.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.250: {} + + elliptic@6.6.1: + dependencies: + bn.js: 4.12.2 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escalade@3.2.0: {} + + esprima@4.0.1: {} + + event-target-shim@5.0.1: {} + + eventemitter3@4.0.7: {} + + events@3.3.0: {} + + evp_bytestokey@1.0.3: + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + + fast-equals@5.3.3: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + goober@2.1.18(csstype@3.1.3): + dependencies: + csstype: 3.1.3 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hash-base@3.0.5: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + hash-base@3.1.2: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + html-entities@2.6.0: {} + + https-browserify@1.0.0: {} + + ieee754@1.2.1: {} + + inherits@2.0.4: {} + + internmap@2.0.3: {} + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-callable@1.2.7: {} + + is-extglob@2.1.1: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-nan@1.3.2: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isbot@5.1.32: {} + + jiti@2.6.1: {} + + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json5@2.2.3: {} + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + lodash@4.17.21: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@0.546.0(react@19.2.0): + dependencies: + react: 19.2.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + md5.js@1.3.5: + dependencies: + hash-base: 3.0.5 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + miller-rabin@4.0.1: + dependencies: + bn.js: 4.12.2 + brorand: 1.1.0 + + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + node-releases@2.0.27: {} + + normalize-path@3.0.0: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-is@1.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + os-browserify@0.3.0: {} + + pako@1.0.11: {} + + parse-asn1@5.1.9: + dependencies: + asn1.js: 4.10.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + pbkdf2: 3.1.5 + safe-buffer: 5.2.1 + + path-browserify@1.0.1: {} + + pathe@2.0.3: {} + + pbkdf2@3.1.5: + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.3 + safe-buffer: 5.2.1 + sha.js: 2.4.12 + to-buffer: 1.2.2 + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prettier@3.6.2: {} + + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + public-encrypt@4.0.3: + dependencies: + bn.js: 4.12.2 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + parse-asn1: 5.1.9 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + punycode@1.4.1: {} + + punycode@2.3.1: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + querystring-es3@0.2.1: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + randomfill@1.0.4: + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + react-dom@19.2.0(react@19.2.0): + dependencies: + react: 19.2.0 + scheduler: 0.27.0 + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-refresh@0.18.0: {} + + react-remove-scroll-bar@2.3.8(@types/react@19.2.4)(react@19.2.0): + dependencies: + react: 19.2.0 + react-style-singleton: 2.2.3(@types/react@19.2.4)(react@19.2.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.4 + + react-remove-scroll@2.7.1(@types/react@19.2.4)(react@19.2.0): + dependencies: + react: 19.2.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.4)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.4)(react@19.2.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.4)(react@19.2.0) + use-sidecar: 1.1.3(@types/react@19.2.4)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + + react-smooth@4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + fast-equals: 5.3.3 + prop-types: 15.8.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + + react-style-singleton@2.2.3(@types/react@19.2.4)(react@19.2.0): + dependencies: + get-nonce: 1.0.1 + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.4 + + react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + '@babel/runtime': 7.28.4 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + + react@19.2.0: {} + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + recharts-scale@0.4.5: + dependencies: + decimal.js-light: 2.5.1 + + recharts@2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + clsx: 2.1.1 + eventemitter3: 4.0.7 + lodash: 4.17.21 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-is: 18.3.1 + react-smooth: 4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + recharts-scale: 0.4.5 + tiny-invariant: 1.3.3 + victory-vendor: 36.9.2 + + resolve-pkg-maps@1.0.0: {} + + ripemd160@2.0.3: + dependencies: + hash-base: 3.1.2 + inherits: 2.0.4 + + rollup@4.53.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.2 + '@rollup/rollup-android-arm64': 4.53.2 + '@rollup/rollup-darwin-arm64': 4.53.2 + '@rollup/rollup-darwin-x64': 4.53.2 + '@rollup/rollup-freebsd-arm64': 4.53.2 + '@rollup/rollup-freebsd-x64': 4.53.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.2 + '@rollup/rollup-linux-arm-musleabihf': 4.53.2 + '@rollup/rollup-linux-arm64-gnu': 4.53.2 + '@rollup/rollup-linux-arm64-musl': 4.53.2 + '@rollup/rollup-linux-loong64-gnu': 4.53.2 + '@rollup/rollup-linux-ppc64-gnu': 4.53.2 + '@rollup/rollup-linux-riscv64-gnu': 4.53.2 + '@rollup/rollup-linux-riscv64-musl': 4.53.2 + '@rollup/rollup-linux-s390x-gnu': 4.53.2 + '@rollup/rollup-linux-x64-gnu': 4.53.2 + '@rollup/rollup-linux-x64-musl': 4.53.2 + '@rollup/rollup-openharmony-arm64': 4.53.2 + '@rollup/rollup-win32-arm64-msvc': 4.53.2 + '@rollup/rollup-win32-ia32-msvc': 4.53.2 + '@rollup/rollup-win32-x64-gnu': 4.53.2 + '@rollup/rollup-win32-x64-msvc': 4.53.2 + fsevents: 2.3.3 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + seroval-plugins@1.3.3(seroval@1.3.2): + dependencies: + seroval: 1.3.2 + + seroval@1.3.2: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + setimmediate@1.0.5: {} + + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + solid-js@1.9.10: + dependencies: + csstype: 3.1.3 + seroval: 1.3.2 + seroval-plugins: 1.3.3(seroval@1.3.2) + + source-map-js@1.2.1: {} + + source-map@0.6.1: {} + + source-map@0.7.6: {} + + sql.js@1.13.0: {} + + stackframe@1.3.4: {} + + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + stream-http@3.2.0: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + tailwind-merge@3.4.0: {} + + tailwindcss@4.1.17: {} + + tapable@2.3.0: {} + + timers-browserify@2.0.12: + dependencies: + setimmediate: 1.0.5 + + tiny-invariant@1.3.3: {} + + tiny-warning@1.0.3: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tslib@2.8.1: {} + + tsx@4.20.6: + dependencies: + esbuild: 0.25.12 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + tty-browserify@0.0.1: {} + + tw-animate-css@1.4.0: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typescript@5.9.3: {} + + undici-types@7.16.0: {} + + unplugin@2.3.10: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.15.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + + update-browserslist-db@1.1.4(browserslist@4.28.0): + dependencies: + browserslist: 4.28.0 + escalade: 3.2.0 + picocolors: 1.1.1 + + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.14.0 + + use-callback-ref@1.3.3(@types/react@19.2.4)(react@19.2.0): + dependencies: + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.4 + + use-sidecar@1.1.3(@types/react@19.2.4)(react@19.2.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.4 + + use-sync-external-store@1.6.0(react@19.2.0): + dependencies: + react: 19.2.0 + + util-deprecate@1.0.2: {} + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.2 + is-typed-array: 1.1.15 + which-typed-array: 1.1.19 + + victory-vendor@36.9.2: + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + + vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.2 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + tsx: 4.20.6 + + vm-browserify@1.1.2: {} + + webpack-virtual-modules@0.6.2: {} + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + xtend@4.0.2: {} + + yallist@3.1.1: {} + + zod@3.25.76: {} diff --git a/benchmark.config.json b/benchmark.config.json new file mode 100644 index 0000000..002d06d --- /dev/null +++ b/benchmark.config.json @@ -0,0 +1,7 @@ +{ + "$schema": "./benchmark.config.schema.json", + "suitesDir": "./suites", + "outputDir": "./results", + "databasePath": "./benchmarks.db", + "comment": "Configuration for ze-benchmarks. All paths are relative to the project root where this file is located." +} diff --git a/benchmark.config.schema.json b/benchmark.config.schema.json new file mode 100644 index 0000000..c2e2b94 --- /dev/null +++ b/benchmark.config.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Ze-Benchmarks Configuration", + "description": "Configuration file for ze-benchmarks project", + "type": "object", + "properties": { + "suitesDir": { + "type": "string", + "description": "Directory containing benchmark suites (relative to project root)", + "default": "./suites" + }, + "outputDir": { + "type": "string", + "description": "Directory for benchmark results and outputs (relative to project root)", + "default": "./results" + }, + "databasePath": { + "type": "string", + "description": "Path to the SQLite database file (relative to project root)", + "default": "./benchmarks.db" + } + }, + "required": ["suitesDir", "outputDir", "databasePath"] +} diff --git a/benchmarks.db b/benchmarks.db new file mode 100644 index 0000000..0d003d3 Binary files /dev/null and b/benchmarks.db differ diff --git a/evaluators-design.md b/evaluators-design.md index f465b19..8174580 100644 --- a/evaluators-design.md +++ b/evaluators-design.md @@ -31,7 +31,7 @@ packages/ │ │ └── types/ │ │ └── ze-evaluator.d.ts # harness-side evaluator definitions └── results/ - └── summary.json # last run output (single artifact) + └── workspaces/ # generated workspace directories for each run ``` ### Roadmap Structure (Planned) @@ -183,7 +183,7 @@ node packages/harness/dist/cli.js run update-deps nx-pnpm-monorepo --tier L1 --a "deps_delta": [] } ``` -The harness writes a single `results/summary.json` file per run; additional evaluator entries follow the same structure, and diff/dependency arrays may be large (often truncated when reporting). +The harness saves benchmark results to `benchmarks.db` (SQLite database); additional evaluator entries follow the same structure, and diff/dependency arrays may be large (often truncated when reporting). ### 3. Harness Integration ```typescript diff --git a/first.json b/first.json new file mode 100644 index 0000000..19dc2c9 --- /dev/null +++ b/first.json @@ -0,0 +1,92 @@ +{ + "batchId": "8af48afb-ca78-4b51-b551-3d804ed31052", + "exportedAt": "2025-11-12T17:30:42.242Z", + "totalRuns": 5, + "runs": [ + { + "runId": "aefb4e60-f59a-4c54-aa8c-43a1793dd03b", + "suite": "shadcn-generate-vite", + "scenario": "shadcn-generate-vite", + "agent": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "tier": "L0", + "messages": [ + { + "role": "user", + "content": "Generate a new shadcn project, use vite and add the button component\n" + } + ], + "score": 0.4444444444444444, + "success": false, + "timestamp": "2025-11-12 16:07:50" + }, + { + "runId": "3998cc58-4983-4794-bb5d-7577780469be", + "suite": "shadcn-generate-vite", + "scenario": "shadcn-generate-vite", + "agent": "specialist:shadcn-specialist:openrouter", + "model": "anthropic/claude-sonnet-4.5", + "tier": "L0", + "messages": [ + { + "role": "user", + "content": "Generate a new shadcn project, use vite and add the button component\n" + } + ], + "score": 0.35555555555555557, + "success": false, + "timestamp": "2025-11-12 16:07:50" + }, + { + "runId": "5ab9b4a2-23d7-42d4-a5f3-ac27c2d396f6", + "suite": "shadcn-generate-vite", + "scenario": "shadcn-generate-vite", + "agent": "specialist:shadcn-specialist:openrouter", + "model": "anthropic/claude-sonnet-4", + "tier": "L0", + "messages": [ + { + "role": "user", + "content": "Generate a new shadcn project, use vite and add the button component\n" + } + ], + "score": 0.4444444444444444, + "success": false, + "timestamp": "2025-11-12 16:07:50" + }, + { + "runId": "7edd6834-8041-4ae9-9134-19ad0fe1c7f4", + "suite": "shadcn-generate-vite", + "scenario": "shadcn-generate-vite", + "agent": "openrouter", + "model": "anthropic/claude-sonnet-4", + "tier": "L0", + "messages": [ + { + "role": "user", + "content": "Generate a new shadcn project, use vite and add the button component\n" + } + ], + "score": 0.44305555555555554, + "success": false, + "timestamp": "2025-11-12 16:07:50" + }, + { + "runId": "91c4eef6-4108-49c4-afc6-5802b8b6fd2d", + "suite": "shadcn-generate-vite", + "scenario": "shadcn-generate-vite", + "agent": "openrouter", + "model": "openai/gpt-4o", + "tier": "L0", + "messages": [ + { + "role": "user", + "content": "Generate a new shadcn project, use vite and add the button component\n" + } + ], + "score": 0.4361111111111111, + "success": false, + "timestamp": "2025-11-12 16:07:50" + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a2bbf29 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2067 @@ +{ + "name": "ze-benchmarks", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ze-benchmarks", + "version": "0.1.0", + "dependencies": { + "@clack/prompts": "^0.11.0", + "chalk": "^5.6.2", + "cli-progress": "^3.12.0", + "dotenv": "^16.4.5", + "figlet": "^1.9.3", + "json5": "^2.2.3", + "yaml": "^2.8.1" + }, + "devDependencies": { + "@types/cli-progress": "^3.11.6", + "@types/figlet": "^1.7.0", + "next": "^14.2.0", + "tsx": "^4.20.6", + "vitest": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "0.5.0", + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@clack/prompts": { + "version": "0.11.0", + "license": "MIT", + "dependencies": { + "@clack/core": "0.5.0", + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@next/env": { + "version": "14.2.33", + "dev": true, + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.33", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", + "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", + "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", + "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", + "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", + "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", + "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", + "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", + "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.2", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@types/cli-progress": { + "version": "3.11.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cli-progress/node_modules/@types/node": { + "version": "20.19.19", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/figlet": { + "version": "1.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "dev": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/cli-progress": { + "version": "3.12.0", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.1", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.2.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/figlet": { + "version": "1.9.3", + "license": "MIT", + "dependencies": { + "commander": "^14.0.0" + }, + "bin": { + "figlet": "bin/index.js" + }, + "engines": { + "node": ">= 17.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json5": { + "version": "2.2.3", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.19", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "14.2.33", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/env": "14.2.33", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.33", + "@next/swc-darwin-x64": "14.2.33", + "@next/swc-linux-arm64-gnu": "14.2.33", + "@next/swc-linux-arm64-musl": "14.2.33", + "@next/swc-linux-x64-gnu": "14.2.33", + "@next/swc-linux-x64-musl": "14.2.33", + "@next/swc-win32-arm64-msvc": "14.2.33", + "@next/swc-win32-ia32-msvc": "14.2.33", + "@next/swc-win32-x64-msvc": "14.2.33" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/caniuse-lite": { + "version": "1.0.30001751", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.6", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rollup": { + "version": "4.53.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.2", + "@rollup/rollup-android-arm64": "4.53.2", + "@rollup/rollup-darwin-arm64": "4.53.2", + "@rollup/rollup-darwin-x64": "4.53.2", + "@rollup/rollup-freebsd-arm64": "4.53.2", + "@rollup/rollup-freebsd-x64": "4.53.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", + "@rollup/rollup-linux-arm-musleabihf": "4.53.2", + "@rollup/rollup-linux-arm64-gnu": "4.53.2", + "@rollup/rollup-linux-arm64-musl": "4.53.2", + "@rollup/rollup-linux-loong64-gnu": "4.53.2", + "@rollup/rollup-linux-ppc64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-musl": "4.53.2", + "@rollup/rollup-linux-s390x-gnu": "4.53.2", + "@rollup/rollup-linux-x64-gnu": "4.53.2", + "@rollup/rollup-linux-x64-musl": "4.53.2", + "@rollup/rollup-openharmony-arm64": "4.53.2", + "@rollup/rollup-win32-arm64-msvc": "4.53.2", + "@rollup/rollup-win32-ia32-msvc": "4.53.2", + "@rollup/rollup-win32-x64-gnu": "4.53.2", + "@rollup/rollup-win32-x64-msvc": "4.53.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "dev": true, + "license": "MIT" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.20.6", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + } + } +} diff --git a/package.json b/package.json index 9a5925d..d754a6b 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,13 @@ "dev": "pnpm --filter @ze/harness run dev", "bench": "tsx packages/harness/src/cli.ts", "harness": "tsx packages/harness/src/cli.ts", - "start:report": "pnpm --filter benchmark-report run dev" + "start:report": "pnpm --filter benchmark-report run dev", + "bench:run": "tsx scripts/run-all-models.ts", + "bench:compare": "tsx scripts/compare-batches.ts", + "export:prompts": "tsx scripts/export-batch-prompts.ts", + "validate:prompts": "tsx scripts/validate-prompts.ts", + "clear:db": "tsx scripts/clear-database.ts", + "workflow:iterate": "tsx scripts/workflow-iterate.ts" }, "engines": { "node": ">=18" @@ -33,6 +39,8 @@ "chalk": "^5.6.2", "cli-progress": "^3.12.0", "dotenv": "^16.4.5", - "figlet": "^1.9.3" + "figlet": "^1.9.3", + "json5": "^2.2.3", + "yaml": "^2.8.1" } } \ No newline at end of file diff --git a/packages/agent-adapters/SPECIALIST_INTEGRATION.md b/packages/agent-adapters/SPECIALIST_INTEGRATION.md new file mode 100644 index 0000000..ca9774a --- /dev/null +++ b/packages/agent-adapters/SPECIALIST_INTEGRATION.md @@ -0,0 +1,215 @@ +# Specialist Adapter Integration Guide + +## Overview + +The `SpecialistAdapter` wraps existing adapters (Anthropic, OpenRouter) and enhances them with specialist template-based prompt transformation using the agency-prompt-creator package. + +## Architecture + +``` +User Prompt → SpecialistAdapter → Prompt Transformation → Underlying Adapter → Model API + ↓ + Template Loading + Task Detection + Model Selection + Mustache Substitution +``` + +## Usage + +### Basic Usage + +```typescript +import { AnthropicAdapter, SpecialistAdapter } from '@ze/agent-adapters'; + +// Create base adapter +const anthropic = new AnthropicAdapter(); + +// Wrap with specialist adapter +const specialist = new SpecialistAdapter( + anthropic, + 'agency-specialist-mint/snapshots/shadcn-specialist/1.0.0/snapshot-001.json5' +); + +// Use like any other adapter +const response = await specialist.send({ + messages: [ + { role: 'user', content: 'Set up a new Vite project with shadcn/ui' } + ] +}); +``` + +### Integration with CLI + +To integrate with the ze-benchmarks CLI (`packages/harness/src/cli.ts`), update the `createAgentAdapter` function: + +```typescript +function createAgentAdapter(agentName: string, model?: string, specialistTemplate?: string): AgentAdapter { + // Create base adapter + let baseAdapter: AgentAdapter; + + switch (agentName) { + case 'openrouter': + baseAdapter = new OpenRouterAdapter(process.env.OPENROUTER_API_KEY, model); + break; + case 'anthropic': + if (model) { + process.env.CLAUDE_MODEL = model; + } + baseAdapter = new AnthropicAdapter(); + break; + case 'claude-code': + baseAdapter = new ClaudeCodeAdapter(model); + break; + case 'echo': + default: + baseAdapter = new EchoAgent(); + } + + // Wrap with specialist if template provided + if (specialistTemplate) { + return new SpecialistAdapter(baseAdapter, specialistTemplate); + } + + return baseAdapter; +} +``` + +### Reading from models.json5 + +To integrate with the `models.json5` configuration: + +```typescript +import JSON5 from 'json5'; +import { readFileSync } from 'fs'; + +interface ModelConfig { + provider: string; + model: string; + specialist?: string; +} + +interface ModelsConfig { + vanilla_models: ModelConfig[]; + specialist_models: ModelConfig[]; +} + +function loadModelsConfig(path: string): ModelsConfig { + const contents = readFileSync(path, 'utf-8'); + return JSON5.parse(contents); +} + +function createAdapterFromConfig(config: ModelConfig): AgentAdapter { + // Create base adapter based on provider + let baseAdapter: AgentAdapter; + switch (config.provider) { + case 'anthropic': + process.env.CLAUDE_MODEL = config.model; + baseAdapter = new AnthropicAdapter(); + break; + case 'openai': + case 'openrouter': + baseAdapter = new OpenRouterAdapter(process.env.OPENROUTER_API_KEY, config.model); + break; + default: + throw new Error(`Unknown provider: ${config.provider}`); + } + + // Wrap with specialist if specified + if (config.specialist) { + // Resolve specialist namespace to template path + const templatePath = resolveSpecialistTemplate(config.specialist); + return new SpecialistAdapter(baseAdapter, templatePath); + } + + return baseAdapter; +} + +function resolveSpecialistTemplate(namespace: string): string { + // Convert @zephyr-cloud/shadcn-specialist to file path + // For now, use the latest snapshot + // TODO: Support version pinning + const name = namespace.replace('@zephyr-cloud/', ''); + return `agency-specialist-mint/snapshots/${name}/1.0.0/snapshot-001.json5`; +} +``` + +## Template Resolution + +The adapter expects a path to a specialist template JSON5 file. The path should be: +- Relative to the project root +- Point to either a template or a snapshot + +**Template path**: `starting_from_outcome/shadcn-specialist-template.json5` +**Snapshot path**: `agency-specialist-mint/snapshots/shadcn-specialist/1.0.0/snapshot-001.json5` + +## Error Handling + +The adapter includes error handling for: + +1. **Missing template file**: Clear error message with file path +2. **Invalid JSON5**: Parse error with details +3. **Malformed template**: Validation of required fields (name, prompts) +4. **Empty user message**: Validation that request has user content +5. **Prompt transformation errors**: Wrapped with specialist context + +## Prompt Transformation Flow + +1. **Extract user prompt**: Gets the last user message from the request +2. **Detect task type**: Uses agency-prompt-creator to detect task (project_setup, component_add, etc.) +3. **Select prompt**: Chooses model-specific prompt if available, falls back to default +4. **Apply substitution**: Uses mustache template substitution with context +5. **Inject system prompt**: Replaces or adds system message with transformed prompt +6. **Delegate**: Sends modified request to underlying adapter + +## Template Context + +The following context variables are available for mustache substitution: + +- `workspaceDir`: The workspace directory from the request +- `hasTools`: Whether tools are available +- `toolCount`: Number of tools available +- (Additional context can be added in `buildTemplateContext()`) + +## Dependency Setup + +**Note**: Due to nested workspace issues, the agency-prompt-creator dependency may need to be resolved: + +### Option 1: Link Protocol (Current) +```json +{ + "dependencies": { + "agency-prompt-creator": "link:../../../agency-prompt-creator" + } +} +``` + +### Option 2: Workspace Protocol (Preferred if root workspace) +```json +{ + "dependencies": { + "agency-prompt-creator": "workspace:*" + } +} +``` + +After updating package.json, run: +```bash +pnpm install +``` + +## Testing + +To test the specialist adapter: + +1. Ensure agency-prompt-creator is built: `cd agency-prompt-creator && pnpm build` +2. Create a test script or use the provided `test-specialist.ts` +3. Run with tsx: `pnpm exec tsx test-specialist.ts` + +## Next Steps + +1. Update ze-benchmarks CLI to support specialist adapter +2. Add CLI flags: `--specialist ` +3. Integrate with models.json5 loading +4. Add specialist to benchmark result metadata +5. Update benchmark report to show specialist vs vanilla comparisons diff --git a/packages/agent-adapters/package.json b/packages/agent-adapters/package.json index 8ccf248..2273745 100644 --- a/packages/agent-adapters/package.json +++ b/packages/agent-adapters/package.json @@ -16,6 +16,8 @@ "dependencies": { "@anthropic-ai/sdk": "^0.65.0", "openai": "^4.0.0", - "dotenv": "^16.0.0" + "dotenv": "^16.0.0", + "json5": "^2.2.3", + "agency-prompt-creator": "link:../../../agency-prompt-creator" } } \ No newline at end of file diff --git a/packages/agent-adapters/pnpm-lock.yaml b/packages/agent-adapters/pnpm-lock.yaml deleted file mode 100644 index a589a67..0000000 --- a/packages/agent-adapters/pnpm-lock.yaml +++ /dev/null @@ -1,76 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@anthropic-ai/sdk': - specifier: ^0.65.0 - version: 0.65.0 - devDependencies: - '@types/node': - specifier: ^20.14.10 - version: 20.19.19 - typescript: - specifier: ^5.5.4 - version: 5.9.3 - -packages: - - '@anthropic-ai/sdk@0.65.0': - resolution: {integrity: sha512-zIdPOcrCVEI8t3Di40nH4z9EoeyGZfXbYSvWdDLsB/KkaSYMnEgC7gmcgWu83g2NTn1ZTpbMvpdttWDGGIk6zw==} - hasBin: true - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - peerDependenciesMeta: - zod: - optional: true - - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} - engines: {node: '>=6.9.0'} - - '@types/node@20.19.19': - resolution: {integrity: sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg==} - - json-schema-to-ts@3.1.1: - resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} - engines: {node: '>=16'} - - ts-algebra@2.0.0: - resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - -snapshots: - - '@anthropic-ai/sdk@0.65.0': - dependencies: - json-schema-to-ts: 3.1.1 - - '@babel/runtime@7.28.4': {} - - '@types/node@20.19.19': - dependencies: - undici-types: 6.21.0 - - json-schema-to-ts@3.1.1: - dependencies: - '@babel/runtime': 7.28.4 - ts-algebra: 2.0.0 - - ts-algebra@2.0.0: {} - - typescript@5.9.3: {} - - undici-types@6.21.0: {} diff --git a/packages/agent-adapters/src/__tests__/llm-cache.test.ts b/packages/agent-adapters/src/__tests__/llm-cache.test.ts new file mode 100644 index 0000000..33e5134 --- /dev/null +++ b/packages/agent-adapters/src/__tests__/llm-cache.test.ts @@ -0,0 +1,233 @@ +/** + * Unit tests for LLM cache + */ + +import { LLMCache } from '../llm-cache'; +import type { PromptSelectionResult, ExtractedVariables } from '../llm-prompt-selector'; + +describe('LLMCache', () => { + let cache: LLMCache; + + beforeEach(() => { + cache = new LLMCache(1000); // 1 second TTL for testing + }); + + describe('Selection cache', () => { + it('should store and retrieve selection results', () => { + const result: PromptSelectionResult = { + selectedPromptId: 'default.project_setup', + confidence: 'High', + reasoning: 'User wants to setup a project' + }; + + cache.setSelection('test-key', result); + const retrieved = cache.getSelection('test-key'); + + expect(retrieved).toEqual(result); + }); + + it('should return null for non-existent keys', () => { + const result = cache.getSelection('non-existent'); + + expect(result).toBeNull(); + }); + + it('should return null for expired entries', async () => { + const result: PromptSelectionResult = { + selectedPromptId: 'default.project_setup', + confidence: 'High', + reasoning: 'Test' + }; + + cache.setSelection('test-key', result); + + // Wait for TTL to expire + await new Promise(resolve => setTimeout(resolve, 1100)); + + const retrieved = cache.getSelection('test-key'); + + expect(retrieved).toBeNull(); + }); + + it('should remove expired entries on access', async () => { + const result: PromptSelectionResult = { + selectedPromptId: 'default.project_setup', + confidence: 'High', + reasoning: 'Test' + }; + + cache.setSelection('test-key', result); + + // Wait for expiry + await new Promise(resolve => setTimeout(resolve, 1100)); + + cache.getSelection('test-key'); + + const stats = cache.getStats(); + expect(stats.selectionCacheSize).toBe(0); + }); + }); + + describe('Variables cache', () => { + it('should store and retrieve variable extraction results', () => { + const vars: ExtractedVariables = { + framework: 'Vite', + packageManager: 'pnpm' + }; + + cache.setVariables('test-key', vars); + const retrieved = cache.getVariables('test-key'); + + expect(retrieved).toEqual(vars); + }); + + it('should return null for non-existent keys', () => { + const result = cache.getVariables('non-existent'); + + expect(result).toBeNull(); + }); + + it('should return null for expired entries', async () => { + const vars: ExtractedVariables = { + framework: 'Next.js' + }; + + cache.setVariables('test-key', vars); + + // Wait for TTL to expire + await new Promise(resolve => setTimeout(resolve, 1100)); + + const retrieved = cache.getVariables('test-key'); + + expect(retrieved).toBeNull(); + }); + + it('should handle empty variable objects', () => { + const vars: ExtractedVariables = {}; + + cache.setVariables('test-key', vars); + const retrieved = cache.getVariables('test-key'); + + expect(retrieved).toEqual({}); + }); + }); + + describe('Cache management', () => { + it('should clear all cached entries', () => { + const selection: PromptSelectionResult = { + selectedPromptId: 'test', + confidence: 'High', + reasoning: 'test' + }; + const vars: ExtractedVariables = { framework: 'Vite' }; + + cache.setSelection('key1', selection); + cache.setVariables('key2', vars); + + expect(cache.getStats().selectionCacheSize).toBe(1); + expect(cache.getStats().variablesCacheSize).toBe(1); + + cache.clear(); + + expect(cache.getStats().selectionCacheSize).toBe(0); + expect(cache.getStats().variablesCacheSize).toBe(0); + }); + + it('should provide accurate cache statistics', () => { + const selection: PromptSelectionResult = { + selectedPromptId: 'test', + confidence: 'High', + reasoning: 'test' + }; + const vars: ExtractedVariables = { framework: 'Vite' }; + + cache.setSelection('key1', selection); + cache.setSelection('key2', selection); + cache.setVariables('key3', vars); + + const stats = cache.getStats(); + + expect(stats.selectionCacheSize).toBe(2); + expect(stats.variablesCacheSize).toBe(1); + }); + + it('should clean up expired entries', async () => { + const selection: PromptSelectionResult = { + selectedPromptId: 'test', + confidence: 'High', + reasoning: 'test' + }; + + cache.setSelection('key1', selection); + cache.setSelection('key2', selection); + + // Wait for expiry + await new Promise(resolve => setTimeout(resolve, 1100)); + + // Add new entry + cache.setSelection('key3', selection); + + // Clean up expired + cache.cleanupExpired(); + + const stats = cache.getStats(); + expect(stats.selectionCacheSize).toBe(1); // Only key3 should remain + }); + }); + + describe('TTL configuration', () => { + it('should use custom TTL', async () => { + const shortCache = new LLMCache(500); // 500ms TTL + const result: PromptSelectionResult = { + selectedPromptId: 'test', + confidence: 'High', + reasoning: 'test' + }; + + shortCache.setSelection('key', result); + + // Before expiry + expect(shortCache.getSelection('key')).not.toBeNull(); + + // Wait for expiry + await new Promise(resolve => setTimeout(resolve, 600)); + + // After expiry + expect(shortCache.getSelection('key')).toBeNull(); + }); + + it('should use default TTL of 1 hour', async () => { + const defaultCache = new LLMCache(); + const result: PromptSelectionResult = { + selectedPromptId: 'test', + confidence: 'High', + reasoning: 'test' + }; + + defaultCache.setSelection('key', result); + + // Should still be valid after 1 second + await new Promise(resolve => setTimeout(resolve, 1000)); + + expect(defaultCache.getSelection('key')).not.toBeNull(); + }); + }); + + describe('Multiple cache instances', () => { + it('should maintain separate caches', () => { + const cache1 = new LLMCache(); + const cache2 = new LLMCache(); + + const result: PromptSelectionResult = { + selectedPromptId: 'test', + confidence: 'High', + reasoning: 'test' + }; + + cache1.setSelection('key', result); + + expect(cache1.getSelection('key')).not.toBeNull(); + expect(cache2.getSelection('key')).toBeNull(); + }); + }); +}); diff --git a/packages/agent-adapters/src/__tests__/llm-prompt-selector.test.ts b/packages/agent-adapters/src/__tests__/llm-prompt-selector.test.ts new file mode 100644 index 0000000..46ef3d5 --- /dev/null +++ b/packages/agent-adapters/src/__tests__/llm-prompt-selector.test.ts @@ -0,0 +1,340 @@ +/** + * Unit tests for LLM prompt selection utilities + */ + +import { + buildPromptSelectionPrompt, + parsePromptSelectionResponse, + buildVariableExtractionPrompt, + parseToolCallResponse, + validateExtractedVariables, + applyDefaults, + createCacheKey, + type ExtractedVariables +} from '../llm-prompt-selector'; + +describe('buildPromptSelectionPrompt', () => { + it('should build a valid selection prompt with default prompts', () => { + const prompts = { + default: { + spawnerPrompt: 'I am a specialist', + project_setup: 'Set up a {framework} project' + } + }; + + const result = buildPromptSelectionPrompt('Create a new Vite project', prompts); + + expect(result).toContain('User Request: "Create a new Vite project"'); + expect(result).toContain('default.spawnerPrompt'); + expect(result).toContain('default.project_setup'); + expect(result).toContain('Available Templates:'); + }); + + it('should include model-specific prompts when model is provided', () => { + const prompts = { + default: { + spawnerPrompt: 'I am a specialist' + }, + model_specific: { + 'claude-sonnet-4.5': { + spawnerPrompt: 'Claude specific prompt', + project_setup: 'Claude specific setup' + } + } + }; + + const result = buildPromptSelectionPrompt('Setup project', prompts, 'claude-sonnet-4.5'); + + expect(result).toContain('model_specific.claude-sonnet-4.5.spawnerPrompt'); + expect(result).toContain('model_specific.claude-sonnet-4.5.project_setup'); + }); +}); + +describe('parsePromptSelectionResponse', () => { + it('should parse valid JSON response', () => { + const response = JSON.stringify({ + selected_prompt_id: 'default.project_setup', + confidence: 'High', + reasoning: 'User wants to setup a project' + }); + + const result = parsePromptSelectionResponse(response); + + expect(result.selectedPromptId).toBe('default.project_setup'); + expect(result.confidence).toBe('High'); + expect(result.reasoning).toBe('User wants to setup a project'); + }); + + it('should parse JSON from markdown code blocks', () => { + const response = `Here's my response: +\`\`\`json +{ + "selected_prompt_id": "model_specific.claude-sonnet-4.5.component_add", + "confidence": "High", + "reasoning": "User wants to add a component" +} +\`\`\``; + + const result = parsePromptSelectionResponse(response); + + expect(result.selectedPromptId).toBe('model_specific.claude-sonnet-4.5.component_add'); + expect(result.confidence).toBe('High'); + }); + + it('should throw error if JSON is invalid', () => { + const response = 'not json at all'; + + expect(() => parsePromptSelectionResponse(response)).toThrow('No JSON found in LLM response'); + }); + + it('should throw error if required fields are missing', () => { + const response = JSON.stringify({ + selected_prompt_id: 'default.project_setup' + // missing confidence and reasoning + }); + + expect(() => parsePromptSelectionResponse(response)).toThrow('Missing required fields'); + }); + + it('should throw error if confidence level is invalid', () => { + const response = JSON.stringify({ + selected_prompt_id: 'default.project_setup', + confidence: 'VeryHigh', // invalid + reasoning: 'test' + }); + + expect(() => parsePromptSelectionResponse(response)).toThrow('Invalid confidence level'); + }); +}); + +describe('buildVariableExtractionPrompt', () => { + it('should build a valid extraction prompt', () => { + const userPrompt = 'Create a Vite project with pnpm'; + const template = 'Set up a {framework} project with {packageManager}'; + + const result = buildVariableExtractionPrompt(userPrompt, template); + + expect(result).toContain(userPrompt); + expect(result).toContain('extract_template_variables'); + expect(result).toContain('Extract ONLY explicitly mentioned'); + }); + + it('should truncate long templates', () => { + const userPrompt = 'Create a Vite project'; + const template = 'a'.repeat(500); + + const result = buildVariableExtractionPrompt(userPrompt, template); + + expect(result.length).toBeLessThan(template.length + 500); // Should be truncated + expect(result).toContain('...'); + }); +}); + +describe('parseToolCallResponse', () => { + it('should parse tool call with input object', () => { + const toolCall = { + input: { + framework: 'Vite', + packageManager: 'pnpm' + } + }; + + const result = parseToolCallResponse(toolCall); + + expect(result.framework).toBe('Vite'); + expect(result.packageManager).toBe('pnpm'); + }); + + it('should parse tool call with arguments object', () => { + const toolCall = { + arguments: { + framework: 'Next.js', + componentName: 'button' + } + }; + + const result = parseToolCallResponse(toolCall); + + expect(result.framework).toBe('Next.js'); + expect(result.componentName).toBe('button'); + }); + + it('should parse tool call with JSON string input', () => { + const toolCall = { + input: JSON.stringify({ + framework: 'Remix', + baseColor: 'zinc' + }) + }; + + const result = parseToolCallResponse(toolCall); + + expect(result.framework).toBe('Remix'); + expect(result.baseColor).toBe('zinc'); + }); + + it('should throw error for invalid tool call', () => { + expect(() => parseToolCallResponse(null)).toThrow('Invalid tool call object'); + expect(() => parseToolCallResponse('string')).toThrow('Tool call input is not an object'); + }); +}); + +describe('validateExtractedVariables', () => { + it('should validate and normalize framework values', () => { + const vars: ExtractedVariables = { + framework: 'vite', // lowercase + packageManager: 'pnpm' + }; + + const result = validateExtractedVariables(vars); + + expect(result.framework).toBe('Vite'); // normalized to proper case + }); + + it('should use default for invalid framework', () => { + const vars: ExtractedVariables = { + framework: 'InvalidFramework' + }; + + const result = validateExtractedVariables(vars); + + expect(result.framework).toBe('Vite'); // default + }); + + it('should normalize package manager to lowercase', () => { + const vars: ExtractedVariables = { + packageManager: 'PNPM' + }; + + const result = validateExtractedVariables(vars); + + expect(result.packageManager).toBe('pnpm'); + }); + + it('should use default for invalid package manager', () => { + const vars: ExtractedVariables = { + packageManager: 'invalid' + }; + + const result = validateExtractedVariables(vars); + + expect(result.packageManager).toBe('pnpm'); + }); + + it('should normalize base color to lowercase', () => { + const vars: ExtractedVariables = { + baseColor: 'SLATE' + }; + + const result = validateExtractedVariables(vars); + + expect(result.baseColor).toBe('slate'); + }); + + it('should use default for invalid base color', () => { + const vars: ExtractedVariables = { + baseColor: 'rainbow' as any + }; + + const result = validateExtractedVariables(vars); + + expect(result.baseColor).toBe('slate'); + }); + + it('should preserve other variables unchanged', () => { + const vars: ExtractedVariables = { + componentName: 'MyButton', + features: 'dark mode, theming', + issueType: 'build error', + description: 'Some description' + }; + + const result = validateExtractedVariables(vars); + + expect(result.componentName).toBe('MyButton'); + expect(result.features).toBe('dark mode, theming'); + expect(result.issueType).toBe('build error'); + expect(result.description).toBe('Some description'); + }); +}); + +describe('applyDefaults', () => { + it('should apply default package manager for project_setup', () => { + const vars: ExtractedVariables = {}; + + const result = applyDefaults(vars, 'default.project_setup'); + + expect(result.packageManager).toBe('pnpm'); + }); + + it('should apply default framework for project_setup', () => { + const vars: ExtractedVariables = {}; + + const result = applyDefaults(vars, 'model_specific.claude.project_setup'); + + expect(result.framework).toBe('Vite'); + }); + + it('should not override existing values', () => { + const vars: ExtractedVariables = { + packageManager: 'npm', + framework: 'Next.js' + }; + + const result = applyDefaults(vars, 'default.project_setup'); + + expect(result.packageManager).toBe('npm'); // not overridden + expect(result.framework).toBe('Next.js'); // not overridden + }); + + it('should apply theme defaults for theme_setup', () => { + const vars: ExtractedVariables = {}; + + const result = applyDefaults(vars, 'default.theme_setup'); + + expect(result.themeType).toBe('CSS variables'); + expect(result.baseColor).toBe('slate'); + }); + + it('should not apply defaults for non-matching prompt types', () => { + const vars: ExtractedVariables = {}; + + const result = applyDefaults(vars, 'default.troubleshoot'); + + expect(result.packageManager).toBeUndefined(); + expect(result.framework).toBeUndefined(); + }); +}); + +describe('createCacheKey', () => { + it('should create consistent keys for same prompt', () => { + const prompt = 'Create a Vite project with pnpm'; + + const key1 = createCacheKey(prompt); + const key2 = createCacheKey(prompt); + + expect(key1).toBe(key2); + }); + + it('should create different keys for different prompts', () => { + const prompt1 = 'Create a Vite project'; + const prompt2 = 'Create a Next.js project'; + + const key1 = createCacheKey(prompt1); + const key2 = createCacheKey(prompt2); + + expect(key1).not.toBe(key2); + }); + + it('should create keys with prompt_ prefix', () => { + const key = createCacheKey('test prompt'); + + expect(key).toMatch(/^prompt_/); + }); + + it('should handle empty prompts', () => { + const key = createCacheKey(''); + + expect(key).toMatch(/^prompt_/); + }); +}); diff --git a/packages/agent-adapters/src/anthropic.ts b/packages/agent-adapters/src/anthropic.ts index cd731ad..4f845c0 100644 --- a/packages/agent-adapters/src/anthropic.ts +++ b/packages/agent-adapters/src/anthropic.ts @@ -2,10 +2,28 @@ import { config } from 'dotenv'; import Anthropic from "@anthropic-ai/sdk"; import type { MessageCreateParams, ContentBlock, TextBlock, ToolUseBlock } from "@anthropic-ai/sdk/resources/messages"; import type { AgentAdapter, AgentRequest, AgentResponse } from "./index.ts"; -import { resolve } from 'node:path'; +import { resolve, join } from 'node:path'; +import { existsSync } from 'node:fs'; // Load environment variables from .env file in project root -config({ path: resolve(process.cwd(), '.env') }); +// Find workspace root by looking for pnpm-workspace.yaml (topmost one) +function findWorkspaceRoot(startDir: string): string { + let currentDir = startDir; + let lastWorkspaceRoot = startDir; + + while (currentDir !== resolve(currentDir, '..')) { + if (existsSync(join(currentDir, 'pnpm-workspace.yaml'))) { + lastWorkspaceRoot = currentDir; + } + currentDir = resolve(currentDir, '..'); + } + + return lastWorkspaceRoot; +} + +const workspaceRoot = findWorkspaceRoot(process.cwd()); +const envPath = resolve(workspaceRoot, '.env'); +config({ path: envPath }); interface ToolResult { diff --git a/packages/agent-adapters/src/index.ts b/packages/agent-adapters/src/index.ts index 8407d21..a0c8eef 100644 --- a/packages/agent-adapters/src/index.ts +++ b/packages/agent-adapters/src/index.ts @@ -41,3 +41,5 @@ export class EchoAgent implements AgentAdapter { export { ClaudeCodeAdapter } from './claude-code.ts'; export { AnthropicAdapter } from './anthropic.ts'; export { OpenRouterAdapter } from './openrouter.ts'; +// SpecialistAdapter is now lazy-loaded directly where needed to avoid loading agency-prompt-creator unless required +// export { SpecialistAdapter } from './specialist.ts'; diff --git a/packages/agent-adapters/src/llm-cache.ts b/packages/agent-adapters/src/llm-cache.ts new file mode 100644 index 0000000..761d365 --- /dev/null +++ b/packages/agent-adapters/src/llm-cache.ts @@ -0,0 +1,283 @@ +/** + * In-memory cache for LLM prompt selection and variable extraction results + * + * Implements TTL-based caching to reduce LLM API calls for similar prompts + */ + +import type { PromptSelectionResult, ExtractedVariables } from './llm-prompt-selector.js'; +import type { ExtractedIntent, SpecialistSelection } from 'agency-prompt-creator'; + +interface CacheEntry { + value: T; + expires: number; +} + +/** + * LLM result cache with TTL support + */ +export class LLMCache { + private selectionCache = new Map>(); + private variablesCache = new Map>(); + private intentCache = new Map>(); + private componentSelectionCache = new Map>(); + private readonly ttl: number; + + /** + * Create a new cache instance + * + * @param ttlMs Time-to-live in milliseconds (default 1 hour) + */ + constructor(ttlMs: number = 3600000) { + this.ttl = ttlMs; + } + + /** + * Get cached prompt selection result + * + * @param cacheKey Cache key (typically hash of user prompt) + * @returns Cached result or null if not found/expired + */ + getSelection(cacheKey: string): PromptSelectionResult | null { + const cached = this.selectionCache.get(cacheKey); + + if (!cached) { + return null; + } + + if (cached.expires < Date.now()) { + // Expired, remove from cache + this.selectionCache.delete(cacheKey); + return null; + } + + return cached.value; + } + + /** + * Store prompt selection result in cache + * + * @param cacheKey Cache key (typically hash of user prompt) + * @param result Selection result to cache + */ + setSelection(cacheKey: string, result: PromptSelectionResult): void { + this.selectionCache.set(cacheKey, { + value: result, + expires: Date.now() + this.ttl + }); + } + + /** + * Get cached variable extraction result + * + * @param cacheKey Cache key (typically hash of user prompt) + * @returns Cached variables or null if not found/expired + */ + getVariables(cacheKey: string): ExtractedVariables | null { + const cached = this.variablesCache.get(cacheKey); + + if (!cached) { + return null; + } + + if (cached.expires < Date.now()) { + // Expired, remove from cache + this.variablesCache.delete(cacheKey); + return null; + } + + return cached.value; + } + + /** + * Store variable extraction result in cache + * + * @param cacheKey Cache key (typically hash of user prompt) + * @param variables Extracted variables to cache + */ + setVariables(cacheKey: string, variables: ExtractedVariables): void { + this.variablesCache.set(cacheKey, { + value: variables, + expires: Date.now() + this.ttl + }); + } + + /** + * Get cached intent extraction result + * + * @param cacheKey Cache key (typically hash of user prompt) + * @returns Cached intent or null if not found/expired + */ + getIntent(cacheKey: string): ExtractedIntent | null { + const cached = this.intentCache.get(cacheKey); + + if (!cached) { + return null; + } + + if (cached.expires < Date.now()) { + // Expired, remove from cache + this.intentCache.delete(cacheKey); + return null; + } + + return cached.value; + } + + /** + * Store intent extraction result in cache + * + * @param cacheKey Cache key (typically hash of user prompt) + * @param intent Intent to cache + */ + setIntent(cacheKey: string, intent: ExtractedIntent): void { + this.intentCache.set(cacheKey, { + value: intent, + expires: Date.now() + this.ttl + }); + } + + /** + * Get cached component selection result + * + * @param cacheKey Cache key (typically hash of user prompt) + * @returns Cached selection or null if not found/expired + */ + getComponentSelection(cacheKey: string): SpecialistSelection | null { + const cached = this.componentSelectionCache.get(cacheKey); + + if (!cached) { + return null; + } + + if (cached.expires < Date.now()) { + // Expired, remove from cache + this.componentSelectionCache.delete(cacheKey); + return null; + } + + return cached.value; + } + + /** + * Store component selection result in cache + * + * @param cacheKey Cache key (typically hash of user prompt) + * @param selection Selection to cache + */ + setComponentSelection(cacheKey: string, selection: SpecialistSelection): void { + this.componentSelectionCache.set(cacheKey, { + value: selection, + expires: Date.now() + this.ttl + }); + } + + /** + * Generic get method for any cached value + * + * @param cacheKey Cache key + * @returns Cached value or null if not found/expired + */ + get(cacheKey: string): any { + // Try all caches + return this.getIntent(cacheKey) || + this.getComponentSelection(cacheKey) || + this.getSelection(cacheKey) || + this.getVariables(cacheKey); + } + + /** + * Generic set method for any cached value + * Note: This is a simple implementation that tries to infer the type + * For better type safety, use the specific methods above + * + * @param cacheKey Cache key + * @param value Value to cache + */ + set(cacheKey: string, value: any): void { + // Store in a generic cache + const genericCache = new Map>(); + if (!this.hasOwnProperty('genericCache')) { + (this as any).genericCache = genericCache; + } + ((this as any).genericCache as Map>).set(cacheKey, { + value, + expires: Date.now() + this.ttl + }); + } + + /** + * Clear all cached entries + */ + clear(): void { + this.selectionCache.clear(); + this.variablesCache.clear(); + this.intentCache.clear(); + this.componentSelectionCache.clear(); + if ((this as any).genericCache) { + ((this as any).genericCache as Map>).clear(); + } + } + + /** + * Get cache statistics + * + * @returns Cache stats (size, hit rate tracking would require additional implementation) + */ + getStats(): { + selectionCacheSize: number; + variablesCacheSize: number; + intentCacheSize: number; + componentSelectionCacheSize: number; + } { + return { + selectionCacheSize: this.selectionCache.size, + variablesCacheSize: this.variablesCache.size, + intentCacheSize: this.intentCache.size, + componentSelectionCacheSize: this.componentSelectionCache.size + }; + } + + /** + * Clean up expired entries (optional periodic maintenance) + */ + cleanupExpired(): void { + const now = Date.now(); + + // Clean selection cache + for (const [key, entry] of this.selectionCache.entries()) { + if (entry.expires < now) { + this.selectionCache.delete(key); + } + } + + // Clean variables cache + for (const [key, entry] of this.variablesCache.entries()) { + if (entry.expires < now) { + this.variablesCache.delete(key); + } + } + + // Clean intent cache + for (const [key, entry] of this.intentCache.entries()) { + if (entry.expires < now) { + this.intentCache.delete(key); + } + } + + // Clean component selection cache + for (const [key, entry] of this.componentSelectionCache.entries()) { + if (entry.expires < now) { + this.componentSelectionCache.delete(key); + } + } + + // Clean generic cache if it exists + if ((this as any).genericCache) { + for (const [key, entry] of ((this as any).genericCache as Map>).entries()) { + if (entry.expires < now) { + ((this as any).genericCache as Map>).delete(key); + } + } + } + } +} diff --git a/packages/agent-adapters/src/llm-prompt-selector.ts b/packages/agent-adapters/src/llm-prompt-selector.ts new file mode 100644 index 0000000..ac92fc3 --- /dev/null +++ b/packages/agent-adapters/src/llm-prompt-selector.ts @@ -0,0 +1,440 @@ +/** + * LLM-powered prompt selection and variable extraction utilities + * + * This module provides utilities for using an LLM (anthropic/claude-3.5-haiku) to: + * 1. Analyze user intent and select the best matching template prompt + * 2. Extract mustache template variables via structured tool calling + */ + +import type { PromptConfig, Prompts } from 'agency-prompt-creator'; + +/** + * Result from LLM prompt selection + */ +export interface PromptSelectionResult { + selectedPromptId: string; + confidence: 'High' | 'Medium' | 'Low'; + reasoning: string; +} + +/** + * Extracted template variables from user prompt + */ +export interface ExtractedVariables { + framework?: string; + packageManager?: string; + componentName?: string; + features?: string; + issueType?: string; + description?: string; + themeType?: string; + baseColor?: 'slate' | 'gray' | 'zinc' | 'neutral' | 'stone'; + [key: string]: any; +} + +/** + * Tool definition for variable extraction + */ +export const VARIABLE_EXTRACTION_TOOL = { + name: "extract_template_variables", + description: "Extract variables from user prompt for template placeholders", + input_schema: { + type: "object", + properties: { + framework: { + type: "string", + enum: ["Vite", "Next.js", "Remix", "Astro", "SvelteKit"], + description: "Framework mentioned or implied" + }, + packageManager: { + type: "string", + enum: ["pnpm", "npm", "yarn", "bun"], + description: "Package manager (default pnpm)" + }, + componentName: { + type: "string", + description: "Component to add (if applicable)" + }, + features: { + type: "string", + description: "Features to configure (comma-separated)" + }, + issueType: { + type: "string", + description: "Issue type for troubleshooting" + }, + description: { + type: "string", + description: "Detailed description from prompt" + }, + themeType: { + type: "string", + description: "Theme configuration approach" + }, + baseColor: { + type: "string", + enum: ["slate", "gray", "zinc", "neutral", "stone"], + description: "Base color palette" + } + }, + required: [] + } +} as const; + +/** + * Build prompt for LLM-powered template selection + * + * @param userPrompt The user's original request + * @param allPrompts All available prompts from the template + * @param model The model being used (for model-specific prompts) + * @returns Prompt for LLM to select best template + */ +export function buildPromptSelectionPrompt( + userPrompt: string, + allPrompts: Prompts, + model?: string +): string { + const promptOptions: Array<{ id: string; preview: string; useCase: string }> = []; + + // NEW STRUCTURE: Task-specific prompts are top-level entries + // Each task has its own default and model_specific nested structure + + // Iterate through all top-level keys (excluding default, model_specific, prompt_strategy) + Object.entries(allPrompts).forEach(([taskType, taskPrompts]) => { + // Skip metadata keys + if (taskType === 'prompt_strategy' || taskType === 'default' || taskType === 'model_specific') { + return; + } + + // taskPrompts should have structure: { default: {...}, model_specific: {...} } + if (typeof taskPrompts === 'object' && taskPrompts !== null) { + // Extract default prompt for this task + if (taskPrompts.default) { + Object.entries(taskPrompts.default).forEach(([key, value]) => { + if (typeof value === 'string' && value.length > 0) { + promptOptions.push({ + id: `${taskType}.default.${key}`, + preview: value.substring(0, 100) + (value.length > 100 ? '...' : ''), + useCase: getUseCaseDescription(taskType) + }); + } + }); + } + + // Extract model-specific prompts for this task if model is provided + if (model && taskPrompts.model_specific) { + const modelKey = Object.keys(taskPrompts.model_specific).find(k => + model.includes(k) || k.includes(model) + ); + + if (modelKey) { + const modelPrompts = taskPrompts.model_specific[modelKey]; + Object.entries(modelPrompts).forEach(([key, value]) => { + if (typeof value === 'string' && value.length > 0) { + promptOptions.push({ + id: `${taskType}.model_specific.${modelKey}.${key}`, + preview: value.substring(0, 100) + (value.length > 100 ? '...' : ''), + useCase: getUseCaseDescription(taskType) + }); + } + }); + } + } + } + }); + + // Also add the general default and model_specific prompts (spawnerPrompt etc.) + if (allPrompts.default) { + Object.entries(allPrompts.default).forEach(([key, value]) => { + if (typeof value === 'string' && value.length > 0) { + promptOptions.push({ + id: `default.${key}`, + preview: value.substring(0, 100) + (value.length > 100 ? '...' : ''), + useCase: getUseCaseDescription(key) + }); + } + }); + } + + if (model && allPrompts.model_specific) { + const modelKey = Object.keys(allPrompts.model_specific).find(k => + model.includes(k) || k.includes(model) + ); + + if (modelKey) { + const modelPrompts = allPrompts.model_specific[modelKey]; + Object.entries(modelPrompts).forEach(([key, value]) => { + if (typeof value === 'string' && value.length > 0) { + promptOptions.push({ + id: `general.model_specific.${modelKey}.${key}`, + preview: value.substring(0, 100) + (value.length > 100 ? '...' : ''), + useCase: getUseCaseDescription(key) + }); + } + }); + } + } + + const optionsText = promptOptions.map((opt, idx) => + `${idx + 1}. ID: ${opt.id}\n Template: ${opt.preview}\n Use Case: ${opt.useCase}` + ).join('\n\n'); + + return `You are a prompt classification expert. Select the BEST template that matches the user's intent. + +User Request: "${userPrompt}" + +Available Templates: +${optionsText} + +Analyze the user's request and respond with JSON in this exact format: +{ + "selected_prompt_id": "", + "confidence": "", + "reasoning": "" +} + +Select the template that best matches the user's intent. Consider: +- Primary action (setup, add component, troubleshoot, configure theme) +- Specificity (model-specific prompts are more detailed) +- Context provided in user request`; +} + +/** + * Get human-readable description of prompt use case + */ +function getUseCaseDescription(promptKey: string): string { + const descriptions: Record = { + spawnerPrompt: 'Initial specialist introduction and capabilities', + project_setup: 'Setting up a new project from scratch', + component_add: 'Adding a component to existing project', + troubleshoot: 'Debugging issues in existing project', + theme_setup: 'Configuring theming and styling', + systemPrompt: 'System-level instructions', + contextPrompt: 'Additional context for requests' + }; + + return descriptions[promptKey] || 'General purpose prompt'; +} + +/** + * Parse LLM response for prompt selection + * + * @param llmResponse Raw response from LLM + * @returns Parsed selection result + * @throws Error if response cannot be parsed + */ +export function parsePromptSelectionResponse(llmResponse: string): PromptSelectionResult { + try { + // Try to extract JSON from markdown code blocks if present + const jsonMatch = llmResponse.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/) || + llmResponse.match(/(\{[\s\S]*\})/); + + if (!jsonMatch) { + throw new Error('No JSON found in LLM response'); + } + + const parsed = JSON.parse(jsonMatch[1]); + + if (!parsed.selected_prompt_id || !parsed.confidence || !parsed.reasoning) { + throw new Error('Missing required fields in parsed response'); + } + + // Validate confidence level + if (!['High', 'Medium', 'Low'].includes(parsed.confidence)) { + throw new Error(`Invalid confidence level: ${parsed.confidence}`); + } + + return { + selectedPromptId: parsed.selected_prompt_id, + confidence: parsed.confidence, + reasoning: parsed.reasoning + }; + } catch (error) { + if (error instanceof Error) { + throw new Error(`Failed to parse prompt selection response: ${error.message}`); + } + throw error; + } +} + +/** + * Build prompt for LLM-powered variable extraction + * + * @param userPrompt The user's original request + * @param selectedTemplate The selected template content with mustache placeholders + * @returns Prompt for LLM to extract variables + */ +export function buildVariableExtractionPrompt( + userPrompt: string, + selectedTemplate: string +): string { + return `Extract variables from user request to fill template placeholders. + +User Request: "${userPrompt}" + +Template Preview: ${selectedTemplate.substring(0, 300)}${selectedTemplate.length > 300 ? '...' : ''} + +Instructions: +- Extract ONLY explicitly mentioned or strongly implied variables +- Use defaults for common patterns (e.g., pnpm is recommended package manager) +- Leave undefined if not mentioned +- Be precise with names (e.g., "Vite" not "vite", exact component names) +- Extract description as a summary of the full request +- For features, list them comma-separated + +Call the extract_template_variables tool with the extracted values.`; +} + +/** + * Parse tool call response from LLM + * + * @param toolCall Tool call object from LLM response + * @returns Extracted variables + * @throws Error if tool call is invalid + */ +export function parseToolCallResponse(toolCall: any): ExtractedVariables { + try { + if (!toolCall || typeof toolCall !== 'object') { + throw new Error('Invalid tool call object'); + } + + // Handle different tool call formats (Claude vs OpenAI) + const input = toolCall.input || toolCall.arguments || toolCall; + + if (typeof input === 'string') { + return JSON.parse(input); + } + + if (typeof input === 'object') { + return input; + } + + throw new Error('Tool call input is not an object or valid JSON string'); + } catch (error) { + if (error instanceof Error) { + throw new Error(`Failed to parse tool call response: ${error.message}`); + } + throw error; + } +} + +/** + * Validate extracted variables against schema + * Ensures enum values are valid and applies defaults + * + * @param vars Extracted variables + * @returns Validated and normalized variables + */ +export function validateExtractedVariables(vars: ExtractedVariables): ExtractedVariables { + const validated: ExtractedVariables = { ...vars }; + + // Validate framework enum + if (validated.framework) { + const validFrameworks = ['Vite', 'Next.js', 'Remix', 'Astro', 'SvelteKit']; + if (!validFrameworks.includes(validated.framework)) { + // Try to normalize common variations + const normalized = normalizeFramework(validated.framework); + if (validFrameworks.includes(normalized)) { + validated.framework = normalized; + } else { + console.warn(`Invalid framework value: ${validated.framework}, using default: Vite`); + validated.framework = 'Vite'; + } + } + } + + // Validate packageManager enum + if (validated.packageManager) { + const validManagers = ['pnpm', 'npm', 'yarn', 'bun']; + if (!validManagers.includes(validated.packageManager.toLowerCase())) { + console.warn(`Invalid packageManager value: ${validated.packageManager}, using default: pnpm`); + validated.packageManager = 'pnpm'; + } else { + validated.packageManager = validated.packageManager.toLowerCase(); + } + } + + // Validate baseColor enum + if (validated.baseColor) { + const validColors = ['slate', 'gray', 'zinc', 'neutral', 'stone']; + if (!validColors.includes(validated.baseColor.toLowerCase())) { + console.warn(`Invalid baseColor value: ${validated.baseColor}, using default: slate`); + validated.baseColor = 'slate'; + } else { + validated.baseColor = validated.baseColor.toLowerCase() as any; + } + } + + return validated; +} + +/** + * Normalize framework name variations + */ +function normalizeFramework(framework: string): string { + const normalized = framework.toLowerCase().trim(); + + const mapping: Record = { + 'vite': 'Vite', + 'nextjs': 'Next.js', + 'next.js': 'Next.js', + 'next': 'Next.js', + 'remix': 'Remix', + 'astro': 'Astro', + 'sveltekit': 'SvelteKit', + 'svelte kit': 'SvelteKit', + 'svelte': 'SvelteKit' + }; + + return mapping[normalized] || framework; +} + +/** + * Apply default values for missing variables + * + * @param vars Extracted variables + * @param promptId The selected prompt ID to determine which defaults to apply + * @returns Variables with defaults applied + */ +export function applyDefaults(vars: ExtractedVariables, promptId: string): ExtractedVariables { + const withDefaults = { ...vars }; + + // Apply default package manager if not specified + if (!withDefaults.packageManager && promptId.includes('project_setup')) { + withDefaults.packageManager = 'pnpm'; + } + + // Apply default framework if not specified and it's a setup task + if (!withDefaults.framework && promptId.includes('project_setup')) { + withDefaults.framework = 'Vite'; + } + + // Apply default theme type if configuring theme + if (!withDefaults.themeType && promptId.includes('theme_setup')) { + withDefaults.themeType = 'CSS variables'; + } + + // Apply default base color if configuring theme + if (!withDefaults.baseColor && promptId.includes('theme_setup')) { + withDefaults.baseColor = 'slate'; + } + + return withDefaults; +} + +/** + * Create cache key from prompt for caching LLM results + * + * @param prompt The user prompt + * @returns Hash-like cache key + */ +export function createCacheKey(prompt: string): string { + // Simple hash function for cache keys + let hash = 0; + for (let i = 0; i < prompt.length; i++) { + const char = prompt.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash = hash & hash; // Convert to 32-bit integer + } + return `prompt_${Math.abs(hash).toString(36)}`; +} diff --git a/packages/agent-adapters/src/openrouter.ts b/packages/agent-adapters/src/openrouter.ts index 7328ced..8407778 100644 --- a/packages/agent-adapters/src/openrouter.ts +++ b/packages/agent-adapters/src/openrouter.ts @@ -1,10 +1,29 @@ import { config } from 'dotenv'; import { OpenAI } from 'openai'; +import chalk from 'chalk'; import type { AgentAdapter, AgentRequest, AgentResponse } from "./index.ts"; -import { resolve } from 'node:path'; +import { resolve, join } from 'node:path'; +import { existsSync } from 'node:fs'; // Load environment variables from .env file in project root -config({ path: resolve(process.cwd(), '.env') }); +// Find workspace root by looking for pnpm-workspace.yaml (topmost one) +function findWorkspaceRoot(startDir: string): string { + let currentDir = startDir; + let lastWorkspaceRoot = startDir; + + while (currentDir !== resolve(currentDir, '..')) { + if (existsSync(join(currentDir, 'pnpm-workspace.yaml'))) { + lastWorkspaceRoot = currentDir; + } + currentDir = resolve(currentDir, '..'); + } + + return lastWorkspaceRoot; +} + +const workspaceRoot = findWorkspaceRoot(process.cwd()); +const envPath = resolve(workspaceRoot, '.env'); +config({ path: envPath }); interface ToolResult { tool_call_id: string; @@ -151,20 +170,56 @@ export class OpenRouterAdapter implements AgentAdapter { const apiRequest = this.buildInitialRequest(request); const maxIterations = this.DEFAULT_MAX_ITERATIONS; + // Log tools configuration on first call + console.log(chalk.blue(`[OpenRouterAdapter] Tools available: ${request.tools?.length || 0}`)); + if (request.tools && request.tools.length > 0) { + console.log(chalk.gray(`[OpenRouterAdapter] Tool names: ${request.tools.map((t: any) => t.name || t.function?.name).join(', ')}`)); + console.log(chalk.gray(`[OpenRouterAdapter] Tools in API request: ${apiRequest.tools ? 'YES' : 'NO'}`)); + console.log(chalk.gray(`[OpenRouterAdapter] Tool choice: ${apiRequest.tool_choice || 'not set'}`)); + } + + // Store the transformed tools so we can reuse them on subsequent turns + const transformedTools = apiRequest.tools; + let totalToolCalls = 0; let totalInputTokens = 0; let totalOutputTokens = 0; // Multi-turn conversation loop for (let turn = 0; turn < maxIterations; turn++) { + console.log(chalk.blue(`[OpenRouterAdapter] Turn ${turn + 1}/${maxIterations}`)); + console.log(chalk.gray(`[OpenRouterAdapter] Sending ${apiRequest.messages.length} messages to API`)); + if (turn > 0) { + // Log last 2 messages to see conversation history + const lastMessages = apiRequest.messages.slice(-2); + lastMessages.forEach((msg: any, idx: number) => { + const role = msg.role; + const preview = msg.content ? msg.content.substring(0, 100) : (msg.tool_calls ? `[${msg.tool_calls.length} tool calls]` : '[no content]'); + console.log(chalk.gray(` Message ${apiRequest.messages.length - 2 + idx + 1}: ${role} - ${preview}`)); + }); + } + try { const response = await this.client.chat.completions.create(apiRequest); - + const message = response.choices[0]?.message; if (!message) { throw new Error('No message in response'); } - + + // Log the full response for debugging + console.log(chalk.gray(`[OpenRouterAdapter] Response finish_reason: ${response.choices[0]?.finish_reason}`)); + console.log(chalk.gray(`[OpenRouterAdapter] Message content: ${message.content ? `${message.content.length} chars` : 'null/empty'}`)); + if (message.content) { + console.log(chalk.gray(`[OpenRouterAdapter] Full content:\n${message.content}`)); + } else { + console.log(chalk.yellow(`[OpenRouterAdapter] ⚠️ Agent returned empty content with finish_reason=${response.choices[0]?.finish_reason}`)); + } + console.log(chalk.gray(`[OpenRouterAdapter] Tool calls present: ${!!message.tool_calls}`)); + if (message.tool_calls) { + console.log(chalk.gray(`[OpenRouterAdapter] Number of tool calls: ${message.tool_calls.length}`)); + } + totalInputTokens += response.usage?.prompt_tokens || 0; totalOutputTokens += response.usage?.completion_tokens || 0; @@ -176,10 +231,10 @@ export class OpenRouterAdapter implements AgentAdapter { try { const content = message.content.trim(); const parsed = JSON.parse(content); - + // Handle array of tool calls const toolDescriptions = Array.isArray(parsed) ? parsed : [parsed]; - + for (const desc of toolDescriptions) { if (desc.name && (desc.parameters || desc.arguments)) { toolCalls.push({ @@ -196,18 +251,35 @@ export class OpenRouterAdapter implements AgentAdapter { // Not JSON or not a tool description - treat as final response } } - + + // Log response type + if (toolCalls.length > 0) { + console.log(chalk.green(`[OpenRouterAdapter] Agent requested ${toolCalls.length} tool(s):`)); + toolCalls.forEach((tc: any, idx: number) => { + console.log(chalk.gray(` ${idx + 1}. ${tc.function.name}`)); + }); + } else if (message.content) { + console.log(chalk.yellow(`[OpenRouterAdapter] Agent returned text response (no tools)`)); + const preview = message.content.substring(0, 80).replace(/\n/g, ' '); + console.log(chalk.gray(` Content preview: ${preview}${message.content.length > 80 ? '...' : ''}`)); + console.log(chalk.gray(` Full content:\n${message.content}`)); + } + // If no tools requested, we're done if (toolCalls.length === 0) { const content = message.content || ''; const modelId = apiRequest.model; + console.log(chalk.blue(`[OpenRouterAdapter] Stopping: No tools requested (after ${turn + 1} turns)`)); + console.log(chalk.blue(`[OpenRouterAdapter] Total tool calls made: ${totalToolCalls}`)); return this.buildResponse(content, totalInputTokens, totalOutputTokens, totalToolCalls, modelId); } // Process all tool calls + console.log(chalk.blue(`[OpenRouterAdapter] Executing ${toolCalls.length} tool(s)...`)); totalToolCalls += toolCalls.length; - + const toolResults = await this.executeTools(toolCalls, request.toolHandlers); + console.log(chalk.green(`[OpenRouterAdapter] ✓ All tools executed`)); // Update request for next turn const assistantMessage = { @@ -228,9 +300,9 @@ export class OpenRouterAdapter implements AgentAdapter { ...toolMessages ]; - // Ensure tools are included in every request - if (request.tools && request.tools.length > 0) { - apiRequest.tools = request.tools; + // Ensure tools are included in every request (use transformed tools) + if (transformedTools && transformedTools.length > 0) { + apiRequest.tools = transformedTools; apiRequest.tool_choice = "auto"; } @@ -257,6 +329,8 @@ export class OpenRouterAdapter implements AgentAdapter { } // Max turns reached + console.log(chalk.red(`[OpenRouterAdapter] Stopping: Max iterations (${maxIterations}) reached`)); + console.log(chalk.blue(`[OpenRouterAdapter] Total tool calls made: ${totalToolCalls}`)); const modelId = apiRequest.model; return this.buildResponse('Max tool calling iterations reached', totalInputTokens, totalOutputTokens, totalToolCalls, modelId); } @@ -280,6 +354,13 @@ export class OpenRouterAdapter implements AgentAdapter { temperature: 0.1 }; + // Add provider restriction for Anthropic models + if (this.model.startsWith('anthropic/')) { + params.provider = { + only: ['anthropic'] + }; + } + if (system) { params.messages = [ { role: "system", content: system }, @@ -288,7 +369,27 @@ export class OpenRouterAdapter implements AgentAdapter { } if (request.tools && request.tools.length > 0) { - params.tools = request.tools; + // Check if tools are already in OpenRouter format (have .function property) + // or still in Anthropic format (have .input_schema property) + const firstTool = request.tools[0] as any; + const alreadyTransformed = firstTool.type === 'function' && firstTool.function; + + if (alreadyTransformed) { + // Tools already in OpenRouter format, use as-is + params.tools = request.tools; + console.log(chalk.gray(`[OpenRouterAdapter] Tools already in OpenRouter format`)); + } else { + // Transform tools from Anthropic format (input_schema) to OpenAI format (function.parameters) + params.tools = request.tools.map((tool: any) => ({ + type: 'function', + function: { + name: tool.name, + description: tool.description, + parameters: tool.input_schema || tool.parameters + } + })); + console.log(chalk.gray(`[OpenRouterAdapter] Transformed tools from Anthropic to OpenRouter format`)); + } params.tool_choice = "auto"; } @@ -302,20 +403,23 @@ export class OpenRouterAdapter implements AgentAdapter { const results: ToolResult[] = []; for (const toolCall of toolCalls) { - const handler = toolHandlers?.get(toolCall.function.name); + const toolName = toolCall.function.name; + const handler = toolHandlers?.get(toolName); let resultContent: string; if (handler) { + console.log(chalk.blue(`[OpenRouterAdapter] Executing tool: ${toolName}`)); try { const input = JSON.parse(toolCall.function.arguments || '{}'); resultContent = await handler(input); + console.log(chalk.green(`[OpenRouterAdapter] ✓ ${toolName} completed`)); } catch (error) { - console.error(` ❌ Error in ${toolCall.function.name}:`, error); + console.error(chalk.red(`[OpenRouterAdapter] ❌ Error in ${toolName}:`), error); resultContent = `Error: ${error instanceof Error ? error.message : String(error)}`; } } else { - console.warn(` ⚠️ No handler found for tool: ${toolCall.function.name}`); - resultContent = `Tool '${toolCall.function.name}' is not available`; + console.warn(chalk.yellow(`[OpenRouterAdapter] ⚠️ No handler found for tool: ${toolName}`)); + resultContent = `Tool '${toolName}' is not available`; } results.push({ diff --git a/packages/agent-adapters/src/specialist.ts b/packages/agent-adapters/src/specialist.ts new file mode 100644 index 0000000..34f5a95 --- /dev/null +++ b/packages/agent-adapters/src/specialist.ts @@ -0,0 +1,1134 @@ +import { readFileSync, existsSync, readdirSync } from 'node:fs'; +import { resolve, dirname, basename, join } from 'node:path'; +import JSON5 from 'json5'; +import { OpenAI } from 'openai'; +import type { AgentAdapter, AgentRequest, AgentResponse } from "./index.ts"; +import type { SpecialistTemplate, TaskType, ExtractedIntent, SpecialistSelection, DocumentationReference } from 'agency-prompt-creator'; +import { + createPrompt, + substituteTemplate, + buildTemplateContext as buildPromptCreatorContext, + buildIntentExtractionPrompt, + parseIntentResponse, + INTENT_EXTRACTION_TOOL, + buildComponentSelectionPrompt, + parseComponentSelectionResponse, + COMPONENT_SELECTION_TOOL, + substituteWithLLM, + SUBSTITUTION_TOOL +} from 'agency-prompt-creator'; +import { + buildPromptSelectionPrompt, + parsePromptSelectionResponse, + buildVariableExtractionPrompt, + parseToolCallResponse, + validateExtractedVariables, + applyDefaults, + createCacheKey, + VARIABLE_EXTRACTION_TOOL, + type PromptSelectionResult, + type ExtractedVariables +} from './llm-prompt-selector.js'; +import { LLMCache } from './llm-cache.js'; + +/** + * LLM configuration from template or environment + */ +interface LLMConfig { + enabled: boolean; + provider: 'openrouter' | 'anthropic'; + selectionModel: string; + extractionModel: string; + timeoutMs: number; + cacheTtlMs: number; + fallbackToStatic: boolean; +} + +/** + * Telemetry data for LLM operations + */ +interface LLMTelemetry { + intent_extraction?: { + intent: ExtractedIntent; + duration_ms: number; + model: string; + cache_hit: boolean; + }; + component_selection?: { + selection: SpecialistSelection; + duration_ms: number; + model: string; + cache_hit: boolean; + }; + substitution?: { + spawner_duration_ms: number; + task_duration_ms: number; + model: string; + }; + // Legacy fields (for backward compatibility if needed) + llm_prompt_selection?: { + enabled: boolean; + selected_prompt_id: string; + confidence: string; + duration_ms: number; + model: string; + cache_hit: boolean; + }; + llm_variable_extraction?: { + extracted_vars: string[]; + var_values: Record; + duration_ms: number; + model: string; + cache_hit: boolean; + }; +} + +/** + * SpecialistAdapter + * + * Decorator adapter that wraps an underlying AgentAdapter (Anthropic, OpenRouter, etc.) + * and enhances it with specialist template-based prompt transformation. + * + * This adapter: + * 1. Loads a specialist template from the filesystem + * 2. Uses agency-prompt-creator to transform user prompts based on: + * - Detected task type + * - Model-specific prompts + * - Mustache template substitution + * 3. Optionally uses LLM-powered prompt selection and variable extraction + * 4. Delegates actual model communication to the underlying adapter + * + * Usage: + * const anthropic = new AnthropicAdapter(); + * const specialist = new SpecialistAdapter( + * anthropic, + * 'agency-specialist-mint/snapshots/shadcn-specialist/1.0.0/snapshot-001.json5' + * ); + * const response = await specialist.send(request); + */ +export class SpecialistAdapter implements AgentAdapter { + name: string; + private readonly underlyingAdapter: AgentAdapter; + private readonly template: SpecialistTemplate; + private readonly templatePath: string; + private lastTransformedMessages?: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>; + + // LLM-powered selection + private readonly llmConfig: LLMConfig; + private readonly llmCache: LLMCache; + private llmClient?: OpenAI; + private llmTelemetry: LLMTelemetry = {}; + + /** + * Create a new specialist adapter + * + * @param underlyingAdapter - The base adapter to wrap (AnthropicAdapter, OpenRouterAdapter, etc.) + * @param templatePath - Path to specialist template or snapshot JSON5 file (relative to project root) + */ + constructor(underlyingAdapter: AgentAdapter, templatePath: string) { + this.underlyingAdapter = underlyingAdapter; + this.templatePath = templatePath; + this.template = this.loadTemplate(templatePath); + this.name = `specialist:${this.template.name}:${underlyingAdapter.name}`; + + // Initialize LLM configuration + this.llmConfig = this.loadLLMConfig(); + + // Initialize cache + this.llmCache = new LLMCache(this.llmConfig.cacheTtlMs); + + // Initialize LLM client if enabled + if (this.llmConfig.enabled) { + this.initializeLLMClient(); + } + } + + /** + * Load and parse specialist template from filesystem + * Automatically uses enriched template if available + * + * @param templatePath - Path to JSON5 template file + * @returns Parsed specialist template + * @throws Error if file not found or invalid JSON5 + */ + private loadTemplate(templatePath: string): SpecialistTemplate { + try { + console.log('[SpecialistAdapter] Loading template from:', templatePath); + + // Resolve template path (automatically use enriched version if available) + const resolvedPath = this.resolveTemplatePath(templatePath); + + const contents = readFileSync(resolvedPath, 'utf-8'); + const template = JSON5.parse(contents) as SpecialistTemplate; + + console.log('[SpecialistAdapter] Template loaded:', template.name, 'version:', template.version); + console.log('[SpecialistAdapter] Documentation entries:', template.documentation?.length || 0); + + // Basic validation + if (!template.name || !template.prompts) { + throw new Error( + `Invalid specialist template: missing required fields 'name' or 'prompts'` + ); + } + + return template; + } catch (error) { + if (error instanceof Error) { + if (error.message.includes('ENOENT')) { + throw new Error( + `Specialist template not found: ${templatePath}\n` + + `Make sure the path is relative to project root and the file exists.` + ); + } + throw new Error(`Failed to load specialist template: ${error.message}`); + } + throw error; + } + } + + /** + * Resolve template path, automatically using latest enriched version if available + * + * NEW STRUCTURE: + * - Original: starting_from_outcome/shadcn-specialist-template.json5 + * - Enriched: starting_from_outcome/enriched/0.0.5/enriched-001.json5 + * starting_from_outcome/enriched/0.0.5/enriched-002.json5 + * ... (always use highest number) + */ + private resolveTemplatePath(templatePath: string): string { + const absolutePath = resolve(process.cwd(), templatePath); + + // If path is already an enriched template, use it + if (this.isEnrichedTemplatePath(absolutePath)) { + console.log(`[SpecialistAdapter] Using explicitly specified enriched template: ${absolutePath}`); + return absolutePath; + } + + // Try to find latest enriched version + try { + const contents = readFileSync(absolutePath, 'utf-8'); + const template = JSON5.parse(contents) as SpecialistTemplate; + const latestEnrichedPath = this.getLatestEnrichedTemplatePath(absolutePath, template.version); + + if (latestEnrichedPath) { + console.log(`[SpecialistAdapter] Using enriched template: ${latestEnrichedPath}`); + return latestEnrichedPath; + } else { + console.warn(`[SpecialistAdapter] No enriched template found for version ${template.version}`); + console.warn(`[SpecialistAdapter] Expected location: ${this.getEnrichedDir(absolutePath, template.version)}`); + console.warn(`[SpecialistAdapter] Using original template. Run enrichment to generate enriched template.`); + } + } catch (error) { + // If we can't read the template, fall back to original path + console.warn(`[SpecialistAdapter] Failed to check for enriched template:`, error instanceof Error ? error.message : 'Unknown error'); + } + + return absolutePath; + } + + /** + * Check if a path is an enriched template + */ + private isEnrichedTemplatePath(path: string): boolean { + return path.includes('/enriched/') && path.includes('enriched-') && path.endsWith('.json5'); + } + + /** + * Get enriched directory for a template version + */ + private getEnrichedDir(templatePath: string, version: string): string { + const dir = dirname(templatePath); + return join(dir, 'enriched', version); + } + + /** + * Get latest enriched template path for a given template path and version + * Returns null if no enriched templates exist + */ + private getLatestEnrichedTemplatePath(templatePath: string, version: string): string | null { + const enrichedDir = this.getEnrichedDir(templatePath, version); + + if (!existsSync(enrichedDir)) { + return null; + } + + try { + // Find all enriched-NNN.json5 files + const files = readdirSync(enrichedDir); + const enrichedFiles = files + .filter(f => f.match(/^enriched-(\d+)\.json5$/)) + .map(f => { + const match = f.match(/^enriched-(\d+)\.json5$/); + return match ? { filename: f, number: parseInt(match[1], 10) } : null; + }) + .filter((f): f is { filename: string; number: number } => f !== null) + .sort((a, b) => b.number - a.number); // Sort descending + + if (enrichedFiles.length === 0) { + return null; + } + + // Return the highest numbered file + return join(enrichedDir, enrichedFiles[0].filename); + } catch { + return null; + } + } + + /** + * Send a request with specialist prompt transformation (NEW 3-STEP WORKFLOW) + * + * This method implements the new 3-step workflow: + * - Step 3a: Extract intent using LLM + * - Step 3b: Select components using LLM (spawner prompt, task prompt, docs) + * - Step 3c: Create system prompt via LLM substitution + concatenation + * - Step 3d: Submit original user prompt + system prompt + * + * @param request - Agent request with messages, tools, etc. + * @returns Agent response from underlying adapter + */ + async send(request: AgentRequest): Promise { + try { + // Check validation mode - passthrough if pre-exported prompts + if (this.isValidationMode(request)) { + console.log('[SpecialistAdapter] Validation mode: using pre-exported system prompt'); + return this.underlyingAdapter.send(request); + } + + // Extract user prompt - NEVER modified + const userPrompt = this.extractUserPrompt(request); + if (!userPrompt) { + throw new Error('No user message found in request'); + } + + console.log('[SpecialistAdapter] ========================================'); + console.log('[SpecialistAdapter] Original user prompt:', userPrompt.substring(0, 200)); + console.log('[SpecialistAdapter] ========================================'); + + // ======================================================================== + // STEP 3a: Extract Intent + // ======================================================================== + console.log('[SpecialistAdapter] Step 3a: Extracting intent...'); + const intentStart = Date.now(); + const intent = await this.extractIntentWithLLM(userPrompt); + const intentDuration = Date.now() - intentStart; + + console.log('[SpecialistAdapter] Step 3a - Extracted intent:'); + console.log(' Intent:', intent.intent); + console.log(' Primary goal:', intent.primaryGoal); + console.log(' Keywords:', intent.keywords.join(', ')); + if (intent.framework) console.log(' Framework:', intent.framework); + if (intent.components) console.log(' Components:', intent.components.join(', ')); + if (intent.packageManager) console.log(' Package manager:', intent.packageManager); + if (intent.features) console.log(' Features:', intent.features.join(', ')); + console.log(' Duration:', intentDuration, 'ms'); + + // ======================================================================== + // STEP 3b: Select Specialist Components + // ======================================================================== + console.log('[SpecialistAdapter] Step 3b: Selecting specialist components...'); + const selectionStart = Date.now(); + const selection = await this.selectComponentsWithLLM(userPrompt, intent); + const selectionDuration = Date.now() - selectionStart; + + console.log('[SpecialistAdapter] Step 3b - Selected components:'); + console.log(' Spawner prompt ID:', selection.spawnerPromptId); + console.log(' Task prompt ID:', selection.taskPromptId); + console.log(' Relevant tags:', selection.relevantTags.join(', ')); + console.log(' Relevant tech stack:', selection.relevantTechStack.join(', ')); + console.log(' Documentation count:', selection.documentation.length); + if (selection.documentation.length > 0) { + console.log(' Documentation URLs:'); + selection.documentation.forEach(doc => console.log(' -', doc.url)); + } + console.log(' Reasoning:', selection.reasoning); + console.log(' Duration:', selectionDuration, 'ms'); + + // ======================================================================== + // STEP 3c: Create System Prompt + // ======================================================================== + console.log('[SpecialistAdapter] Step 3c: Creating system prompt...'); + const systemPrompt = await this.createSystemPrompt(userPrompt, intent, selection); + + console.log('[SpecialistAdapter] Step 3c - System prompt created:'); + console.log(' Length:', systemPrompt.length, 'characters'); + console.log(' Contains CRITICAL marker:', systemPrompt.includes('⚠️ CRITICAL')); + console.log(' Contains documentation URLs:', systemPrompt.includes('http')); + console.log(' Preview (first 300 chars):', systemPrompt.substring(0, 300)); + + // ======================================================================== + // STEP 3d: Submit to LLM + // ======================================================================== + console.log('[SpecialistAdapter] Step 3d: Submitting to LLM...'); + console.log(' User prompt (ORIGINAL, UNCHANGED):', userPrompt.substring(0, 100)); + console.log(' System prompt length:', systemPrompt.length); + + // Build request with: + // - System prompt (from 3c) + // - Original user prompt (UNCHANGED) + const transformedMessages = this.injectSystemPrompt(request.messages, systemPrompt); + const modifiedRequest: AgentRequest = { + ...request, + messages: transformedMessages + }; + + // Store for telemetry/debugging + this.lastTransformedMessages = transformedMessages; + this.llmTelemetry = { + intent_extraction: { + intent, + duration_ms: intentDuration, + model: this.llmConfig.extractionModel, + cache_hit: false // TODO: track cache hits + }, + component_selection: { + selection, + duration_ms: selectionDuration, + model: this.llmConfig.selectionModel, + cache_hit: false // TODO: track cache hits + } + }; + + console.log('[SpecialistAdapter] ========================================'); + console.log('[SpecialistAdapter] Delegating to underlying adapter...'); + console.log('[SpecialistAdapter] ========================================'); + + return this.underlyingAdapter.send(modifiedRequest); + } catch (error) { + // Re-throw with more context about specialist adapter + if (error instanceof Error) { + throw new Error( + `SpecialistAdapter (${this.template.name}) error: ${error.message}` + ); + } + throw error; + } + } + + /** + * Extract the primary user prompt from the request + * Uses the last user message as the primary prompt + */ + private extractUserPrompt(request: AgentRequest): string { + // Find last user message + const userMessages = request.messages.filter(m => m.role === 'user'); + const lastUserMessage = userMessages[userMessages.length - 1]; + + return lastUserMessage?.content || ''; + } + + /** + * Get the model name being used + * Tries to extract from environment variables based on adapter type + */ + private getModelName(): string | undefined { + // Try to get model from environment based on underlying adapter + if (this.underlyingAdapter.name === 'anthropic') { + return process.env.CLAUDE_MODEL; + } else if (this.underlyingAdapter.name === 'openrouter') { + return process.env.OPENROUTER_MODEL; + } + return undefined; + } + + /** + * Build template context for mustache substitution + * Extracts useful metadata from the request + */ + private buildTemplateContext(request: AgentRequest): Record { + return { + workspaceDir: request.workspaceDir, + hasTools: request.tools && request.tools.length > 0, + toolCount: request.tools?.length || 0, + // Add other useful context as needed + }; + } + + /** + * Inject or replace system prompt in message list + * If a system message exists, replace it. Otherwise, add one. + */ + private injectSystemPrompt( + messages: AgentRequest['messages'], + systemPrompt: string + ): AgentRequest['messages'] { + const systemIndex = messages.findIndex(m => m.role === 'system'); + + if (systemIndex >= 0) { + // Replace existing system message + return [ + ...messages.slice(0, systemIndex), + { role: 'system', content: systemPrompt }, + ...messages.slice(systemIndex + 1) + ]; + } else { + // Add system message at the beginning + return [ + { role: 'system', content: systemPrompt }, + ...messages + ]; + } + } + + /** + * Get the last transformed messages (after specialist prompt was applied) + * Useful for logging/debugging what was actually sent to the model + * + * @returns The transformed messages from the last send() call, or undefined if no send() has been called yet + */ + getLastTransformedMessages(): Array<{ role: 'system' | 'user' | 'assistant'; content: string }> | undefined { + return this.lastTransformedMessages; + } + + /** + * Get LLM telemetry data from last operation + */ + getLLMTelemetry(): LLMTelemetry { + return this.llmTelemetry; + } + + // ========================================================================= + // NEW 3-STEP WORKFLOW METHODS + // ========================================================================= + + /** + * Check if we're in validation mode (pre-exported prompts) + */ + private isValidationMode(request: AgentRequest): boolean { + const systemMessage = request.messages.find(m => m.role === 'system'); + + return ( + systemMessage !== undefined && + systemMessage.content.includes('⚠️ CRITICAL: Required Documentation Reading') && + process.env.SPECIALIST_VALIDATION_MODE === 'true' + ); + } + + /** + * Step 3a: Extract intent using LLM + */ + private async extractIntentWithLLM(userPrompt: string): Promise { + if (!this.llmClient) { + throw new Error('LLM client not initialized'); + } + + const cacheKey = createCacheKey(userPrompt + ':intent'); + const cached = this.llmCache.get(cacheKey); + if (cached) { + console.log('[SpecialistAdapter] Using cached intent extraction'); + return cached as ExtractedIntent; + } + + const prompt = buildIntentExtractionPrompt(userPrompt); + + try { + const response = await Promise.race([ + this.llmClient.chat.completions.create({ + model: this.llmConfig.extractionModel, + messages: [{ role: 'user', content: prompt }], + tools: [INTENT_EXTRACTION_TOOL as any], + tool_choice: { type: 'function', function: { name: 'extract_intent' } } as any, + temperature: 0.1 + }), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Intent extraction timeout')), this.llmConfig.timeoutMs) + ) + ]); + + const toolCall = response.choices[0]?.message?.tool_calls?.[0]; + if (!toolCall) { + throw new Error('No tool call in intent extraction response'); + } + + const intent = parseIntentResponse(toolCall.function.arguments); + this.llmCache.set(cacheKey, intent); + return intent; + } catch (error) { + if (error instanceof Error) { + throw new Error(`Intent extraction failed: ${error.message}`); + } + throw error; + } + } + + /** + * Step 3b: Select specialist components using LLM + */ + private async selectComponentsWithLLM( + userPrompt: string, + intent: ExtractedIntent + ): Promise { + if (!this.llmClient) { + throw new Error('LLM client not initialized'); + } + + const cacheKey = createCacheKey(userPrompt + ':selection'); + const cached = this.llmCache.get(cacheKey); + if (cached) { + console.log('[SpecialistAdapter] Using cached component selection'); + return cached as SpecialistSelection; + } + + const prompt = buildComponentSelectionPrompt(userPrompt, intent, this.template); + + try { + const response = await Promise.race([ + this.llmClient.chat.completions.create({ + model: this.llmConfig.selectionModel, + messages: [{ role: 'user', content: prompt }], + tools: [COMPONENT_SELECTION_TOOL as any], + tool_choice: { type: 'function', function: { name: 'select_specialist_components' } } as any, + temperature: 0.1 + }), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Component selection timeout')), this.llmConfig.timeoutMs) + ) + ]); + + const toolCall = response.choices[0]?.message?.tool_calls?.[0]; + if (!toolCall) { + throw new Error('No tool call in component selection response'); + } + + const selection = parseComponentSelectionResponse(toolCall.function.arguments, this.template); + this.llmCache.set(cacheKey, selection); + return selection; + } catch (error) { + if (error instanceof Error) { + throw new Error(`Component selection failed: ${error.message}`); + } + throw error; + } + } + + /** + * Step 3c: Create system prompt via LLM substitution + concatenation + */ + private async createSystemPrompt( + userPrompt: string, + intent: ExtractedIntent, + selection: SpecialistSelection + ): Promise { + // 1. Get spawner prompt content + const spawnerPromptContent = this.getPromptById(selection.spawnerPromptId); + if (!spawnerPromptContent) { + throw new Error(`Spawner prompt not found: ${selection.spawnerPromptId}`); + } + + // 2. Get task prompt content + const taskPromptContent = this.getPromptById(selection.taskPromptId); + if (!taskPromptContent) { + throw new Error(`Task prompt not found: ${selection.taskPromptId}`); + } + + // 3. Build context for substitution + const context = { + name: this.template.name, + version: this.template.version, + specialistName: this.template.name, + framework: intent.framework, + packageManager: intent.packageManager, + techStack: selection.relevantTechStack.join(', '), + tags: selection.relevantTags.join(', '), + components: intent.components?.join(', '), + features: intent.features?.join(', '), + ...selection + }; + + // 4. LLM-based substitution on spawner prompt + const spawnerStart = Date.now(); + const substitutedSpawner = await substituteWithLLM( + this.llmClient!, + this.llmConfig.extractionModel, + spawnerPromptContent, + userPrompt, + intent, + context + ); + const spawnerDuration = Date.now() - spawnerStart; + + console.log('[SpecialistAdapter] Step 3c - Spawner prompt substituted:'); + console.log(' Original length:', spawnerPromptContent.length); + console.log(' Substituted length:', substitutedSpawner.length); + console.log(' Duration:', spawnerDuration, 'ms'); + + // 5. LLM-based substitution on task prompt + const taskStart = Date.now(); + const substitutedTask = await substituteWithLLM( + this.llmClient!, + this.llmConfig.extractionModel, + taskPromptContent, + userPrompt, + intent, + context + ); + const taskDuration = Date.now() - taskStart; + + console.log('[SpecialistAdapter] Step 3c - Task prompt substituted:'); + console.log(' Original length:', taskPromptContent.length); + console.log(' Substituted length:', substitutedTask.length); + console.log(' Duration:', taskDuration, 'ms'); + + // Store substitution telemetry + if (this.llmTelemetry.substitution) { + this.llmTelemetry.substitution.spawner_duration_ms = spawnerDuration; + this.llmTelemetry.substitution.task_duration_ms = taskDuration; + } else { + this.llmTelemetry.substitution = { + spawner_duration_ms: spawnerDuration, + task_duration_ms: taskDuration, + model: this.llmConfig.extractionModel + }; + } + + // 6. Format documentation section with CRITICAL marker + const docSection = this.formatDocumentationSection(selection.documentation); + + console.log('[SpecialistAdapter] Step 3c - Documentation section formatted:'); + console.log(' Documentation section length:', docSection.length); + console.log(' Has CRITICAL marker:', docSection.includes('⚠️ CRITICAL')); + + // 7. String concatenation ONLY - no interpretation + const systemPrompt = [ + substitutedSpawner, + '', + substitutedTask, + '', + docSection + ].join('\n'); + + return systemPrompt; + } + + /** + * Format documentation section with CRITICAL marker + */ + private formatDocumentationSection(docs: DocumentationReference[]): string { + if (docs.length === 0) { + return ''; + } + + let section = '\n## ⚠️ CRITICAL: Required Documentation Reading\n\n'; + section += '**YOU MUST START BY USING THE WEB FETCH TOOL TO READ THESE DOCUMENTATION PAGES BEFORE PROCEEDING WITH ANY IMPLEMENTATION.**\n\n'; + section += 'These documentation pages contain essential patterns and configurations that you must follow exactly.\n\n'; + + for (const doc of docs) { + section += `### ${doc.title}\n\n`; + section += `**📄 Documentation URL**: ${doc.url}\n`; + section += `**YOU MUST READ THIS PAGE FIRST**\n\n`; + section += `**Summary**: ${doc.summary}\n\n`; + + if (doc.keyConcepts.length > 0) { + section += `**Key Concepts**: ${doc.keyConcepts.join(', ')}\n\n`; + } + + if (doc.codePatterns.length > 0) { + section += '**Code Patterns from Documentation**:\n'; + for (const pattern of doc.codePatterns) { + section += `- \`${pattern}\`\n`; + } + section += '\n'; + } + + section += '---\n\n'; + } + + return section; + } + + // ========================================================================= + // LLM-POWERED PROMPT SELECTION AND VARIABLE EXTRACTION (LEGACY) + // ========================================================================= + + /** + * Load LLM configuration from template and environment variables + */ + private loadLLMConfig(): LLMConfig { + // Check template llm_config + const templateConfig = (this.template as any).llm_config; + + return { + enabled: true, // Always enabled + provider: templateConfig?.provider || 'openrouter', + selectionModel: process.env.LLM_SELECTION_MODEL || templateConfig?.selection_model || 'anthropic/claude-3.5-haiku', + extractionModel: process.env.LLM_EXTRACTION_MODEL || templateConfig?.extraction_model || 'anthropic/claude-3.5-haiku', + timeoutMs: parseInt(process.env.LLM_SELECTION_TIMEOUT || '') || templateConfig?.timeout_ms || 10000, + cacheTtlMs: templateConfig?.cache_ttl_ms || 3600000, // 1 hour default + fallbackToStatic: templateConfig?.fallback_to_static ?? true + }; + } + + /** + * Initialize LLM client based on configuration + */ + private initializeLLMClient(): void { + if (this.llmConfig.provider === 'openrouter') { + const apiKey = process.env.OPENROUTER_API_KEY; + if (!apiKey) { + console.warn('[SpecialistAdapter] LLM selection enabled but OPENROUTER_API_KEY not found, disabling LLM selection'); + (this.llmConfig as any).enabled = false; + return; + } + + this.llmClient = new OpenAI({ + baseURL: 'https://openrouter.ai/api/v1', + apiKey: apiKey + }); + } else if (this.llmConfig.provider === 'anthropic') { + const apiKey = process.env.ANTHROPIC_API_KEY; + if (!apiKey) { + console.warn('[SpecialistAdapter] LLM selection enabled but ANTHROPIC_API_KEY not found, disabling LLM selection'); + (this.llmConfig as any).enabled = false; + return; + } + + this.llmClient = new OpenAI({ + baseURL: 'https://api.anthropic.com/v1', + apiKey: apiKey, + defaultHeaders: { + 'anthropic-version': '2023-06-01' + } + }); + } + } + + /** + * Create prompt using LLM-powered selection and variable extraction + * + * @param userPrompt User's original request + * @param model Model being used + * @param request Agent request for context building + * @returns Final prompt with variables substituted + */ + private async createPromptWithLLM(userPrompt: string, model?: string, request?: AgentRequest): Promise { + const cacheKey = createCacheKey(userPrompt); + + // Phase 1: Select best prompt template + console.log('[SpecialistAdapter] Phase 1: Selecting prompt template'); + + const selectionStart = Date.now(); + let selectionResult: PromptSelectionResult; + let selectionCacheHit = false; + + const cachedSelection = this.llmCache.getSelection(cacheKey); + if (cachedSelection) { + selectionResult = cachedSelection; + selectionCacheHit = true; + console.log(' Using cached selection'); + } else { + selectionResult = await this.selectPromptWithLLM(userPrompt, model); + this.llmCache.setSelection(cacheKey, selectionResult); + console.log(' LLM selected prompt'); + } + + console.log(' Selected prompt ID:', selectionResult.selectedPromptId); + console.log(' Confidence:', selectionResult.confidence); + console.log(' Reasoning:', selectionResult.reasoning); + + const selectionDuration = Date.now() - selectionStart; + + // Phase 2: Extract variables for substitution + const extractionStart = Date.now(); + let extractedVars: ExtractedVariables; + let extractionCacheHit = false; + + const cachedVars = this.llmCache.getVariables(cacheKey); + if (cachedVars) { + extractedVars = cachedVars; + extractionCacheHit = true; + } else { + extractedVars = await this.extractVariablesWithLLM(userPrompt, selectionResult.selectedPromptId); + this.llmCache.setVariables(cacheKey, extractedVars); + } + + const extractionDuration = Date.now() - extractionStart; + + // Store telemetry + this.llmTelemetry = { + llm_prompt_selection: { + enabled: true, + selected_prompt_id: selectionResult.selectedPromptId, + confidence: selectionResult.confidence, + duration_ms: selectionDuration, + model: this.llmConfig.selectionModel, + cache_hit: selectionCacheHit + }, + llm_variable_extraction: { + extracted_vars: Object.keys(extractedVars), + var_values: extractedVars, + duration_ms: extractionDuration, + model: this.llmConfig.extractionModel, + cache_hit: extractionCacheHit + } + }; + + // Phase 3: Get template content + const templateContent = this.getPromptById(selectionResult.selectedPromptId); + if (!templateContent) { + throw new Error(`Template not found for prompt ID: ${selectionResult.selectedPromptId}`); + } + + // Phase 4: Extract task type from prompt ID and build full context with documentation + const taskType = this.extractTaskTypeFromPromptId(selectionResult.selectedPromptId); + const defaultedVars = applyDefaults(extractedVars, selectionResult.selectedPromptId); + + console.log('[SpecialistAdapter] Phase 4: Building context'); + console.log(' Task type:', taskType); + console.log(' Extracted variables:', Object.keys(defaultedVars)); + + // Build base context from request + const baseContext = request ? this.buildTemplateContext(request) : { + workspaceDir: undefined, + hasTools: false, + toolCount: 0 + }; + + console.log(' Base context:', Object.keys(baseContext)); + + // Build context using agency-prompt-creator (includes documentation filtering) + const templateContext = buildPromptCreatorContext( + this.template, + userPrompt, + taskType, + { + // Merge specialist adapter context + ...baseContext, + // Merge LLM-extracted variables + ...defaultedVars + } + ); + + console.log(' Template context keys:', Object.keys(templateContext)); + console.log(' Documentation in context:', templateContext.documentation?.length || 0); + if (templateContext.documentation && templateContext.documentation.length > 0) { + console.log(' Documentation entries:', templateContext.documentation.map((d: any) => d.title)); + } + + // Phase 5: Append documentation section if available + const promptWithDocs = this.appendDocumentationSection(templateContent, templateContext); + + console.log('[SpecialistAdapter] Phase 5: Appended documentation section'); + console.log(' Template length before:', templateContent.length); + console.log(' Template length after:', promptWithDocs.length); + console.log(' Documentation section added:', promptWithDocs.length > templateContent.length); + + // Phase 6: Substitute and return + const finalPrompt = substituteTemplate(promptWithDocs, templateContext); + + console.log('[SpecialistAdapter] Phase 6: Final prompt generated'); + console.log(' Final prompt length:', finalPrompt.length); + console.log(' Contains "Relevant Documentation":', finalPrompt.includes('Relevant Documentation')); + + return finalPrompt; + } + + /** + * Select best prompt using LLM + */ + private async selectPromptWithLLM(userPrompt: string, model?: string): Promise { + if (!this.llmClient) { + throw new Error('LLM client not initialized'); + } + + const prompt = buildPromptSelectionPrompt(userPrompt, this.template.prompts, model); + + console.log('[SpecialistAdapter] Prompt selection LLM prompt (first 500 chars):'); + console.log(prompt.substring(0, 500) + '...'); + + try { + const response = await Promise.race([ + this.llmClient.chat.completions.create({ + model: this.llmConfig.selectionModel, + messages: [{ role: 'user', content: prompt }], + temperature: 0.1, + max_tokens: 500 + }), + new Promise((_, reject) => + setTimeout(() => reject(new Error('LLM selection timeout')), this.llmConfig.timeoutMs) + ) + ]); + + const content = response.choices[0]?.message?.content; + if (!content) { + throw new Error('No content in LLM response'); + } + + return parsePromptSelectionResponse(content); + } catch (error) { + if (error instanceof Error) { + throw new Error(`LLM prompt selection failed: ${error.message}`); + } + throw error; + } + } + + /** + * Extract variables using LLM with tool calling + */ + private async extractVariablesWithLLM(userPrompt: string, selectedPromptId: string): Promise { + if (!this.llmClient) { + throw new Error('LLM client not initialized'); + } + + const templateContent = this.getPromptById(selectedPromptId); + if (!templateContent) { + return {}; // Return empty if template not found + } + + const prompt = buildVariableExtractionPrompt(userPrompt, templateContent); + + try { + const response = await Promise.race([ + this.llmClient.chat.completions.create({ + model: this.llmConfig.extractionModel, + messages: [{ role: 'user', content: prompt }], + tools: [VARIABLE_EXTRACTION_TOOL as any], + tool_choice: { type: 'function', function: { name: 'extract_template_variables' } } as any, + temperature: 0.1, + max_tokens: 500 + }), + new Promise((_, reject) => + setTimeout(() => reject(new Error('LLM extraction timeout')), this.llmConfig.timeoutMs) + ) + ]); + + const toolCalls = response.choices[0]?.message?.tool_calls; + if (!toolCalls || toolCalls.length === 0) { + console.warn('[SpecialistAdapter] No tool calls in extraction response, returning empty variables'); + return {}; + } + + const extracted = parseToolCallResponse(toolCalls[0].function); + return validateExtractedVariables(extracted); + } catch (error) { + if (error instanceof Error) { + console.warn(`[SpecialistAdapter] LLM variable extraction failed: ${error.message}`); + } + return {}; // Return empty on error - will use defaults + } + } + + /** + * Extract task type from prompt ID + * Examples: + * - "project_setup.default.systemPrompt" -> "project_setup" + * - "component_generation.model_specific.claude.systemPrompt" -> "component_generation" + * - "default.spawnerPrompt" -> "default" + * - "general.model_specific.claude.spawnerPrompt" -> "default" + */ + private extractTaskTypeFromPromptId(promptId: string): TaskType { + const parts = promptId.split('.'); + + // If starts with "default" or "general", it's a default task + if (parts[0] === 'default' || parts[0] === 'general') { + return 'default'; + } + + // Otherwise, first part is the task type + const taskType = parts[0]; + + // Validate it's a known TaskType, otherwise default + const validTaskTypes: TaskType[] = [ + 'project_setup', + 'component_generation', + 'migration', + 'bug_fix', + 'refactoring', + 'testing', + 'documentation', + 'default' + ]; + + if (validTaskTypes.includes(taskType as TaskType)) { + return taskType as TaskType; + } + + console.warn(`[SpecialistAdapter] Unknown task type "${taskType}" in prompt ID "${promptId}", using "default"`); + return 'default'; + } + + /** + * Append documentation section to prompt if documentation is available + * This matches the logic in agency-prompt-creator's createPrompt + */ + private appendDocumentationSection(prompt: string, context: any): string { + // Only append if documentation is available in context + if (!context.documentation || !Array.isArray(context.documentation) || context.documentation.length === 0) { + return prompt; + } + + // Documentation template section (matches agency-prompt-creator) + const docSection = ` + +## Relevant Documentation + +The following documentation resources are most relevant to your task: + +{{#documentation}} +### {{title}} + +{{summary}} + +**Key Concepts**: {{#key_concepts}}{{.}}{{^last}}, {{/last}}{{/key_concepts}} + +{{#link}}**Reference**: {{link}}{{/link}} + +{{#code_patterns}} +{{#code_patterns.0}}**Code Patterns**: +{{#code_patterns}} +- {{.}} +{{/code_patterns}} +{{/code_patterns.0}} +{{/code_patterns}} + +--- +{{/documentation}}`; + + return prompt + docSection; + } + + /** + * Get prompt content by ID from template + * + * NEW STRUCTURE: Handles IDs like: + * - "project_setup.default.systemPrompt" + * - "project_setup.model_specific.claude-sonnet-4.5.systemPrompt" + * - "default.spawnerPrompt" (general default prompts) + * - "general.model_specific.claude-sonnet-4.5.spawnerPrompt" + */ + private getPromptById(promptId: string): string | null { + const parts = promptId.split('.'); + + // Handle general default prompts: "default.spawnerPrompt" + if (parts[0] === 'default' && parts.length === 2) { + const key = parts[1]; + return (this.template.prompts.default as any)?.[key] || null; + } + + // Handle general model-specific prompts: "general.model_specific.model.key" + if (parts[0] === 'general' && parts[1] === 'model_specific' && parts.length >= 4) { + const modelKey = parts[2]; + const promptKey = parts[3]; + return (this.template.prompts.model_specific as any)?.[modelKey]?.[promptKey] || null; + } + + // Handle task-specific default prompts: "project_setup.default.systemPrompt" + if (parts.length >= 3 && parts[1] === 'default') { + const taskType = parts[0]; + const promptKey = parts[2]; + const taskPrompts = (this.template.prompts as any)[taskType]; + return taskPrompts?.default?.[promptKey] || null; + } + + // Handle task-specific model prompts: "project_setup.model_specific.claude-sonnet-4.5.systemPrompt" + if (parts.length >= 4 && parts[1] === 'model_specific') { + const taskType = parts[0]; + const modelKey = parts[2]; + const promptKey = parts[3]; + const taskPrompts = (this.template.prompts as any)[taskType]; + return taskPrompts?.model_specific?.[modelKey]?.[promptKey] || null; + } + + return null; + } +} diff --git a/packages/agent-adapters/test-specialist.ts b/packages/agent-adapters/test-specialist.ts new file mode 100644 index 0000000..e3f4ee5 --- /dev/null +++ b/packages/agent-adapters/test-specialist.ts @@ -0,0 +1,67 @@ +/** + * Test script for SpecialistAdapter + * Run with: pnpm exec tsx test-specialist.ts + */ + +import { SpecialistAdapter } from './src/specialist.ts'; +import { EchoAgent } from './src/index.ts'; + +async function test() { + console.log('🧪 Testing SpecialistAdapter...\n'); + + // Test 1: Load template + console.log('Test 1: Loading specialist template...'); + try { + const echo = new EchoAgent(); + const specialist = new SpecialistAdapter( + echo, + 'starting_from_outcome/shadcn-specialist-template.json5' + ); + console.log(`✅ Loaded specialist: ${specialist.name}\n`); + } catch (error) { + console.error(`❌ Failed to load template:`, error); + return; + } + + // Test 2: Send request + console.log('Test 2: Sending request with prompt transformation...'); + try { + const echo = new EchoAgent(); + const specialist = new SpecialistAdapter( + echo, + 'starting_from_outcome/shadcn-specialist-template.json5' + ); + + const response = await specialist.send({ + messages: [ + { role: 'user', content: 'Set up a new Vite project with shadcn/ui' } + ] + }); + + console.log('✅ Request succeeded'); + console.log('Response content length:', response.content.length); + console.log('First 200 chars:', response.content.substring(0, 200)); + console.log(); + } catch (error) { + console.error('❌ Request failed:', error); + return; + } + + // Test 3: Error handling - missing template + console.log('Test 3: Testing error handling (missing template)...'); + try { + const echo = new EchoAgent(); + new SpecialistAdapter(echo, 'nonexistent/template.json5'); + console.log('❌ Should have thrown an error'); + } catch (error) { + if (error instanceof Error && error.message.includes('not found')) { + console.log('✅ Correctly threw error for missing template\n'); + } else { + console.error('❌ Unexpected error:', error); + } + } + + console.log('✅ All tests passed!'); +} + +test().catch(console.error); diff --git a/packages/agent-adapters/tsconfig.json b/packages/agent-adapters/tsconfig.json index 621256e..2b6ce18 100644 --- a/packages/agent-adapters/tsconfig.json +++ b/packages/agent-adapters/tsconfig.json @@ -6,5 +6,6 @@ "allowImportingTsExtensions": true, "noEmit": true }, - "include": ["src/**/*"] + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts", "src/**/__tests__/**"] } diff --git a/packages/database/pnpm-lock.yaml b/packages/database/pnpm-lock.yaml deleted file mode 100644 index 7ce65d3..0000000 --- a/packages/database/pnpm-lock.yaml +++ /dev/null @@ -1,330 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - better-sqlite3: - specifier: ^12.4.1 - version: 12.4.1 - zod: - specifier: ^3.23.8 - version: 3.25.76 - devDependencies: - '@types/better-sqlite3': - specifier: ^7.6.13 - version: 7.6.13 - '@types/node': - specifier: ^20.14.10 - version: 20.19.22 - typescript: - specifier: ^5.5.4 - version: 5.9.3 - -packages: - - '@types/better-sqlite3@7.6.13': - resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} - - '@types/node@20.19.22': - resolution: {integrity: sha512-hRnu+5qggKDSyWHlnmThnUqg62l29Aj/6vcYgUaSFL9oc7DVjeWEQN3PRgdSc6F8d9QRMWkf36CLMch1Do/+RQ==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - better-sqlite3@12.4.1: - resolution: {integrity: sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==} - engines: {node: 20.x || 22.x || 23.x || 24.x} - - bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - - file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - - github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - - napi-build-utils@2.0.0: - resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - - node-abi@3.78.0: - resolution: {integrity: sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==} - engines: {node: '>=10'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - prebuild-install@7.1.3: - resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} - engines: {node: '>=10'} - hasBin: true - - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - tar-fs@2.1.4: - resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - -snapshots: - - '@types/better-sqlite3@7.6.13': - dependencies: - '@types/node': 20.19.22 - - '@types/node@20.19.22': - dependencies: - undici-types: 6.21.0 - - base64-js@1.5.1: {} - - better-sqlite3@12.4.1: - dependencies: - bindings: 1.5.0 - prebuild-install: 7.1.3 - - bindings@1.5.0: - dependencies: - file-uri-to-path: 1.0.0 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - chownr@1.1.4: {} - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - deep-extend@0.6.0: {} - - detect-libc@2.1.2: {} - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - expand-template@2.0.3: {} - - file-uri-to-path@1.0.0: {} - - fs-constants@1.0.0: {} - - github-from-package@0.0.0: {} - - ieee754@1.2.1: {} - - inherits@2.0.4: {} - - ini@1.3.8: {} - - mimic-response@3.1.0: {} - - minimist@1.2.8: {} - - mkdirp-classic@0.5.3: {} - - napi-build-utils@2.0.0: {} - - node-abi@3.78.0: - dependencies: - semver: 7.7.3 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - prebuild-install@7.1.3: - dependencies: - detect-libc: 2.1.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 2.0.0 - node-abi: 3.78.0 - pump: 3.0.3 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.4 - tunnel-agent: 0.6.0 - - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - rc@1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - safe-buffer@5.2.1: {} - - semver@7.7.3: {} - - simple-concat@1.0.1: {} - - simple-get@4.0.1: - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-json-comments@2.0.1: {} - - tar-fs@2.1.4: - dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.3 - tar-stream: 2.2.0 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - - typescript@5.9.3: {} - - undici-types@6.21.0: {} - - util-deprecate@1.0.2: {} - - wrappy@1.0.2: {} - - zod@3.25.76: {} diff --git a/packages/database/src/logger.ts b/packages/database/src/logger.ts index 186b7f0..23cb585 100644 --- a/packages/database/src/logger.ts +++ b/packages/database/src/logger.ts @@ -82,6 +82,7 @@ export interface BenchmarkRun { weightedScore?: number | null; packageManager?: string | null; testResults?: string | null; + specialistEnabled?: boolean; metadata?: string; } @@ -104,6 +105,7 @@ export interface RunTelemetry { costUsd?: number; durationMs?: number; workspaceDir?: string; + promptSent?: string; } export interface BatchRun { @@ -129,23 +131,87 @@ export interface BatchStatistics { runs: BenchmarkRun[]; } +/** + * Retry a database operation with exponential backoff on SQLITE_BUSY errors + * @param operation Function to execute + * @param maxRetries Maximum number of retry attempts (default: 5) + * @param baseDelayMs Initial delay in milliseconds (default: 100) + * @returns Result of the operation + */ +function retryOnBusy( + operation: () => T, + maxRetries: number = 5, + baseDelayMs: number = 100 +): T { + let lastError: Error | null = null; + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + try { + return operation(); + } catch (error) { + lastError = error as Error; + const isBusy = error instanceof Error && + (error.message.includes('database is locked') || + error.message.includes('SQLITE_BUSY')); + + if (!isBusy || attempt === maxRetries) { + // Not a lock error, or out of retries - throw immediately + throw error; + } + + // Exponential backoff: 100ms, 200ms, 400ms, 800ms, 1600ms + const delayMs = baseDelayMs * Math.pow(2, attempt); + console.log(`\x1b[33m[DEBUG] Database locked, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})...\x1b[0m`); + + // Synchronous sleep + const start = Date.now(); + while (Date.now() - start < delayMs) { + // Busy wait (not ideal but necessary for sync retry) + } + } + } + + // Should never reach here, but TypeScript needs it + throw lastError || new Error('Retry failed'); +} + export class BenchmarkLogger { private static instance: BenchmarkLogger | null = null; private db: Database.Database; private currentRunId: string | null = null; constructor(dbPath?: string) { - // Use absolute path to avoid working directory issues - const defaultPath = join(process.cwd(), 'benchmark-report', 'public', 'benchmarks.db'); - const path = dbPath || process.env.ZE_BENCHMARKS_DB || defaultPath; - + // Priority: explicit dbPath > BENCHMARK_DB_PATH env var > config > default + let path: string; + + if (dbPath) { + path = dbPath; + } else if (process.env.BENCHMARK_DB_PATH) { + path = process.env.BENCHMARK_DB_PATH; + } else { + // Try to load from config, fallback to legacy path + try { + // Dynamically import to avoid circular dependencies + const configModule = require('../../harness/src/lib/config.js'); + if (configModule && configModule.getDatabasePath) { + path = configModule.getDatabasePath(); + } else { + // Fallback to legacy path + path = join(process.cwd(), 'benchmark-report', 'public', 'benchmarks.db'); + } + } catch { + // Config not available, use legacy default + path = join(process.cwd(), 'benchmark-report', 'public', 'benchmarks.db'); + } + } + console.log(`Database path: ${path}`); - + // Ensure directory exists mkdirSync(dirname(path), { recursive: true }); this.db = new Database(path); this.initializeSchema(); - + // Ensure database is properly created and accessible this.ensureDatabaseExists(); } @@ -158,22 +224,36 @@ export class BenchmarkLogger { } private initializeSchema() { - this.db.exec(SCHEMA); + // Enable WAL mode for better concurrent write performance + // WAL (Write-Ahead Logging) allows concurrent reads while writes are happening + // and reduces lock contention between parallel benchmarks + retryOnBusy(() => this.db.pragma('journal_mode = WAL')); + retryOnBusy(() => this.db.pragma('synchronous = NORMAL')); + + // Log foreign key status at startup for debugging + const fkEnabled = retryOnBusy(() => this.db.pragma('foreign_keys', { simple: true })); + console.log('\x1b[90m[DEBUG] Foreign keys enabled:', fkEnabled, '\x1b[0m'); + + retryOnBusy(() => this.db.exec(SCHEMA)); this.addBatchIdColumnIfNeeded(); this.addSuccessTrackingColumnsIfNeeded(); this.addPackageManagerAndTestResultsColumnsIfNeeded(); + this.addSpecialistEnabledColumnIfNeeded(); + this.addPromptSentColumnIfNeeded(); } private addBatchIdColumnIfNeeded() { try { // Check if batchId column exists - const columns = this.db.prepare("PRAGMA table_info(benchmark_runs)").all() as Array<{name: string}>; + const columns = retryOnBusy(() => + this.db.prepare("PRAGMA table_info(benchmark_runs)").all() as Array<{name: string}> + ); const hasBatchId = columns.some(col => col.name === 'batchId'); - + if (!hasBatchId) { console.log('Adding batchId column to existing database...'); - this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN batchId TEXT'); - this.db.exec('CREATE INDEX IF NOT EXISTS idx_runs_batchId ON benchmark_runs(batchId)'); + retryOnBusy(() => this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN batchId TEXT')); + retryOnBusy(() => this.db.exec('CREATE INDEX IF NOT EXISTS idx_runs_batchId ON benchmark_runs(batchId)')); console.log('✓ batchId column added successfully'); } } catch (error) { @@ -184,25 +264,27 @@ export class BenchmarkLogger { private addSuccessTrackingColumnsIfNeeded() { try { // Check if success tracking columns exist - const columns = this.db.prepare("PRAGMA table_info(benchmark_runs)").all() as Array<{name: string}>; + const columns = retryOnBusy(() => + this.db.prepare("PRAGMA table_info(benchmark_runs)").all() as Array<{name: string}> + ); const hasIsSuccessful = columns.some(col => col.name === 'is_successful'); const hasSuccessMetric = columns.some(col => col.name === 'success_metric'); - + if (!hasIsSuccessful) { console.log('Adding is_successful column to existing database...'); - this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN is_successful BOOLEAN DEFAULT 0'); + retryOnBusy(() => this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN is_successful BOOLEAN DEFAULT 0')); console.log('✓ is_successful column added successfully'); } - + if (!hasSuccessMetric) { console.log('Adding success_metric column to existing database...'); - this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN success_metric REAL'); + retryOnBusy(() => this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN success_metric REAL')); console.log('✓ success_metric column added successfully'); } - + // Add index for is_successful if it doesn't exist try { - this.db.exec('CREATE INDEX IF NOT EXISTS idx_runs_is_successful ON benchmark_runs(is_successful)'); + retryOnBusy(() => this.db.exec('CREATE INDEX IF NOT EXISTS idx_runs_is_successful ON benchmark_runs(is_successful)')); } catch (error) { // Index might already exist, ignore error } @@ -213,19 +295,21 @@ export class BenchmarkLogger { private addPackageManagerAndTestResultsColumnsIfNeeded() { try { - const columns = this.db.prepare("PRAGMA table_info(benchmark_runs)").all() as Array<{name: string}>; + const columns = retryOnBusy(() => + this.db.prepare("PRAGMA table_info(benchmark_runs)").all() as Array<{name: string}> + ); const hasPackageManager = columns.some(col => col.name === 'package_manager'); const hasTestResults = columns.some(col => col.name === 'test_results'); - + if (!hasPackageManager) { console.log('Adding package_manager column to existing database...'); - this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN package_manager TEXT'); + retryOnBusy(() => this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN package_manager TEXT')); console.log('✓ package_manager column added successfully'); } - + if (!hasTestResults) { console.log('Adding test_results column to existing database...'); - this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN test_results TEXT'); + retryOnBusy(() => this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN test_results TEXT')); console.log('✓ test_results column added successfully'); } } catch (error) { @@ -233,6 +317,40 @@ export class BenchmarkLogger { } } + private addSpecialistEnabledColumnIfNeeded() { + try { + const columns = retryOnBusy(() => + this.db.prepare("PRAGMA table_info(benchmark_runs)").all() as Array<{name: string}> + ); + const hasSpecialistEnabled = columns.some(col => col.name === 'specialist_enabled'); + + if (!hasSpecialistEnabled) { + console.log('Adding specialist_enabled column to existing database...'); + retryOnBusy(() => this.db.exec('ALTER TABLE benchmark_runs ADD COLUMN specialist_enabled BOOLEAN DEFAULT 0')); + console.log('✓ specialist_enabled column added successfully'); + } + } catch (error) { + console.warn('Failed to add specialist_enabled column:', error); + } + } + + private addPromptSentColumnIfNeeded() { + try { + const columns = retryOnBusy(() => + this.db.prepare("PRAGMA table_info(run_telemetry)").all() as Array<{name: string}> + ); + const hasPromptSent = columns.some(col => col.name === 'prompt_sent'); + + if (!hasPromptSent) { + console.log('Adding prompt_sent column to existing database...'); + retryOnBusy(() => this.db.exec('ALTER TABLE run_telemetry ADD COLUMN prompt_sent TEXT')); + console.log('✓ prompt_sent column added successfully'); + } + } catch (error) { + console.warn('Failed to add prompt_sent column:', error); + } + } + private updateTimestamp(): void { try { const versionFile = join(dirname(this.db.name), 'db-version.json'); @@ -297,18 +415,64 @@ export class BenchmarkLogger { } } - startRun(suite: string, scenario: string, tier: string, agent: string, model?: string, batchId?: string): string { + startRun(suite: string, scenario: string, tier: string, agent: string, model?: string, batchId?: string, specialistEnabled?: boolean): string { const runId = uuidv4(); this.currentRunId = runId; - this.db.prepare(` - INSERT INTO benchmark_runs (run_id, batchId, suite, scenario, tier, agent, model, status) - VALUES (?, ?, ?, ?, ?, ?, ?, 'running') - `).run(runId, batchId, suite, scenario, tier, agent, model); + // Log database write attempt + console.log('\x1b[90m[DEBUG] Database startRun()\x1b[0m'); + console.log(`\x1b[90m Run ID: ${runId}\x1b[0m`); + console.log(`\x1b[90m Suite: ${suite}, Scenario: ${scenario}\x1b[0m`); + console.log(`\x1b[90m Tier: ${tier}, Agent: ${agent}, Model: ${model || 'default'}\x1b[0m`); + console.log(`\x1b[90m Batch ID: ${batchId || 'none'}\x1b[0m`); + console.log(`\x1b[90m Specialist: ${specialistEnabled ? 'yes' : 'no'}\x1b[0m`); + console.log(`\x1b[90m Database path: ${this.db.name}\x1b[0m`); + + // If a batchId is provided, ensure the batch exists (critical for child processes) + if (batchId) { + try { + const batchExists = retryOnBusy(() => + this.db.prepare('SELECT 1 FROM batch_runs WHERE batchId = ?').get(batchId) + ); + + if (!batchExists) { + console.log('\x1b[90m[DEBUG] Batch not found in child process, creating batch record\x1b[0m'); + this.createBatch(batchId); + } + } catch (error) { + console.warn(`\x1b[33m[DEBUG] Failed to check/create batch: ${error instanceof Error ? error.message : String(error)}\x1b[0m`); + // Don't throw - we can still proceed without batch tracking + } + } + + try { + retryOnBusy(() => + this.db.prepare(` + INSERT INTO benchmark_runs (run_id, batchId, suite, scenario, tier, agent, model, status, specialist_enabled) + VALUES (?, ?, ?, ?, ?, ?, ?, 'running', ?) + `).run(runId, batchId, suite, scenario, tier, agent, model, specialistEnabled ? 1 : 0) + ); + + console.log(`\x1b[90m ✓ Database record created successfully\x1b[0m`); + } catch (error) { + console.error(`\x1b[31m[DEBUG] Failed to create database record: ${error instanceof Error ? error.message : String(error)}\x1b[0m`); + throw error; + } return runId; } + updateAgent(agentName: string, runId?: string) { + const targetRunId = runId || this.currentRunId; + if (!targetRunId) throw new Error('No active run'); + + this.db.prepare(` + UPDATE benchmark_runs + SET agent = ? + WHERE run_id = ? + `).run(agentName, targetRunId); + } + completeRun(totalScore?: number, weightedScore?: number, metadata?: Record, isSuccessful?: boolean, successMetric?: number, packageManager?: string, testResults?: string) { if (!this.currentRunId) throw new Error('No active run'); @@ -331,15 +495,15 @@ export class BenchmarkLogger { this.updateTimestamp(); } - failRun(error: string, errorType?: 'workspace' | 'prompt' | 'agent' | 'evaluation' | 'unknown') { + failRun(error: string, errorType?: 'workspace' | 'prompt' | 'agent' | 'evaluation' | 'warmup' | 'unknown') { if (!this.currentRunId) throw new Error('No active run'); - + this.db.prepare(` - UPDATE benchmark_runs + UPDATE benchmark_runs SET status = 'failed', completed_at = CURRENT_TIMESTAMP, metadata = ? WHERE run_id = ? `).run(JSON.stringify({ error, errorType: errorType || 'unknown' }), this.currentRunId); - + this.updateTimestamp(); } @@ -354,18 +518,18 @@ export class BenchmarkLogger { this.updateTimestamp(); } - logTelemetry(toolCalls?: number, tokensIn?: number, tokensOut?: number, costUsd?: number, durationMs?: number, workspaceDir?: string) { + logTelemetry(toolCalls?: number, tokensIn?: number, tokensOut?: number, costUsd?: number, durationMs?: number, workspaceDir?: string, promptSent?: string) { if (!this.currentRunId) throw new Error('No active run'); - + this.db.prepare(` - INSERT INTO run_telemetry (run_id, tool_calls, tokens_in, tokens_out, cost_usd, duration_ms, workspace_dir) - VALUES (?, ?, ?, ?, ?, ?, ?) - `).run(this.currentRunId, toolCalls, tokensIn, tokensOut, costUsd, durationMs, workspaceDir); + INSERT INTO run_telemetry (run_id, tool_calls, tokens_in, tokens_out, cost_usd, duration_ms, workspace_dir, prompt_sent) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `).run(this.currentRunId, toolCalls, tokensIn, tokensOut, costUsd, durationMs, workspaceDir, promptSent); } getRunHistory(limit: number = 50): BenchmarkRun[] { const rows = this.db.prepare(` - SELECT + SELECT id, run_id as runId, suite, @@ -380,12 +544,13 @@ export class BenchmarkLogger { weighted_score as weightedScore, package_manager as packageManager, test_results as testResults, + specialist_enabled as specialistEnabled, metadata - FROM benchmark_runs - ORDER BY started_at DESC + FROM benchmark_runs + ORDER BY started_at DESC LIMIT ? `).all(limit) as any[]; - + return rows.map(row => ({ id: row.id, runId: row.runId, @@ -401,13 +566,14 @@ export class BenchmarkLogger { weightedScore: row.weightedScore, packageManager: row.packageManager, testResults: row.testResults, + specialistEnabled: row.specialistEnabled === 1, metadata: row.metadata })); } getRunDetails(runId: string) { const runRow = this.db.prepare(` - SELECT + SELECT id, run_id as runId, suite, @@ -422,10 +588,11 @@ export class BenchmarkLogger { weighted_score as weightedScore, package_manager as packageManager, test_results as testResults, + specialist_enabled as specialistEnabled, metadata FROM benchmark_runs WHERE run_id = ? `).get(runId) as any; - + const run: BenchmarkRun | undefined = runRow ? { id: runRow.id, runId: runRow.runId, @@ -441,6 +608,7 @@ export class BenchmarkLogger { weightedScore: runRow.weightedScore, packageManager: runRow.packageManager, testResults: runRow.testResults, + specialistEnabled: runRow.specialistEnabled === 1, metadata: runRow.metadata } : undefined; @@ -467,7 +635,7 @@ export class BenchmarkLogger { })); const telemetryRows = this.db.prepare(` - SELECT + SELECT id, run_id as runId, tool_calls as toolCalls, @@ -475,10 +643,11 @@ export class BenchmarkLogger { tokens_out as tokensOut, cost_usd as costUsd, duration_ms as durationMs, - workspace_dir as workspaceDir + workspace_dir as workspaceDir, + prompt_sent as promptSent FROM run_telemetry WHERE run_id = ? `).all(runId) as any[]; - + const telemetry: RunTelemetry[] = telemetryRows.map(row => ({ id: row.id, runId: row.runId, @@ -487,7 +656,8 @@ export class BenchmarkLogger { tokensOut: row.tokensOut, costUsd: row.costUsd, durationMs: row.durationMs, - workspaceDir: row.workspaceDir + workspaceDir: row.workspaceDir, + promptSent: row.promptSent })); return { run, evaluations, telemetry }; @@ -722,15 +892,46 @@ export class BenchmarkLogger { startBatch(): string { const batchId = uuidv4(); const now = Date.now(); - + this.db.prepare(` INSERT INTO batch_runs (batchId, createdAt, totalRuns, successfulRuns) VALUES (?, ?, 0, 0) `).run(batchId, now); - + return batchId; } + createBatch(batchId: string): void { + const now = Date.now(); + + console.log('\x1b[90m[DEBUG] createBatch()\x1b[0m'); + console.log(`\x1b[90m Batch ID: ${batchId}\x1b[0m`); + console.log(`\x1b[90m Timestamp: ${now}\x1b[0m`); + + try { + const result = retryOnBusy(() => + this.db.prepare(` + INSERT OR IGNORE INTO batch_runs (batchId, createdAt, totalRuns, successfulRuns) + VALUES (?, ?, 0, 0) + `).run(batchId, now) + ); + + console.log(`\x1b[90m Insert result: changes=${result.changes}, lastInsertRowid=${result.lastInsertRowid}\x1b[0m`); + + // Verify it was inserted + const check = retryOnBusy(() => + this.db.prepare('SELECT * FROM batch_runs WHERE batchId = ?').get(batchId) + ); + console.log(`\x1b[90m Verification: ${check ? 'EXISTS' : 'NOT FOUND'}\x1b[0m`); + if (check) { + console.log(`\x1b[90m Record: ${JSON.stringify(check)}\x1b[0m`); + } + } catch (error) { + console.error(`\x1b[31m[DEBUG] Failed to create batch: ${error}\x1b[0m`); + throw error; + } + } + getBatchSuccessfulRunsCount(batchId: string): number { const result = this.db.prepare(` SELECT COUNT(*) as count @@ -819,7 +1020,7 @@ export class BenchmarkLogger { if (!batchRow) return null; const runs = this.db.prepare(` - SELECT + SELECT id, run_id as runId, batchId, @@ -833,14 +1034,15 @@ export class BenchmarkLogger { completed_at as completedAt, total_score as totalScore, weighted_score as weightedScore, + specialist_enabled as specialistEnabled, metadata - FROM benchmark_runs + FROM benchmark_runs WHERE batchId = ? ORDER BY started_at `).all(batchId) as any[]; - + const duration = batchRow.completedAt ? batchRow.completedAt - batchRow.createdAt : 0; - + return { batchId: batchRow.batchId, createdAt: batchRow.createdAt, @@ -864,6 +1066,7 @@ export class BenchmarkLogger { completedAt: run.completedAt, totalScore: run.totalScore, weightedScore: run.weightedScore, + specialistEnabled: run.specialistEnabled === 1, metadata: run.metadata })) }; @@ -1115,6 +1318,27 @@ export class BenchmarkLogger { this.db.exec('DELETE FROM batch_runs'); } + /** + * Force a WAL checkpoint to ensure all writes are visible to other connections + * This is critical when using WAL mode and child processes that open new connections + */ + checkpoint() { + try { + console.log('\x1b[90m[DEBUG] Running WAL checkpoint (TRUNCATE mode)...\x1b[0m'); + // TRUNCATE mode: checkpoint and truncate the WAL to 0 bytes + // This is more aggressive than RESTART and ensures data is fully written + const result = this.db.pragma('wal_checkpoint(TRUNCATE)'); + console.log('\x1b[90m[DEBUG] Checkpoint result:', JSON.stringify(result), '\x1b[0m'); + + // Also ensure synchronous write + this.db.pragma('synchronous = FULL'); + console.log('\x1b[90m[DEBUG] Set synchronous = FULL\x1b[0m'); + } catch (error) { + console.error('\x1b[31m[DEBUG] Failed to checkpoint database:', error, '\x1b[0m'); + throw error; + } + } + close() { this.db.close(); BenchmarkLogger.instance = null; diff --git a/packages/database/src/schema.ts b/packages/database/src/schema.ts index dbbd243..83d0682 100644 --- a/packages/database/src/schema.ts +++ b/packages/database/src/schema.ts @@ -28,8 +28,12 @@ export const SCHEMA = ` success_metric REAL, package_manager TEXT, test_results TEXT, - metadata TEXT, - FOREIGN KEY (batchId) REFERENCES batch_runs(batchId) + specialist_enabled BOOLEAN DEFAULT 0, + metadata TEXT + -- Note: FOREIGN KEY constraint removed to avoid issues with SQLite WAL mode + -- and multiple concurrent connections. Referential integrity is maintained + -- by the application logic in BenchmarkLogger. + -- FOREIGN KEY (batchId) REFERENCES batch_runs(batchId) ); CREATE TABLE IF NOT EXISTS evaluation_results ( @@ -52,6 +56,7 @@ export const SCHEMA = ` cost_usd REAL, duration_ms INTEGER, workspace_dir TEXT, + prompt_sent TEXT, FOREIGN KEY (run_id) REFERENCES benchmark_runs(run_id) ); diff --git a/packages/evaluators/package.json b/packages/evaluators/package.json index 84a8361..89636bd 100644 --- a/packages/evaluators/package.json +++ b/packages/evaluators/package.json @@ -11,12 +11,13 @@ "start": "tsx src/index.ts" }, "devDependencies": { - "typescript": "^5.5.4", - "@types/node": "^20.14.10" + "@types/node": "^20.14.10", + "@types/semver": "^7.7.1", + "typescript": "^5.5.4" }, "dependencies": { - "semver": "^7.6.3", + "dotenv": "^16.0.0", "openai": "^4.0.0", - "dotenv": "^16.0.0" + "semver": "^7.6.3" } } \ No newline at end of file diff --git a/packages/evaluators/src/evaluators/config-accuracy.ts b/packages/evaluators/src/evaluators/config-accuracy.ts new file mode 100644 index 0000000..67ff8de --- /dev/null +++ b/packages/evaluators/src/evaluators/config-accuracy.ts @@ -0,0 +1,175 @@ +import type { EvaluationContext, Evaluator, EvaluatorResult } from '../types.ts'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import chalk from 'chalk'; +import { findProjectDir } from '../utils/workspace.ts'; + +export class ConfigAccuracyEvaluator implements Evaluator { + meta = { name: 'ConfigAccuracyEvaluator' } as const; + + async evaluate(ctx: EvaluationContext): Promise { + try { + // Log the paths received from context + console.log(chalk.blue(`[ConfigAccuracyEvaluator] Suites dir from context: ${ctx.suitesDir || 'not provided'}`)); + console.log(chalk.blue(`[ConfigAccuracyEvaluator] Reference path from context: ${ctx.referencePath || 'not provided'}`)); + console.log(chalk.blue(`[ConfigAccuracyEvaluator] Workspace dir: ${ctx.workspaceDir}`)); + + // Use reference path from context (no filesystem traversal needed) + if (!ctx.referencePath || !fs.existsSync(ctx.referencePath)) { + console.error(chalk.red(`[ConfigAccuracyEvaluator] ❌ Reference not found: ${ctx.referencePath || 'undefined'}`)); + return { + name: this.meta.name, + score: 0, + details: 'Reference implementation not found', + }; + } + + const contents = fs.readdirSync(ctx.referencePath); + console.log(chalk.green(`[ConfigAccuracyEvaluator] ✓ Reference found`)); + console.log(chalk.blue(`[ConfigAccuracyEvaluator] Contents (${contents.length} items): [${contents.slice(0, 5).join(', ')}${contents.length > 5 ? '...' : ''}]`)); + + // Find the generated project directory (not 'control') + const projectDir = findProjectDir(ctx.workspaceDir); + if (!projectDir) { + return { + name: this.meta.name, + score: 0, + details: 'Generated project directory not found in workspace', + }; + } + + const checks = [ + this.checkViteConfig(projectDir, ctx.referencePath), + this.checkTailwindSetup(projectDir, ctx.referencePath), + this.checkTsConfig(projectDir, ctx.referencePath), + this.checkComponentsJson(projectDir, ctx.referencePath), + ]; + + const results = await Promise.all(checks); + const score = results.reduce((sum, r) => sum + r.score, 0) / results.length; + + const details = JSON.stringify({ + score: score.toFixed(2), + checks: results.map((r) => ({ name: r.name, score: r.score, reason: r.reason })), + }); + + return { + name: this.meta.name, + score, + details, + }; + } catch (error) { + return { + name: this.meta.name, + score: 0, + details: `Evaluation failed: ${error instanceof Error ? error.message : String(error)}`, + }; + } + } + + private checkViteConfig(workspaceDir: string, referencePath: string): { name: string; score: number; reason: string } { + const viteConfigPath = path.join(workspaceDir, 'vite.config.ts'); + if (!fs.existsSync(viteConfigPath)) { + return { name: 'vite.config.ts', score: 0, reason: 'File does not exist' }; + } + + const content = fs.readFileSync(viteConfigPath, 'utf-8'); + + // Check for required patterns in vite.config.ts + const hasReactPlugin = /react\(\)/.test(content) || /@vitejs\/plugin-react/.test(content); + const hasTailwindPlugin = /tailwindcss\(\)/.test(content) || /@tailwindcss\/vite/.test(content); + const hasPathAlias = /@.*path\.resolve/.test(content) || /resolve:/.test(content); + + let score = 0; + const reasons = []; + if (hasReactPlugin) { score += 0.33; } else { reasons.push('missing React plugin'); } + if (hasTailwindPlugin) { score += 0.34; } else { reasons.push('missing Tailwind plugin'); } + if (hasPathAlias) { score += 0.33; } else { reasons.push('missing path alias config'); } + + return { + name: 'vite.config.ts', + score, + reason: score === 1 ? 'All checks passed' : reasons.join(', ') + }; + } + + private checkTailwindSetup(workspaceDir: string, referencePath: string): { name: string; score: number; reason: string } { + const indexCssPath = path.join(workspaceDir, 'src/index.css'); + if (!fs.existsSync(indexCssPath)) { + return { name: 'tailwind-setup', score: 0, reason: 'src/index.css does not exist' }; + } + + const content = fs.readFileSync(indexCssPath, 'utf-8'); + + // Check for Tailwind v4 syntax (@import "tailwindcss") or v3 syntax (@tailwind directives) + const hasTailwindV4 = /@import\s+["']tailwindcss["']/.test(content); + const hasTailwindV3 = /@tailwind\s+(base|components|utilities)/.test(content); + + if (hasTailwindV4 || hasTailwindV3) { + return { name: 'tailwind-setup', score: 1, reason: 'Tailwind properly configured' }; + } + + return { name: 'tailwind-setup', score: 0, reason: 'No Tailwind imports found' }; + } + + private checkTsConfig(workspaceDir: string, referencePath: string): { name: string; score: number; reason: string } { + const tsconfigPath = path.join(workspaceDir, 'tsconfig.json'); + const tsconfigAppPath = path.join(workspaceDir, 'tsconfig.app.json'); + + if (!fs.existsSync(tsconfigPath)) { + return { name: 'tsconfig', score: 0, reason: 'tsconfig.json does not exist' }; + } + + let score = 0.5; // Base score for having tsconfig.json + const reasons = ['tsconfig.json exists']; + + if (fs.existsSync(tsconfigAppPath)) { + score += 0.25; + reasons.push('tsconfig.app.json exists'); + } else { + reasons.push('missing tsconfig.app.json'); + } + + // Check for path aliases in tsconfig.json + const tsconfigContent = fs.readFileSync(tsconfigPath, 'utf-8'); + const hasPathAlias = /"paths"\s*:\s*\{/.test(tsconfigContent) || /@\/\*/.test(tsconfigContent); + if (hasPathAlias) { + score += 0.25; + reasons.push('path aliases configured'); + } else { + reasons.push('missing path aliases'); + } + + return { name: 'tsconfig', score, reason: reasons.join(', ') }; + } + + private checkComponentsJson(workspaceDir: string, referencePath: string): { name: string; score: number; reason: string } { + const componentsJsonPath = path.join(workspaceDir, 'components.json'); + if (!fs.existsSync(componentsJsonPath)) { + return { name: 'components.json', score: 0, reason: 'components.json does not exist (shadcn not initialized)' }; + } + + try { + const content = JSON.parse(fs.readFileSync(componentsJsonPath, 'utf-8')); + + // Check for required fields + const hasStyle = content.style !== undefined; + const hasAliases = content.aliases !== undefined; + const hasTsConfig = content.tsx !== undefined || content.aliases?.components !== undefined; + + let score = 0; + const reasons = []; + if (hasStyle) { score += 0.33; } else { reasons.push('missing style config'); } + if (hasAliases) { score += 0.34; } else { reasons.push('missing aliases'); } + if (hasTsConfig) { score += 0.33; } else { reasons.push('missing component paths'); } + + return { + name: 'components.json', + score, + reason: score === 1 ? 'Properly configured' : reasons.join(', ') + }; + } catch (error) { + return { name: 'components.json', score: 0, reason: 'Invalid JSON' }; + } + } +} diff --git a/packages/evaluators/src/evaluators/dependency-proximity.ts b/packages/evaluators/src/evaluators/dependency-proximity.ts new file mode 100644 index 0000000..f8e7b71 --- /dev/null +++ b/packages/evaluators/src/evaluators/dependency-proximity.ts @@ -0,0 +1,170 @@ +import type { EvaluationContext, Evaluator, EvaluatorResult } from '../types.ts'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as semver from 'semver'; +import chalk from 'chalk'; +import { findProjectDir } from '../utils/workspace.ts'; + +export class DependencyProximityEvaluator implements Evaluator { + meta = { name: 'DependencyProximityEvaluator' } as const; + + async evaluate(ctx: EvaluationContext): Promise { + try { + // Log the paths received from context + console.log(chalk.blue(`[DependencyProximityEvaluator] Suites dir from context: ${ctx.suitesDir || 'not provided'}`)); + console.log(chalk.blue(`[DependencyProximityEvaluator] Reference path from context: ${ctx.referencePath || 'not provided'}`)); + console.log(chalk.blue(`[DependencyProximityEvaluator] Workspace dir: ${ctx.workspaceDir}`)); + + // Use reference path from context (no filesystem traversal needed) + if (!ctx.referencePath || !fs.existsSync(ctx.referencePath)) { + console.error(chalk.red(`[DependencyProximityEvaluator] ❌ Reference not found: ${ctx.referencePath || 'undefined'}`)); + return { + name: this.meta.name, + score: 0, + details: 'Reference implementation not found', + }; + } + + const contents = fs.readdirSync(ctx.referencePath); + console.log(chalk.green(`[DependencyProximityEvaluator] ✓ Reference found`)); + console.log(chalk.blue(`[DependencyProximityEvaluator] Contents (${contents.length} items): [${contents.slice(0, 5).join(', ')}${contents.length > 5 ? '...' : ''}]`)); + + // Find the generated project directory (not 'control') + const projectDir = findProjectDir(ctx.workspaceDir); + if (!projectDir) { + return { + name: this.meta.name, + score: 0, + details: 'Generated project directory not found in workspace', + }; + } + + const workspacePkgPath = path.join(projectDir, 'package.json'); + const referencePkgPath = path.join(ctx.referencePath, 'package.json'); + + if (!fs.existsSync(workspacePkgPath)) { + return { + name: this.meta.name, + score: 0, + details: 'package.json not found in workspace', + }; + } + + const workspacePkg = JSON.parse(fs.readFileSync(workspacePkgPath, 'utf-8')); + const referencePkg = JSON.parse(fs.readFileSync(referencePkgPath, 'utf-8')); + + // Key dependencies to check + const keyDeps = [ + 'vite', + 'react', + 'react-dom', + 'tailwindcss', + '@tailwindcss/vite', + 'typescript', + '@types/node', + ]; + + const results = keyDeps.map((dep) => this.compareDependency( + dep, + workspacePkg, + referencePkg + )).filter(r => r !== null); + + if (results.length === 0) { + return { + name: this.meta.name, + score: 0, + details: 'No key dependencies found', + }; + } + + const avgScore = results.reduce((sum, r) => sum + r!.score, 0) / results.length; + + const details = JSON.stringify({ + score: avgScore.toFixed(2), + dependencies: results, + }); + + return { + name: this.meta.name, + score: avgScore, + details, + }; + } catch (error) { + return { + name: this.meta.name, + score: 0, + details: `Evaluation failed: ${error instanceof Error ? error.message : String(error)}`, + }; + } + } + + private compareDependency( + name: string, + workspacePkg: any, + referencePkg: any + ): { name: string; score: number; workspace: string; reference: string; reason: string } | null { + // Check both dependencies and devDependencies + const workspaceVersion = workspacePkg.dependencies?.[name] || workspacePkg.devDependencies?.[name]; + const referenceVersion = referencePkg.dependencies?.[name] || referencePkg.devDependencies?.[name]; + + if (!referenceVersion) { + // Not in reference, skip + return null; + } + + if (!workspaceVersion) { + return { + name, + score: 0, + workspace: 'missing', + reference: referenceVersion, + reason: 'Dependency not installed', + }; + } + + // Try to compare versions + const score = this.compareVersions(workspaceVersion, referenceVersion); + + return { + name, + score, + workspace: workspaceVersion, + reference: referenceVersion, + reason: score === 1 ? 'Exact or compatible version' : score > 0.5 ? 'Close version' : 'Version mismatch', + }; + } + + private compareVersions(workspaceVersion: string, referenceVersion: string): number { + try { + // Clean version strings (remove ^ ~ etc) + const cleanWorkspace = semver.coerce(workspaceVersion); + const cleanReference = semver.coerce(referenceVersion); + + if (!cleanWorkspace || !cleanReference) { + // If we can't parse, just check if they're the same string + return workspaceVersion === referenceVersion ? 1 : 0; + } + + // Exact match + if (semver.eq(cleanWorkspace, cleanReference)) { + return 1; + } + + // Same major version + if (cleanWorkspace.major === cleanReference.major) { + // Same minor version + if (cleanWorkspace.minor === cleanReference.minor) { + return 0.9; // Same major.minor, different patch + } + return 0.7; // Same major, different minor + } + + // Different major version + return 0.3; + } catch (error) { + // If comparison fails, just check string equality + return workspaceVersion === referenceVersion ? 1 : 0; + } + } +} diff --git a/packages/evaluators/src/evaluators/dependency-targets.ts b/packages/evaluators/src/evaluators/dependency-targets.ts index 065dd56..44162cc 100644 --- a/packages/evaluators/src/evaluators/dependency-targets.ts +++ b/packages/evaluators/src/evaluators/dependency-targets.ts @@ -3,6 +3,7 @@ import { relative } from 'node:path'; import type { EvaluationContext, Evaluator, EvaluatorResult } from '../types.ts'; import { getAllPackageJsonPaths, readJson } from '../utils/package-json.ts'; import { versionSatisfies } from '../utils/semver.ts'; +import { findProjectDir } from '../utils/workspace.ts'; export class DependencyTargetsEvaluator implements Evaluator { meta = { name: 'DependencyTargetsEvaluator' } as const; @@ -13,13 +14,23 @@ export class DependencyTargetsEvaluator implements Evaluator { return { name: this.meta.name, score: 1, details: 'No required targets' }; } - const pkgPaths = getAllPackageJsonPaths(ctx.workspaceDir); + // Find the generated project directory (not 'control') + const projectDir = findProjectDir(ctx.workspaceDir); + if (!projectDir) { + return { + name: this.meta.name, + score: 0, + details: 'Generated project directory not found in workspace', + }; + } + + const pkgPaths = getAllPackageJsonPaths(projectDir); let total = 0; let ok = 0; const misses: string[] = []; for (const pkgPath of pkgPaths) { - const rel = relative(ctx.workspaceDir, pkgPath) || '.'; + const rel = relative(projectDir, pkgPath) || '.'; const pkg = readJson(pkgPath); for (const target of targets) { diff --git a/packages/evaluators/src/evaluators/file-structure.ts b/packages/evaluators/src/evaluators/file-structure.ts new file mode 100644 index 0000000..518f372 --- /dev/null +++ b/packages/evaluators/src/evaluators/file-structure.ts @@ -0,0 +1,75 @@ +import type { EvaluationContext, Evaluator, EvaluatorResult } from '../types.ts'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { findProjectDir } from '../utils/workspace.ts'; + +export class FileStructureEvaluator implements Evaluator { + meta = { name: 'FileStructureEvaluator' } as const; + + async evaluate(ctx: EvaluationContext): Promise { + try { + // Find the generated project directory (not 'control') + const projectDir = findProjectDir(ctx.workspaceDir); + if (!projectDir) { + return { + name: this.meta.name, + score: 0, + details: 'Generated project directory not found in workspace', + }; + } + + // Define required files for shadcn-generate-vite benchmark + const requiredFiles = [ + 'package.json', + 'vite.config.ts', + 'tsconfig.json', + 'tsconfig.app.json', + 'tsconfig.node.json', + 'index.html', + 'src/index.css', + 'src/main.tsx', + 'src/App.tsx', + 'src/components/ui/button.tsx', + 'src/lib/utils.ts', + 'components.json', + ]; + + // Check which files exist + const existingFiles: string[] = []; + const missingFiles: string[] = []; + + for (const file of requiredFiles) { + const filePath = path.join(projectDir, file); + if (fs.existsSync(filePath)) { + existingFiles.push(file); + } else { + missingFiles.push(file); + } + } + + // Calculate score + const score = existingFiles.length / requiredFiles.length; + + // Build details + const details = JSON.stringify({ + score: score.toFixed(2), + total: requiredFiles.length, + present: existingFiles.length, + missing: missingFiles.length, + missingFiles: missingFiles.length > 0 ? missingFiles : undefined, + }); + + return { + name: this.meta.name, + score, + details, + }; + } catch (error) { + return { + name: this.meta.name, + score: 0, + details: `Evaluation failed: ${error instanceof Error ? error.message : String(error)}`, + }; + } + } +} diff --git a/packages/evaluators/src/evaluators/package-manager.ts b/packages/evaluators/src/evaluators/package-manager.ts index b91fd88..5fb41ee 100644 --- a/packages/evaluators/src/evaluators/package-manager.ts +++ b/packages/evaluators/src/evaluators/package-manager.ts @@ -2,13 +2,24 @@ import { existsSync } from 'node:fs'; import { join } from 'node:path'; import type { EvaluationContext, Evaluator, EvaluatorResult } from '../types.ts'; +import { findProjectDir } from '../utils/workspace.ts'; export class PackageManagerEvaluator implements Evaluator { meta = { name: 'PackageManagerEvaluator' } as const; async evaluate(ctx: EvaluationContext): Promise { + // Find the generated project directory (not 'control') + const projectDir = findProjectDir(ctx.workspaceDir); + if (!projectDir) { + return { + name: this.meta.name, + score: 0, + details: 'Generated project directory not found in workspace', + }; + } + const allowed = ctx.scenario.constraints?.managers_allowed || []; - const usedPnpm = existsSync(join(ctx.workspaceDir, 'pnpm-lock.yaml')); + const usedPnpm = existsSync(join(projectDir, 'pnpm-lock.yaml')); const ok = allowed.length === 0 || (allowed.includes('pnpm') ? usedPnpm : true); return { name: this.meta.name, diff --git a/packages/evaluators/src/evaluators/test.ts b/packages/evaluators/src/evaluators/test.ts index 78bf1e5..4dd482f 100644 --- a/packages/evaluators/src/evaluators/test.ts +++ b/packages/evaluators/src/evaluators/test.ts @@ -4,13 +4,41 @@ export class TestEvaluator implements Evaluator { meta = { name: 'TestEvaluator' } as const; async evaluate(ctx: EvaluationContext): Promise { + // Check if a test command was configured in the scenario + const testCommandConfigured = ctx.scenario.validation?.commands?.test; + const entry = (ctx.commandLog || []).find((command) => command.type === 'test'); + + // If no test command is configured, pass with success + if (!testCommandConfigured) { + if (!entry) { + return { + name: this.meta.name, + score: 1, + details: 'No test script configured - skipping tests' + }; + } + // Agent ran tests even though none were required - still pass + const ok = entry.exitCode === 0; + return { + name: this.meta.name, + score: ok ? 1 : 0, + details: ok ? 'Tests passed (optional)' : 'Tests failed (optional)' + }; + } + + // Test command IS configured but wasn't run if (!entry) { - return { name: this.meta.name, score: 0, details: 'No test run recorded' }; + return { + name: this.meta.name, + score: 0, + details: 'Test command was configured but not executed' + }; } + // Test command was run - check result const ok = entry.exitCode === 0; - const details = ok ? 'Tests passed (or none present)' : `Tests failed: exit=${entry.exitCode}`; + const details = ok ? 'Tests passed' : `Tests failed: exit=${entry.exitCode}`; return { name: this.meta.name, score: ok ? 1 : 0, details }; } } diff --git a/packages/evaluators/src/index.ts b/packages/evaluators/src/index.ts index b1f98b2..906b144 100644 --- a/packages/evaluators/src/index.ts +++ b/packages/evaluators/src/index.ts @@ -6,6 +6,9 @@ import { IntegrityGuardEvaluator } from './evaluators/integrity-guard.ts'; import { LLMJudgeEvaluator } from './evaluators/llm-judge.ts'; import { PackageManagerEvaluator } from './evaluators/package-manager.ts'; import { TestEvaluator } from './evaluators/test.ts'; +import { FileStructureEvaluator } from './evaluators/file-structure.ts'; +import { ConfigAccuracyEvaluator } from './evaluators/config-accuracy.ts'; +import { DependencyProximityEvaluator } from './evaluators/dependency-proximity.ts'; export async function runEvaluators( ctx: EvaluationContext, @@ -16,6 +19,9 @@ export async function runEvaluators( new PackageManagerEvaluator(), new DependencyTargetsEvaluator(), new IntegrityGuardEvaluator(), + new FileStructureEvaluator(), + new ConfigAccuracyEvaluator(), + new DependencyProximityEvaluator(), ]; // Add LLM judge if enabled @@ -38,6 +44,9 @@ export async function runEvaluators( manager_correctness: results.find((result) => result.name === 'PackageManagerEvaluator')?.score ?? 0, dependency_targets: results.find((result) => result.name === 'DependencyTargetsEvaluator')?.score ?? 0, integrity_guard: results.find((result) => result.name === 'IntegrityGuardEvaluator')?.score ?? 0, + file_structure: results.find((result) => result.name === 'FileStructureEvaluator')?.score ?? 0, + config_accuracy: results.find((result) => result.name === 'ConfigAccuracyEvaluator')?.score ?? 0, + dependency_proximity: results.find((result) => result.name === 'DependencyProximityEvaluator')?.score ?? 0, llm_judge: results.find((result) => result.name === 'LLMJudgeEvaluator')?.score ?? 0, }; @@ -57,4 +66,7 @@ export { IntegrityGuardEvaluator } from './evaluators/integrity-guard.ts'; export { LLMJudgeEvaluator } from './evaluators/llm-judge.ts'; export { PackageManagerEvaluator } from './evaluators/package-manager.ts'; export { TestEvaluator } from './evaluators/test.ts'; +export { FileStructureEvaluator } from './evaluators/file-structure.ts'; +export { ConfigAccuracyEvaluator } from './evaluators/config-accuracy.ts'; +export { DependencyProximityEvaluator } from './evaluators/dependency-proximity.ts'; diff --git a/packages/evaluators/src/types.ts b/packages/evaluators/src/types.ts index 27bb7a6..2a49d1f 100644 --- a/packages/evaluators/src/types.ts +++ b/packages/evaluators/src/types.ts @@ -3,6 +3,7 @@ export type ScoreCard = Record; export interface ScenarioConfig { id: string; suite: string; + reference_path?: string; title?: string; description?: string; constraints?: { @@ -53,6 +54,8 @@ export interface ExecutedCommand { export interface EvaluationContext { scenario: ScenarioConfig; workspaceDir: string; + suitesDir: string; // Absolute path to suites directory + referencePath?: string; // Absolute path to reference implementation (if scenario has reference_path) agentResponse?: string; diffSummary?: FileDiff[]; depsDelta?: DepChange[]; diff --git a/packages/evaluators/src/utils/workspace.ts b/packages/evaluators/src/utils/workspace.ts new file mode 100644 index 0000000..65d4afa --- /dev/null +++ b/packages/evaluators/src/utils/workspace.ts @@ -0,0 +1,59 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import chalk from 'chalk'; + +/** + * Find the generated/modified project directory within the workspace. + * + * The workspace structure depends on the scenario type: + * + * **Generation scenarios** (e.g., shadcn-generate-vite): + * - workspaceDir/ + * - control/ (reference implementation, optional) + * - / (generated project - what we want to find) + * + * **Mutation scenarios** (e.g., dependency updates): + * - workspaceDir/ + * - control/ (reference implementation, optional) + * - (copied from repo-fixture, modified in place) + * + * This function handles both cases: + * 1. If there are subdirectories other than 'control', use the first non-control directory + * 2. If there are no subdirectories (or only 'control'), assume mutation scenario and return workspaceDir + * + * @param workspaceDir - The workspace root directory + * @returns The absolute path to the generated/modified project directory, or null if not found + */ +export function findProjectDir(workspaceDir: string): string | null { + try { + const entries = fs.readdirSync(workspaceDir, { withFileTypes: true }); + const dirs = entries.filter(e => e.isDirectory() && e.name !== 'control'); + + // Case 1: Generation scenario - agent created a new directory + if (dirs.length > 0) { + if (dirs.length > 1) { + console.warn(chalk.yellow(`[findProjectDir] Multiple non-control directories found, using first: ${dirs[0].name}`)); + } + + const projectDir = path.join(workspaceDir, dirs[0].name); + console.log(chalk.blue(`[findProjectDir] Found project directory (generation): ${dirs[0].name}`)); + return projectDir; + } + + // Case 2: Mutation scenario - files modified in place + // Check if there's a package.json directly in workspaceDir + const hasPackageJson = fs.existsSync(path.join(workspaceDir, 'package.json')); + if (hasPackageJson) { + console.log(chalk.blue(`[findProjectDir] Using workspace root (mutation scenario)`)); + return workspaceDir; + } + + // No project found + console.error(chalk.red(`[findProjectDir] No project directory found in ${workspaceDir}`)); + console.error(chalk.red(`[findProjectDir] Workspace contents: ${entries.map(e => e.name).join(', ')}`)); + return null; + } catch (error) { + console.error(chalk.red(`[findProjectDir] Error reading workspace: ${error}`)); + return null; + } +} diff --git a/packages/harness/benchmark-report/public/benchmarks.db b/packages/harness/benchmark-report/public/benchmarks.db new file mode 100644 index 0000000..f0baee1 Binary files /dev/null and b/packages/harness/benchmark-report/public/benchmarks.db differ diff --git a/packages/harness/benchmark-report/public/db-version.json b/packages/harness/benchmark-report/public/db-version.json new file mode 100644 index 0000000..8e0e629 --- /dev/null +++ b/packages/harness/benchmark-report/public/db-version.json @@ -0,0 +1 @@ +{"lastModified":1762923144225} \ No newline at end of file diff --git a/packages/harness/src/cli.ts b/packages/harness/src/cli.ts index 413abed..5f5084d 100644 --- a/packages/harness/src/cli.ts +++ b/packages/harness/src/cli.ts @@ -1,2604 +1,124 @@ #!/usr/bin/env tsx import { config } from 'dotenv'; -import { readFileSync, writeFileSync, mkdirSync, mkdtempSync, existsSync, cpSync, readdirSync } from 'node:fs'; +import { existsSync, readdirSync } from 'node:fs'; import { join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import YAML from 'yaml'; // Load environment variables from .env file in project root -config({ path: resolve(process.cwd(), '.env') }); -import { EchoAgent, ClaudeCodeAdapter, OpenRouterAdapter, AnthropicAdapter, type AgentAdapter, type AgentRequest } from '../../agent-adapters/src/index.ts'; -import { OpenAI } from 'openai'; -import { runEvaluators } from '../../evaluators/src/index.ts'; -import { runValidationCommands } from './runtime/validation.ts'; -import { buildDiffArtifacts } from './runtime/diff.ts'; -import { detectPackageManager, extractPackageManagerFromCommand, extractTestResults } from './runtime/extractors.ts'; -import { Oracle } from './runtime/oracle.ts'; -import { createAskUserToolDefinition, createAskUserHandler } from './runtime/ask-user-tool.ts'; -import { getAllWorkspaceTools, createWorkspaceToolHandlers } from './runtime/workspace-tools.ts'; -import { BenchmarkLogger } from '@ze/database'; -import { intro, outro, spinner, log, select, confirm, multiselect, isCancel, cancel, text } from '@clack/prompts'; -import chalk from 'chalk'; -import figlet from 'figlet'; -import { startDevServer, getServerUrl, stopDevServer } from './dev-server.ts'; -import { OpenRouterAPI } from './lib/openrouter-api.ts'; - -// ============================================================================ -// DYNAMIC MODEL LOADING -// ============================================================================ +// Find workspace root by looking for pnpm-workspace.yaml +// In case of nested workspaces, find the topmost one +function findWorkspaceRoot(startDir: string): string { + let currentDir = startDir; + let lastWorkspaceRoot = startDir; + + while (currentDir !== resolve(currentDir, '..')) { + if (existsSync(join(currentDir, 'pnpm-workspace.yaml'))) { + lastWorkspaceRoot = currentDir; + } + currentDir = resolve(currentDir, '..'); + } -async function getAvailableAgents(): Promise> { - const agents = [ - { value: '__ALL__', label: 'All agents' }, - { value: 'echo', label: 'Echo (Test Agent)' }, - { value: 'openrouter', label: 'OpenRouter (Any Model)' }, - { value: 'anthropic', label: 'Anthropic Claude (Direct API)' }, - { value: 'claude-code', label: 'Claude Code' } - ]; - - return agents; + return lastWorkspaceRoot; } -// ============================================================================ -// CONSTANTS -// ============================================================================ +const workspaceRoot = findWorkspaceRoot(process.cwd()); +const envPath = resolve(workspaceRoot, '.env'); +config({ path: envPath }); -const TABLE_WIDTH = 60; -const SCORE_THRESHOLDS = { - EXCELLENT: 90, - GOOD: 70, - NEEDS_WORK: 60 -} as const; +// External package imports +import { BenchmarkLogger } from '@ze/database'; +import { intro, outro, log } from '@clack/prompts'; +import chalk from 'chalk'; -// Simple progress state and helpers -const TOTAL_STAGES = 6; +// Runtime imports +import { startDevServer, stopDevServer } from './dev-server.ts'; -interface ProgressState { - spinner: any; - currentStage: number; -} +// CLI module imports +import { parseArgs, showHelp } from './cli/args.ts'; +import { validateEnvironment } from './cli/environment.ts'; -function createProgress(): ProgressState { - return { - spinner: spinner(), - currentStage: 0 - }; -} +// Interactive module imports +import { showInteractiveMenu } from './interactive/menu.ts'; +import { runInteractiveBenchmark } from './interactive/benchmark.ts'; +import { runInteractiveSuiteStats, runInteractiveScenarioStats, runInteractiveRunStats, runInteractiveEvaluators } from './interactive/statistics.ts'; +import { runInteractiveClear } from './interactive/clear.ts'; +import { createNewSuite, createNewScenario } from './interactive/suite-management.ts'; -function updateProgress(state: ProgressState, stage: number, description: string) { - const percent = Math.round((stage / TOTAL_STAGES) * 100); - const message = `[${stage}/${TOTAL_STAGES}] ${percent}% - ${description}`; - - if (state.currentStage === 0) { - // First time - start the spinner - state.spinner.start(message); - } else { - // Update existing spinner message - state.spinner.message(message); - } - - state.currentStage = stage; -} +// Execution module imports +import { executeBenchmark } from './execution/benchmark.ts'; -function completeProgress(state: ProgressState) { - state.spinner.stop('Benchmark complete'); -} +// Lib module imports +import { formatStats, displayRunInfo } from './lib/display.ts'; // ============================================================================ -// SECTION 1: UTILITY FUNCTIONS +// HELPER FUNCTIONS // ============================================================================ -function formatStats(label: string, value: string | number, color: 'green' | 'blue' | 'yellow' | 'red' = 'blue') { - return `${chalk.gray(label)}: ${chalk[color](value)}`; -} - -function createTitle() { - return figlet.textSync('ze-benchmarks', { - font: 'ANSI Shadow', - horizontalLayout: 'fitted', - verticalLayout: 'default' - }); -} -// LLM Judge display function -function displayLLMJudgeScores(result: { scores?: Record; evaluator_results?: Array<{ name: string; details?: string }> }) { - const llmJudgeScore = (result.scores as any)['LLMJudgeEvaluator']; - const evaluatorResults = (result as any).evaluator_results; - - let llmJudgeResult = null; - if (evaluatorResults && Array.isArray(evaluatorResults)) { - llmJudgeResult = evaluatorResults.find((r: any) => r.name === 'LLMJudgeEvaluator'); - } - - if (!llmJudgeResult && !llmJudgeScore) return; - - const details = llmJudgeResult?.details || llmJudgeScore?.details; - if (!details) return; - - try { - const parsedDetails = JSON.parse(details); - if (parsedDetails.scores && Array.isArray(parsedDetails.scores)) { - console.log(`\n${chalk.bold.underline('LLM Judge Detailed Scores')}`); - console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); - console.log(`│ ${chalk.bold('Category'.padEnd(20))} ${chalk.bold('Score'.padEnd(8))} ${chalk.bold('Weight'.padEnd(8))} ${chalk.bold('Status'.padEnd(15))} │`); - console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); - - const weights: Record = { - 'dependency_quality': 25, - 'safety_stability': 20, - 'best_practices': 15, - 'monorepo_coordination': 15, - 'technical_execution': 10, - 'communication_transparency': 10, - 'long_term_maintainability': 5 - }; - - const expectedCategories = [ - 'dependency_quality', - 'safety_stability', - 'best_practices', - 'monorepo_coordination', - 'technical_execution', - 'communication_transparency', - 'long_term_maintainability', - 'overall_integrity' - ]; - - const scoreMap = new Map(); - parsedDetails.scores.forEach((score: any) => { - if (score.category && score.score !== undefined) { - scoreMap.set(score.category, score); - } - }); - - expectedCategories.forEach(category => { - const score = scoreMap.get(category); - const weight = weights[category] || 0; - - if (score) { - const percent = (score.score / 5) * 100; - const color = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; - const status = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'Excellent' : percent >= SCORE_THRESHOLDS.GOOD ? 'Good' : 'Needs Work'; - const statusColor = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; - const categoryName = category.replace(/_/g, ' ').replace(/\\b\\w/g, (l: string) => l.toUpperCase()); - - console.log(`│ ${chalk.cyan(categoryName.padEnd(20))} ${chalk[color]((score.score.toFixed(1)).padEnd(8))} ${chalk.gray((weight + '%').padEnd(8))} ${chalk[statusColor](status.padEnd(15))} │`); - } else { - const categoryName = category.replace(/_/g, ' ').replace(/\\b\\w/g, (l: string) => l.toUpperCase()); - console.log(`│ ${chalk.red(categoryName.padEnd(20))} ${chalk.red('N/A'.padEnd(8))} ${chalk.gray((weight + '%').padEnd(8))} ${chalk.red('Missing'.padEnd(15))} │`); - } - }); - - console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); - - if (parsedDetails.overall_assessment) { - console.log(`\n${chalk.bold('LLM Judge Assessment:')}`); - console.log(chalk.gray(parsedDetails.overall_assessment)); - } - - if (parsedDetails.input_tokens) { - console.log(`\n${chalk.bold('Token Usage:')}`); - console.log(chalk.blue(`Input tokens: ${parsedDetails.input_tokens}`)); - } +function findRepoRoot(): string { + let currentDir = process.cwd(); + while (currentDir !== resolve(currentDir, '..')) { + if (existsSync(join(currentDir, 'suites'))) { + return currentDir; } - } catch (error) { - console.log(`\n${chalk.bold('LLM Judge Details:')}`); - console.log(chalk.gray(details)); + currentDir = resolve(currentDir, '..'); } + throw new Error('Could not find repo root (no suites directory found)'); } -// Common display functions -function displayRunInfo(run: { status: string; suite: string; scenario: string; tier: string; agent: string; model?: string; weightedScore?: number | null; runId: string; startedAt: string | number }, index: number) { - const status = run.status === 'completed' - ? chalk.green('✓') - : run.status === 'failed' - ? chalk.red('✗') - : run.status === 'incomplete' - ? chalk.yellow('◐') - : chalk.blue('○'); - - console.log(`\n${chalk.bold(`${index + 1}.`)} ${status} ${chalk.cyan(run.suite)}/${chalk.cyan(run.scenario)} ${chalk.gray(`(${run.tier})`)}`); - console.log(` ${formatStats('Agent', run.agent + (run.model ? ` (${run.model})` : ''))}`); - console.log(` ${formatStats('Score', run.weightedScore?.toFixed(4) || 'N/A', 'green')}`); - console.log(` ${chalk.gray(new Date(run.startedAt).toLocaleString())}`); - console.log(` ${chalk.dim(`ID: ${run.runId.substring(0, 8)}...`)}`); -} - - -function displayModelPerformance(modelStats: Array<{ model: string; avgScore: number; runs: number }>) { - if (modelStats.length === 0) return; - - console.log('\n' + chalk.underline('Model Performance')); - modelStats.forEach((model, index) => { - const rank = index + 1; - const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; - const percent = model.avgScore > 1 ? model.avgScore.toFixed(1) : (model.avgScore * 100).toFixed(1); - const color = model.avgScore >= 0.9 ? 'green' : model.avgScore >= 0.7 ? 'yellow' : 'red'; - - console.log(` ${rankDisplay} ${chalk.bold(model.model.padEnd(35))} ${chalk[color](percent + '%')} ${chalk.gray(`(${model.runs} runs)`)}`); - }); - - const bestModel = modelStats[0]; - const bestPercent = bestModel.avgScore > 1 ? bestModel.avgScore.toFixed(1) : (bestModel.avgScore * 100).toFixed(1); - console.log(`\n ${chalk.cyan('Top Model:')} ${chalk.bold(bestModel.model)} ${chalk.green(bestPercent + '%')} ${chalk.gray(`(${bestModel.runs} runs)`)}`); -} - -// ============================================================================ -// SECTION 2: CORE DOMAIN FUNCTIONS -// ============================================================================ - -function computeWeightedTotals( - scores: Record, - scenarioCfg: { rubric_overrides?: { weights?: Record } }, -) { - const baseWeights: Record = { - install_success: 1.5, - tests_nonregression: 2.5, - manager_correctness: 1, - dependency_targets: 2, - integrity_guard: 1.5, - }; - - const overrideWeights = scenarioCfg.rubric_overrides?.weights ?? {}; - - let totalWeight = 0; - let achieved = 0; - - for (const [metric, score] of Object.entries(scores || {})) { - const weight = overrideWeights[metric] ?? baseWeights[metric] ?? 1; - if (weight <= 0) continue; - totalWeight += weight; - achieved += (typeof score === 'number' ? score : 0) * weight; - } - - const weighted = totalWeight > 0 ? (achieved / totalWeight) * 10 : 0; - return { weighted: Number(weighted.toFixed(4)), max: 10 }; -} - -function findRepoRoot(): string { - return resolve(fileURLToPath(import.meta.url), '../../../..'); -} - -// ============================================================================ -// SECTION 2.5: VALIDATION HELPERS FOR SUITE/SCENARIO CREATION -// ============================================================================ - function validateName(name: string, type: 'suite' | 'scenario'): { valid: boolean; error?: string } { - if (!name || name.trim().length === 0) { - return { valid: false, error: `${type} name cannot be empty` }; - } - - // Check kebab-case: lowercase, alphanumeric + hyphens, no spaces or special chars - const kebabCasePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; - if (!kebabCasePattern.test(name)) { - return { - valid: false, - error: `${type} name must be in kebab-case (lowercase, alphanumeric and hyphens only, e.g., "my-benchmark")` - }; - } - - return { valid: true }; -} - -function checkSuiteExists(suiteName: string): boolean { - const root = findRepoRoot(); - const suitePath = join(root, 'suites', suiteName); - return existsSync(suitePath); -} - -function checkScenarioExists(suiteName: string, scenarioName: string): boolean { - const root = findRepoRoot(); - const scenarioPath = join(root, 'suites', suiteName, 'scenarios', scenarioName); - return existsSync(scenarioPath); -} - -async function createNewSuite(name?: string): Promise { - const root = findRepoRoot(); - const suitesDir = join(root, 'suites'); - - // Get suite name from argument or prompt - let suiteName: string | undefined = name; - if (!suiteName) { - intro(chalk.bgGreen(' Create New Suite ')); - const input = await text({ - message: 'Enter suite name (kebab-case):', - placeholder: 'e.g., my-benchmark-suite', - validate: (value) => { - const validation = validateName(value, 'suite'); - if (!validation.valid) { - return validation.error; - } - if (checkSuiteExists(value)) { - return `Suite "${value}" already exists`; - } - return; - } - }); - - if (isCancel(input)) { - cancel('Operation cancelled.'); - return; - } - suiteName = input as string; - } - - // Validate name - TypeScript guard - if (!suiteName) { - log.error('Suite name is required'); - return; - } - - // TypeScript type narrowing - suiteName is guaranteed to be string here - const finalSuiteName = suiteName as string; - const validation = validateName(finalSuiteName, 'suite'); - if (!validation.valid) { - log.error(validation.error || 'Invalid suite name'); - return; - } - - // Check if suite exists - if (checkSuiteExists(finalSuiteName)) { - log.error(`Suite "${finalSuiteName}" already exists at suites/${finalSuiteName}/`); - return; - } - - // Create directory structure - const suitePath = join(suitesDir, finalSuiteName); - const promptsPath = join(suitePath, 'prompts'); - const scenariosPath = join(suitePath, 'scenarios'); - - const s = spinner(); - s.start('Creating suite directory structure...'); - - try { - mkdirSync(suitePath, { recursive: true }); - mkdirSync(promptsPath, { recursive: true }); - mkdirSync(scenariosPath, { recursive: true }); - s.stop('Suite created'); - - // Show relative path - const relativePath = join('suites', finalSuiteName); - console.log(`\n${chalk.green('✓')} Suite created at: ${chalk.cyan(relativePath)}`); - console.log(` ${chalk.gray('Structure:')} ${relativePath}/{prompts,scenarios}`); - - outro(chalk.green(`Suite "${finalSuiteName}" created successfully`)); - } catch (error) { - s.stop('Failed to create suite'); - log.error(`Failed to create suite: ${error instanceof Error ? error.message : String(error)}`); - } -} - -async function createNewScenario(suite?: string, scenarioName?: string): Promise { - const root = findRepoRoot(); - const suitesDir = join(root, 'suites'); - - // Get suite name - let suiteName = suite; - if (!suiteName) { - intro(chalk.bgGreen(' Create New Scenario ')); - - // Check if any suites exist - if (!existsSync(suitesDir)) { - log.error('No suites directory found. Create a suite first.'); - return; - } - - const existingSuites = readdirSync(suitesDir).filter(dir => - existsSync(join(suitesDir, dir, 'scenarios')) - ); - - if (existingSuites.length === 0) { - const shouldCreate = await confirm({ - message: 'No existing suites found. Create a new suite first?', - initialValue: true - }); - - if (isCancel(shouldCreate) || !shouldCreate) { - cancel('Operation cancelled.'); - return; - } - - await createNewSuite(); - // Refresh suites list - const refreshedSuites = readdirSync(suitesDir).filter(dir => - existsSync(join(suitesDir, dir, 'scenarios')) - ); - if (refreshedSuites.length === 0) { - log.error('Failed to create suite or no suites available'); - return; - } - existingSuites.push(...refreshedSuites); - } - - const selectedSuite = await select({ - message: 'Select suite to add scenario to:', - options: existingSuites.map(s => ({ value: s, label: s })) - }); - - if (isCancel(selectedSuite)) { - cancel('Operation cancelled.'); - return; - } - suiteName = selectedSuite as string; - } - - // TypeScript guard - suiteName must be string at this point - if (!suiteName) { - log.error('Suite name is required'); - return; - } - - // Validate suite exists - if (!checkSuiteExists(suiteName)) { - log.error(`Suite "${suiteName}" does not exist. Create it first with --new-suite.`); - return; - } - - // Get scenario name - let name: string | undefined = scenarioName; - if (!name) { - const input = await text({ - message: 'Enter scenario name (kebab-case):', - placeholder: 'e.g., my-test-scenario', - validate: (value) => { - const validation = validateName(value, 'scenario'); - if (!validation.valid) { - return validation.error; - } - if (checkScenarioExists(suiteName, value)) { - return `Scenario "${value}" already exists in suite "${suiteName}"`; - } - return; - } - }); - - if (isCancel(input)) { - cancel('Operation cancelled.'); - return; - } - name = input as string; - } - - // Validate name - TypeScript guard - if (!name) { - log.error('Scenario name is required'); - return; - } - - // TypeScript type narrowing - name is guaranteed to be string here - const finalName = name as string; - const validation = validateName(finalName, 'scenario'); - if (!validation.valid) { - log.error(validation.error || 'Invalid scenario name'); - return; - } - - // Check if scenario exists - if (checkScenarioExists(suiteName, finalName)) { - log.error(`Scenario "${finalName}" already exists in suite "${suiteName}" at suites/${suiteName}/scenarios/${finalName}/`); - return; - } - - const s = spinner(); - s.start('Creating scenario structure...'); - - try { - // Create scenario directory - const scenarioPath = join(suitesDir, suiteName, 'scenarios', finalName); - mkdirSync(scenarioPath, { recursive: true }); - - // Create prompts directory for this scenario - const promptsPath = join(suitesDir, suiteName, 'prompts', finalName); - mkdirSync(promptsPath, { recursive: true }); - - s.message('Copying scenario template...'); - - // Copy and customize scenario.yaml template - const templatePath = join(root, 'docs', 'templates', 'scenario.yaml'); - if (!existsSync(templatePath)) { - throw new Error(`Template file not found: ${templatePath}`); - } - - let templateContent = readFileSync(templatePath, 'utf8'); - // Replace placeholders - templateContent = templateContent.replace(/^id: my-scenario$/m, `id: ${finalName}`); - templateContent = templateContent.replace(/^suite: my-suite$/m, `suite: ${suiteName}`); - - const scenarioYamlPath = join(scenarioPath, 'scenario.yaml'); - writeFileSync(scenarioYamlPath, templateContent); - - s.message('Creating oracle-answers.json...'); - - // Create oracle-answers.json - const oraclePath = join(scenarioPath, 'oracle-answers.json'); - writeFileSync(oraclePath, '{}\n'); - - s.message('Creating repo-fixture directory...'); - - // Create repo-fixture directory - const repoFixturePath = join(scenarioPath, 'repo-fixture'); - mkdirSync(repoFixturePath, { recursive: true }); - - s.message('Copying repo-fixture guide to README.md...'); - - // Copy repo-fixture.md template content into README.md inside repo-fixture - const repoFixtureTemplatePath = join(root, 'docs', 'templates', 'repo-fixture.md'); - if (existsSync(repoFixtureTemplatePath)) { - const templateContent = readFileSync(repoFixtureTemplatePath, 'utf8'); - const repoFixtureReadmePath = join(repoFixturePath, 'README.md'); - writeFileSync(repoFixtureReadmePath, templateContent); - } else { - log.warning(`Template file not found: ${repoFixtureTemplatePath}`); - // Create a basic README if template is missing - const repoFixtureReadmePath = join(repoFixturePath, 'README.md'); - const basicContent = `# Repository Fixture - -This directory contains the starting codebase state for this scenario. - -## Setup Instructions - -1. Add a \`package.json\` file with your project dependencies -2. Include source files, tests, and configuration files -3. Ensure the fixture represents the starting state before the agent performs the task -4. Test that baseline commands (install, build, test) work correctly -`; - writeFileSync(repoFixtureReadmePath, basicContent); - } - - s.message('Creating default prompt tiers...'); - - // Create default prompt tier files - const promptTiers = [ - { file: 'L0-minimal.md', content: 'Complete the task with minimal guidance.\n' }, - { file: 'L1-basic.md', content: 'Complete the task with basic context and requirements.\n\nConstraints and goals:\n- Follow best practices\n- Ensure correctness\n' }, - { file: 'L2-directed.md', content: 'Complete the task with detailed guidance.\n\nConstraints and goals:\n- Follow all specified requirements\n- Maintain code quality\n- Ensure tests pass\n- Handle edge cases appropriately\n\nIf major changes are required, ask before proceeding.\n' } - ]; - - for (const tier of promptTiers) { - const tierPath = join(promptsPath, tier.file); - writeFileSync(tierPath, tier.content); - } - - s.stop('Scenario created'); - - // Show relative paths - const scenarioRelativePath = join('suites', suiteName, 'scenarios', finalName); - const promptsRelativePath = join('suites', suiteName, 'prompts', finalName); - - console.log(`\n${chalk.green('✓')} Scenario created:`); - console.log(` ${chalk.cyan('Scenario:')} ${scenarioRelativePath}/`); - console.log(` ${chalk.cyan('Prompts:')} ${promptsRelativePath}/`); - const repoFixtureRelativePath = join('suites', suiteName, 'scenarios', finalName, 'repo-fixture'); - - console.log(`\n${chalk.gray('Created files:')}`); - console.log(` - ${scenarioRelativePath}/scenario.yaml`); - console.log(` - ${scenarioRelativePath}/oracle-answers.json`); - console.log(` - ${repoFixtureRelativePath}/README.md ${chalk.dim('(setup guide)')}`); - console.log(` - ${repoFixtureRelativePath}/ ${chalk.dim('(empty - add your fixture files here)')}`); - console.log(` - ${promptsRelativePath}/L0-minimal.md`); - console.log(` - ${promptsRelativePath}/L1-basic.md`); - console.log(` - ${promptsRelativePath}/L2-directed.md`); - console.log(`\n${chalk.yellow('Next steps:')}`); - console.log(` ${chalk.cyan('1.')} Read ${chalk.bold('repo-fixture/README.md')} for setup instructions`); - console.log(` ${chalk.cyan('2.')} Add your starting codebase to ${chalk.bold('repo-fixture/')} directory`); - console.log(` ${chalk.cyan('3.')} Customize ${chalk.bold('scenario.yaml')} with your scenario configuration`); - console.log(` ${chalk.cyan('4.')} Update prompt files in ${chalk.bold('prompts/')} directory`); - - outro(chalk.green(`Scenario "${finalName}" created successfully in suite "${suiteName}"`)); - } catch (error) { - s.stop('Failed to create scenario'); - log.error(`Failed to create scenario: ${error instanceof Error ? error.message : String(error)}`); - } -} - -function loadScenario(suite: string, scenario: string) { - const root = findRepoRoot(); - const scenarioPath = join(root, 'suites', suite, 'scenarios', scenario, 'scenario.yaml'); - const yamlText = readFileSync(scenarioPath, 'utf8'); - return YAML.parse(yamlText); -} - -function getScenarioDir(suite: string, scenario: string) { - const root = findRepoRoot(); - return join(root, 'suites', suite, 'scenarios', scenario); -} - -// ============================================================================ -// DYNAMIC TIER LOADING -// ============================================================================ - -function getTierLabel(tier: string): string { - const labels: Record = { - 'L0': 'L0 - Minimal', - 'L1': 'L1 - Basic', - 'L2': 'L2 - Directed', - 'L3': 'L3 - Migration', - 'Lx': 'Lx - Adversarial' - }; - return labels[tier] || tier; -} - -function getAvailableTiers(suite: string, scenario: string): Array<{value: string, label: string}> { - const root = findRepoRoot(); - const promptDir = join(root, 'suites', suite, 'prompts', scenario); - - if (!existsSync(promptDir)) { - return []; + if (!name || name.trim().length === 0) { + return { valid: false, error: `${type} name cannot be empty` }; } - - const files = readdirSync(promptDir); - const tierPattern = /^(L\d+|Lx)(-.*)?\.md$/; - - const tiers = new Set(); - files.forEach(file => { - const match = file.match(tierPattern); - if (match) { - tiers.add(match[1]); - } - }); - - return Array.from(tiers).sort().map(tier => ({ - value: tier, - label: getTierLabel(tier) - })); -} - -function prepareWorkspaceFromFixture(suite: string, scenario: string): { workspaceDir: string; fixtureDir: string } | undefined { - const scenarioDir = getScenarioDir(suite, scenario); - const candidates = ['repo', 'repo-fixture']; - let fixtureDir: string | null = null; - for (const name of candidates) { - const dir = join(scenarioDir, name); - if (existsSync(dir)) { fixtureDir = dir; break; } - } - if (!fixtureDir) { - log.warning(`No raw fixture directory found (looked for ${candidates.join(', ')}) in ${scenarioDir}`); - return; - } - const root = findRepoRoot(); - const workspacesDir = join(root, 'results', 'workspaces'); - mkdirSync(workspacesDir, { recursive: true }); - const workspaceDir = mkdtempSync(join(workspacesDir, `${suite}-${scenario}-`)); - try { - // Copy fixture directory while excluding README.md files - cpSync(fixtureDir, workspaceDir, { - recursive: true, - filter: (src: string) => { - // Exclude README.md files from being copied (check filename and path) - const fileName = src.split(/[/\\]/).pop() || ''; - return fileName !== 'README.md'; - } - }); - return { workspaceDir, fixtureDir }; - } catch (err) { - console.error('Failed to copy fixture directory:', err); - return; - } -} - -function loadPrompt(suite: string, scenario: string, tier: string): string | null { - const root = findRepoRoot(); - const promptDir = join(root, 'suites', suite, 'prompts', scenario); - - if (!existsSync(promptDir)) { - log.warning(`Prompt directory not found: ${promptDir}`); - return null; - } - - // Look for files that start with the tier (e.g., L1-basic.md, L1.md) - try { - const files = readdirSync(promptDir); - const promptFile = files.find((file: string) => file.startsWith(`${tier}-`) || file === `${tier}.md`); - - if (!promptFile) { - log.warning(`No prompt file found for tier ${tier} in ${promptDir}`); - return null; - } - - const promptPath = join(promptDir, promptFile); - return readFileSync(promptPath, 'utf8'); - } catch (err) { - log.error('Failed to load prompt file:'); - console.error(chalk.dim(err instanceof Error ? err.message : String(err))); - return null; - } -} - -function createAgentAdapter(agentName: string, model?: string): AgentAdapter { - switch (agentName) { - case 'openrouter': - // Pass model directly to constructor instead of using environment variable - return new OpenRouterAdapter(process.env.OPENROUTER_API_KEY, model); - case 'anthropic': - if (model) { - process.env.CLAUDE_MODEL = model; - } - return new AnthropicAdapter(); - case 'claude-code': - return new ClaudeCodeAdapter(model); - case 'echo': - default: - return new EchoAgent(); - } -} - -function writeResult(out: unknown, suite: string, scenario: string) { - const root = findRepoRoot(); - const resultsDir = join(root, 'results'); - mkdirSync(resultsDir, { recursive: true }); - const outPath = join(resultsDir, `summary.json`); - writeFileSync(outPath, JSON.stringify(out, null, 2)); - console.log(`Wrote results to ${outPath}`); -} - - -// ============================================================================ -// SECTION 3: COMMAND-LINE ARGUMENT PARSING -// ============================================================================ - -function parseArgs(argv: string[]) { - // Skip node, script path - arguments are directly suite and scenario - const args = argv.slice(2); - - // Check for history command - if (args[0] === '--history') { - const limit = args[1] ? parseInt(args[1], 10) : 10; - return { cmd: 'history', limit: isNaN(limit) ? 10 : limit } as const; - } - - // Check for evaluators command - if (args[0] === '--evaluators') { - return { cmd: 'evaluators' } as const; - } - - // Check for clear-db command - if (args[0] === '--clear-db') { - return { cmd: 'clear-db' } as const; - } - - // Check for stats command - if (args[0] === '--stats') { - const level = args[1]; // 'run', 'suite', or 'scenario' - const identifier = args.slice(2); // suite name, scenario name, or run ID - return { cmd: 'stats', level, identifier } as const; - } - - // Check for batches command - if (args[0] === '--batches') { - const limit = args[1] ? parseInt(args[1], 10) : 20; - return { cmd: 'batches', limit: isNaN(limit) ? 20 : limit } as const; - } - - // Check for batch-details command - if (args[0] === '--batch-details') { - const batchId = args[1]; - return { cmd: 'batch-details', batchId } as const; - } - - // Check for compare-batches command - if (args[0] === '--compare-batches') { - const batchIds = args.slice(1); - return { cmd: 'compare-batches', batchIds } as const; - } - - // Check for new-suite command - if (args[0] === '--new-suite') { - const name = args[1]; - return { cmd: 'new-suite', name } as const; - } - - // Check for new-scenario command - if (args[0] === '--new-scenario') { - const suite = args[1]; - const name = args[2]; - return { cmd: 'new-scenario', suite, name } as const; - } - - const cmd = 'bench'; - const suite = args[0]; - const scenario = args[1]; - const rest = args.slice(2); - - const tierIndex = rest.indexOf('--tier'); - const tier = tierIndex !== -1 ? rest[tierIndex + 1] : 'L0'; - - const agentIndex = rest.indexOf('--agent'); - const agent = agentIndex !== -1 ? rest[agentIndex + 1] : 'echo'; - - const modelIndex = rest.indexOf('--model'); - const model = modelIndex !== -1 ? rest[modelIndex + 1] : undefined; - - - const noJson = rest.includes('--no-json'); - - return { cmd, suite, scenario, tier, agent, model, noJson } as const; -} - -function showHelp() { - console.log(chalk.cyan(createTitle())); - intro(chalk.bgBlue(' CLI Help ')); - - console.log('\n' + chalk.bold('Usage:')); - console.log(` ${chalk.cyan('pnpm bench')} [options]`); - - console.log('\n' + chalk.bold('Commands:')); - console.log(` ${chalk.cyan('--history')} [limit] Show recent runs`); - console.log(` ${chalk.cyan('--evaluators')} Show evaluator stats`); - console.log(` ${chalk.cyan('--stats')} suite Suite statistics`); - console.log(` ${chalk.cyan('--stats')} scenario Scenario statistics`); - console.log(` ${chalk.cyan('--stats')} run Run details`); - console.log(` ${chalk.cyan('--batches')} [limit] List recent batches`); - console.log(` ${chalk.cyan('--batch-details')} Detailed batch analytics`); - console.log(` ${chalk.cyan('--compare-batches')} Compare multiple batches`); - console.log(` ${chalk.cyan('--new-suite')} [name] Create a new benchmark suite`); - console.log(` ${chalk.cyan('--new-scenario')} [suite] [name] Create a new scenario in a suite`); - console.log(` ${chalk.cyan('--clear-db')} Clear database`); - - console.log('\n' + chalk.bold('Options:')); - console.log(` ${chalk.cyan('--tier')} Difficulty tier (varies by scenario)`); - console.log(` ${chalk.cyan('--agent')} Agent to use`); - console.log(` ${chalk.cyan('--model')} Model name`); - console.log(` ${chalk.cyan('--no-json')} Skip JSON output`); - - console.log('\n' + chalk.bold('Model Selection:')); - console.log(` ${chalk.cyan('OpenRouter Models:')} Search-based selection from 200+ models`); - console.log(` ${chalk.gray('Search by:')} model name, provider, or description`); - console.log(` ${chalk.gray('Example searches:')} \"gpt-4o\", \"llama-3\", \"gemma free\", \"claude sonnet\"`); - - console.log('\n' + chalk.bold('Web Dashboard:')); - console.log(` ${chalk.blue('http://localhost:3000')} ${chalk.gray('- Interactive charts and analytics')}`); - console.log(` ${chalk.gray('Run:')} ${chalk.yellow('pnpm dev')} ${chalk.gray('to start the web server')}`); - - outro(chalk.gray('Run any command to get started')); -} - -// ============================================================================ -// SECTION 4: INTERACTIVE MENU SYSTEM -// ============================================================================ - -async function showInteractiveMenu() { - // Check environment variables for interactive mode - await validateEnvironment(); - - console.log(chalk.cyan(createTitle())); - intro(chalk.bgBlue(' Interactive Mode ')); - - // Show web dashboard info - console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')} ${chalk.blue('http://localhost:3000')} ${chalk.gray('- Interactive charts and analytics')}`); - console.log(` ${chalk.gray('Run:')} ${chalk.yellow('pnpm dev')} ${chalk.gray('to start the web server')}\n`); - - while (true) { - const action = await select({ - message: 'What would you like to do?', - options: [ - { value: 'benchmark', label: 'Run Benchmarks' }, - { value: 'history', label: 'History' }, - { value: 'statistics', label: 'Statistics' }, - { value: 'new-suite', label: 'Create New Suite' }, - { value: 'new-scenario', label: 'Create New Scenario' }, - { value: 'clear', label: 'Clear Database' }, - { value: 'help', label: 'Show Help' }, - { value: 'exit', label: 'Exit' } - ] - }); - - switch (action) { - case 'benchmark': - await runInteractiveBenchmark(); - break; - case 'history': - await runInteractiveHistoryMenu(); - break; - case 'statistics': - await runInteractiveStatisticsMenu(); - break; - case 'new-suite': - await createNewSuite(); - break; - case 'new-scenario': - await createNewScenario(); - break; - case 'clear': - await runInteractiveClear(); - break; - case 'help': - showHelp(); - break; - case 'exit': - outro(chalk.green('Goodbye!')); - process.exit(0); - break; - } - - // Add a small pause before showing the menu again - console.log('\n'); - } -} - -// ============================================================================ -// SECTION 5: INTERACTIVE COMMANDS - BENCHMARKS -// ============================================================================ - -async function executeMultipleBenchmarks( - suites: string[], - scenarios: string[], - tiers: string[], - agents: string[], - models: (string | undefined)[], - noJson: boolean -) { - // Initialize batch tracking - const logger = BenchmarkLogger.getInstance(); - const batchId = logger.startBatch(); - - // Calculate total combinations - const combinations: Array<{ - suite: string; - scenario: string; - tier: string; - agent: string; - model?: string; - }> = []; - - for (const suite of suites) { - for (const scenario of scenarios) { - const availableTiers = getAvailableTiers(suite, scenario); - const availableTierValues = availableTiers.map(t => t.value); - const validTiers = tiers.filter(tier => availableTierValues.includes(tier)); - - if (validTiers.length === 0) { - console.log(chalk.yellow(`⚠ Skipping ${suite}/${scenario}: no valid tiers (available: ${availableTierValues.join(', ')})`)); - continue; - } - - // Log if some tiers are being skipped - const skippedTiers = tiers.filter(tier => !availableTierValues.includes(tier)); - if (skippedTiers.length > 0) { - console.log(chalk.gray(` Skipping tiers for ${suite}/${scenario}: ${skippedTiers.join(', ')}`)); - } - - for (const tier of validTiers) { - for (const agent of agents) { - // Handle model selection per agent - const agentModels = (agent === 'anthropic' || agent === 'claude-code' || agent === 'openrouter') - ? models.filter(m => m !== undefined) - : [undefined]; - - for (const model of agentModels) { - combinations.push({ suite, scenario, tier, agent, model }); - } - } - } - } - } - - // Automatically determine parallel execution based on number of benchmarks - const useParallel = combinations.length >= 3; // Enable parallel for 3+ benchmarks - let concurrency = 3; - - if (useParallel) { - // Smart concurrency based on number of benchmarks - if (combinations.length <= 5) { - concurrency = 2; // Conservative for small batches - } else if (combinations.length <= 15) { - concurrency = 3; // Balanced for medium batches - } else if (combinations.length <= 30) { - concurrency = 5; // Aggressive for large batches - } else { - concurrency = 8; // Maximum for very large batches - } - } - - // Show summary - console.log(chalk.bold.underline(`\nRunning ${combinations.length} benchmark(s):`)); - if (useParallel) { - console.log(chalk.gray(`Parallel execution with concurrency: ${concurrency}`)); - } - - // Track batch statistics - let successfulRuns = 0; - let totalScore = 0; - let totalWeightedScore = 0; - const startTime = Date.now(); - - if (useParallel) { - // Parallel execution - await executeWithConcurrency( - combinations, - concurrency, - async (combo, i) => { - const { suite, scenario, tier, agent, model } = combo; - console.log(`${chalk.bold.cyan(`[${i + 1}/${combinations.length}]`)} ${suite}/${scenario} ${chalk.gray(`(${tier}) ${agent}${model ? ` [${model}]` : ''}`)}`); - await executeBenchmark(suite, scenario, tier, agent, model, noJson, batchId, true); // quiet mode - } - ); - } else { - // Sequential execution - for (let i = 0; i < combinations.length; i++) { - const { suite, scenario, tier, agent, model } = combinations[i]; - - console.log(`${chalk.bold.cyan(`[${i + 1}/${combinations.length}]`)} ${suite}/${scenario} ${chalk.gray(`(${tier}) ${agent}${model ? ` [${model}]` : ''}`)}`); - - await executeBenchmark(suite, scenario, tier, agent, model, noJson, batchId, true); // quiet mode - } - } - - // Complete batch tracking - const endTime = Date.now(); - const duration = endTime - startTime; - - // Calculate batch statistics - const batchStats = logger.getBatchDetails(batchId); - if (batchStats) { - // Calculate successful runs directly from individual runs in the batch - // This ensures we use the new is_successful field - successfulRuns = logger.getBatchSuccessfulRunsCount(batchId); - - // Calculate average scores directly from individual runs in the batch - const scoreStats = logger.getBatchScoreStats(batchId); - totalScore = scoreStats.avgScore; - totalWeightedScore = scoreStats.avgWeightedScore; - } - - logger.completeBatch(batchId, { - totalRuns: combinations.length, - successfulRuns, - avgScore: totalScore, - avgWeightedScore: totalWeightedScore, - metadata: { - suites, - scenarios, - tiers, - agents, - models: models.filter(m => m !== undefined), - duration - } - }); - - // Note: Database is now created directly in public/ directory - - // Get comprehensive batch analytics - const analytics = logger.getBatchAnalytics(batchId); - - // Show batch summary header - console.log('\n' + chalk.bold.underline('Batch Summary')); - console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); - console.log(`│ ${chalk.bold('Batch ID:')} ${chalk.dim(batchId.substring(0, 8))}...`); - - // Show model if all runs used the same model - const uniqueModels = [...new Set(combinations.map(c => c.model).filter(m => m))]; - if (uniqueModels.length === 1) { - console.log(`│ ${chalk.bold('Model:')} ${chalk.cyan(uniqueModels[0])}`); - } - - console.log(`│ ${chalk.bold('Total Runs:')} ${combinations.length}`); - console.log(`│ ${chalk.bold('Completed:')} ${successfulRuns} (${combinations.length > 0 ? ((successfulRuns / combinations.length) * 100).toFixed(1) : 0}%)`); - - // Show failed runs breakdown - const failedRuns = combinations.length - successfulRuns; - if (failedRuns > 0) { - console.log(`│ ${chalk.bold('Failed:')} ${chalk.red(failedRuns)} (${combinations.length > 0 ? ((failedRuns / combinations.length) * 100).toFixed(1) : 0}%)`); - - // Get failure breakdown - const failureBreakdown = logger.getFailureBreakdown(batchId); - if (failureBreakdown.length > 0) { - const failureReasons = failureBreakdown.map(f => `${f.errorType}: ${f.count}`).join(', '); - console.log(`│ ${chalk.bold('Failure Reasons:')} ${chalk.red(failureReasons)}`); - } - } - - console.log(`│ ${chalk.bold('Avg Score:')} ${combinations.length > 0 ? (totalWeightedScore / combinations.length).toFixed(4) : 0} / 10.0`); - console.log(`│ ${chalk.bold('Duration:')} ${(duration / 1000).toFixed(2)}s`); - console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); - - // Show suite breakdown if analytics available - if (analytics && analytics.suiteBreakdown.length > 0) { - console.log(`\n${chalk.bold.underline('Suite Breakdown')}`); - analytics.suiteBreakdown.forEach(suite => { - const successRate = suite.runs > 0 ? ((suite.successfulRuns / suite.runs) * 100).toFixed(0) : 0; - console.log(` ${chalk.cyan(suite.suite)}/${suite.scenario}: ${suite.avgWeightedScore.toFixed(2)}/10 ${chalk.gray(`(${successRate}% success, ${suite.runs} runs)`)}`); - }); - } - - // Show agent performance - if (analytics && analytics.agentPerformance.length > 0) { - console.log(`\n${chalk.bold.underline('Agent Performance')}`); - analytics.agentPerformance.forEach((agent, index) => { - const rank = index + 1; - const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; - const modelStr = agent.model && agent.model !== 'default' ? ` [${agent.model}]` : ''; - const scoreColor = agent.avgWeightedScore >= 9 ? 'green' : agent.avgWeightedScore >= 7 ? 'yellow' : 'red'; - console.log(` ${rankDisplay} ${chalk.cyan(agent.agent)}${modelStr}: ${chalk[scoreColor](agent.avgWeightedScore.toFixed(2))}/10 ${chalk.gray(`(${agent.successfulRuns}/${agent.runs} runs)`)}`); - }); - } - - // Show failed runs if any - if (analytics && analytics.failedRuns.length > 0) { - console.log(`\n${chalk.bold.underline(chalk.red('Failed Runs'))}`); - analytics.failedRuns.forEach(run => { - console.log(` ${chalk.red('✗')} ${run.suite}/${run.scenario} (${run.tier}) ${run.agent} - ${run.error || 'Unknown error'}`); - }); - } - - // Show completion summary - console.log('\n' + chalk.green('✓') + chalk.bold(` Completed all ${combinations.length} benchmark(s)!`)); - - // Note: Database is now created directly in public/ directory -} - -// ============================================================================ -// PARALLEL EXECUTION HELPER -// ============================================================================ - -async function executeWithConcurrency( - items: T[], - concurrency: number, - executor: (item: T, index: number) => Promise -): Promise { - const results: Promise[] = []; - let currentIndex = 0; - - async function runNext(): Promise { - const index = currentIndex++; - if (index >= items.length) return; - - await executor(items[index], index); - await runNext(); + if (!/^[a-z0-9-]+$/.test(name)) { + return { valid: false, error: `${type} name must be kebab-case (lowercase letters, numbers, and hyphens only)` }; } - - // Start initial batch of concurrent executions - for (let i = 0; i < Math.min(concurrency, items.length); i++) { - results.push(runNext()); + if (name.startsWith('-') || name.endsWith('-')) { + return { valid: false, error: `${type} name cannot start or end with a hyphen` }; } - - await Promise.all(results); -} - -async function runInteractiveHistoryMenu() { - const historyType = await select({ - message: 'What history would you like to view?', - options: [ - { value: 'run-history', label: 'Run History' }, - { value: 'batch-history', label: 'Batch History' } - ] - }) as string; - - switch (historyType) { - case 'run-history': - await runInteractiveHistory(); - break; - case 'batch-history': - await runInteractiveBatchHistory(); - break; - } -} - -async function runInteractiveStatisticsMenu() { - const statsType = await select({ - message: 'What statistics would you like to view?', - options: [ - { value: 'suite-stats', label: 'Suite Statistics' }, - { value: 'scenario-stats', label: 'Scenario Statistics' }, - { value: 'run-stats', label: 'Run Details' }, - { value: 'batch-stats', label: 'Batch Statistics' }, - { value: 'evaluators', label: 'Evaluator Performance' } - ] - }) as string; - - switch (statsType) { - case 'suite-stats': - await runInteractiveSuiteStats(); - break; - case 'scenario-stats': - await runInteractiveScenarioStats(); - break; - case 'run-stats': - await runInteractiveRunStats(); - break; - case 'batch-stats': - await runInteractiveBatchStats(); - break; - case 'evaluators': - await runInteractiveEvaluators(); - break; - } -} - -async function runInteractiveBenchmark() { - console.log(chalk.bold.underline('Demo: Benchmarking AI Agents:')); - - // Get available suites and scenarios - const root = findRepoRoot(); - const suitesDir = join(root, 'suites'); - - if (!existsSync(suitesDir)) { - log.error(chalk.red('No suites directory found')); - return; - } - - const suites = readdirSync(suitesDir).filter(dir => - existsSync(join(suitesDir, dir, 'scenarios')) - ); - - if (suites.length === 0) { - log.error(chalk.red('No suites found')); - return; - } - - // Select suites (multiselect) - const selectedSuites = await multiselect({ - message: 'Choose suites:', - options: [ - { value: '__ALL__', label: 'All suites' }, - ...suites.map(suite => ({ value: suite, label: suite })) - ], - required: true - }); - - if (isCancel(selectedSuites)) { - cancel('Operation cancelled.'); - return; - } - - // Expand \"All\" selection - const suitesToUse = selectedSuites.includes('__ALL__') ? suites : selectedSuites; - - // Get scenarios for all selected suites - const allScenarios: Array<{ value: string; label: string; suite: string }> = []; - for (const suite of suitesToUse) { - const scenariosDir = join(suitesDir, suite, 'scenarios'); - if (existsSync(scenariosDir)) { - const scenarios = readdirSync(scenariosDir).filter(dir => - existsSync(join(scenariosDir, dir, 'scenario.yaml')) - ); - scenarios.forEach(scenario => { - allScenarios.push({ - value: scenario, - label: `${scenario} (${suite})`, - suite - }); - }); - } - } - - if (allScenarios.length === 0) { - log.error(chalk.red('No scenarios found for selected suites')); - return; - } - - // Select scenarios (multiselect) - const selectedScenarios = await multiselect({ - message: 'Choose scenarios:', - options: [ - { value: '__ALL__', label: 'All scenarios' }, - ...allScenarios - ], - required: true - }); - - if (isCancel(selectedScenarios)) { - cancel('Operation cancelled.'); - return; - } - - // Expand \"All\" selection and filter by suite - const scenariosToUse = selectedScenarios.includes('__ALL__') - ? allScenarios.map(s => s.value) - : selectedScenarios; - - // Collect available tiers from all selected scenarios - console.log('🔍 Scanning available tiers for selected scenarios...'); - const availableTiersSet = new Set(); - const scenarioTierMap = new Map(); - - for (const suite of suitesToUse) { - for (const scenario of scenariosToUse) { - const tiers = getAvailableTiers(suite, scenario); - const tierValues = tiers.map(t => t.value); - scenarioTierMap.set(`${suite}/${scenario}`, tierValues); - tiers.forEach(tier => availableTiersSet.add(tier.value)); - } - } - - // Show what tiers are available for each scenario - scenarioTierMap.forEach((tiers, scenario) => { - console.log(` ${scenario}: ${tiers.join(', ')}`); - }); - - const tierOptions = [ - { value: '__ALL__', label: 'All available tiers' }, - ...Array.from(availableTiersSet).sort().map(tier => ({ - value: tier, - label: getTierLabel(tier) - })) - ]; - - console.log(`✅ Found ${availableTiersSet.size} unique tiers across all scenarios`); - - // Select tiers (multiselect) - const selectedTiers = await multiselect({ - message: 'Choose difficulty tiers:', - options: tierOptions, - required: true - }); - - if (isCancel(selectedTiers)) { - cancel('Operation cancelled.'); - return; - } - - // Expand \"All\" selection - const tiersToUse = selectedTiers.includes('__ALL__') - ? Array.from(availableTiersSet).sort() - : selectedTiers; - - // Select agents (multiselect) - dynamically loaded - console.log('Loading available agents...'); - const agentOptions = await getAvailableAgents(); - console.log(`✅ Loaded ${agentOptions.length} agent options`); - - const selectedAgents = await multiselect({ - message: 'Choose agents:', - options: agentOptions, - required: true - }); - - if (isCancel(selectedAgents)) { - cancel('Operation cancelled.'); - return; - } - - // Expand \"All\" selection - const agentsToUse = selectedAgents.includes('__ALL__') - ? ['echo', 'openrouter', 'anthropic', 'claude-code'] - : selectedAgents; - - console.log(`🎯 Selected agents: ${agentsToUse.join(', ')}`); - - // Ask for models if needed - let modelsToUse: (string | undefined)[] = [undefined]; - const needsOpenRouterModels = agentsToUse.some(agent => agent === 'openrouter'); - const needsAnthropicModels = agentsToUse.some(agent => agent === 'anthropic'); - const needsClaudeCodeModels = agentsToUse.some(agent => agent === 'claude-code'); - - if (needsOpenRouterModels) { - console.log('🔍 Loading OpenRouter models with tool support...'); - - const openrouterAPI = new OpenRouterAPI(process.env.OPENROUTER_API_KEY || ''); - const toolModels = await openrouterAPI.getModelsWithToolSupport(); - - console.log(`✅ Found ${toolModels.length} models with tool support`); - - // Quick shortcuts for common models - const QUICK_MODELS = { - 'free': 'Filter by free models only', - 'gpt': 'OpenAI GPT models', - 'claude': 'Anthropic Claude models', - 'llama': 'Meta Llama models', - 'gemma': 'Google Gemma models', - 'mistral': 'Mistral AI models' - }; - - // Show shortcuts - console.log(`\n${chalk.gray('Quick searches:')} ${Object.keys(QUICK_MODELS).join(', ')}`); - - // Text-based search instead of dropdown - const modelSearch = await text({ - message: 'Search for OpenRouter model (type to filter):', - placeholder: 'e.g., gpt-4o, llama, gemma, claude', - validate: (value) => { - if (!value || value.length < 2) { - return 'Please enter at least 2 characters to search'; - } - } - }); - - if (isCancel(modelSearch)) { - cancel('Operation cancelled.'); - return; - } - - // Search and display results - const searchResults = openrouterAPI.searchModels(toolModels, modelSearch); - - if (searchResults.length === 0) { - log.warning(`No models found matching \"${modelSearch}\"`); - log.info('Try searching for: gpt-4o, llama, gemma, claude, mistral'); - return; - } - - // Show search results with pricing info - console.log(`\n📋 Found ${searchResults.length} matching models:\n`); - - const selectedModel = await select({ - message: 'Choose a model:', - options: searchResults.map(model => { - const promptCost = parseFloat(model.pricing.prompt); - const isFree = promptCost === 0; - const costLabel = isFree ? '(FREE)' : `($${promptCost}/1K tokens)`; - - return { - value: model.id, - label: `${model.name} ${costLabel}`, - hint: model.description?.substring(0, 80) - }; - }) - }); - - if (isCancel(selectedModel)) { - cancel('Operation cancelled.'); - return; - } - - modelsToUse = [selectedModel]; - console.log(`🎯 Selected model: ${selectedModel}`); - - // Display selected model details - const selectedModelInfo = toolModels.find(m => m.id === selectedModel); - - if (selectedModelInfo) { - console.log(`\n${chalk.bold.cyan('Model Details:')}`); - console.log(` ${chalk.gray('Name:')} ${selectedModelInfo.name}`); - console.log(` ${chalk.gray('ID:')} ${selectedModelInfo.id}`); - console.log(` ${chalk.gray('Context:')} ${selectedModelInfo.context_length.toLocaleString()} tokens`); - console.log(` ${chalk.gray('Cost:')} $${selectedModelInfo.pricing.prompt}/1K prompt, $${selectedModelInfo.pricing.completion}/1K completion`); - - const isFree = parseFloat(selectedModelInfo.pricing.prompt) === 0; - if (isFree) { - console.log(` ${chalk.green('✓ FREE MODEL')}`); - } - } - } else if (needsAnthropicModels) { - console.log('🧠 Loading available Anthropic models...'); - // Anthropic has a fixed set of models - const anthropicModels = [ - { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet (Current)' }, - { value: 'claude-3-5-haiku-20241022', label: 'Claude 3.5 Haiku (Current)' }, - { value: 'claude-3-7-sonnet-20250219', label: 'Claude 3.7 Sonnet (Active)' }, - { value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4 (Active)' }, - { value: 'claude-opus-4-20250514', label: 'Claude Opus 4 (Active)' }, - { value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1 (Active)' }, - { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5 (Active)' }, - { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5 (Active)' } - ]; - - const selectedModels = await multiselect({ - message: 'Choose Anthropic models:', - options: anthropicModels, - required: true - }); - - if (isCancel(selectedModels)) { - cancel('Operation cancelled.'); - return; - } - - modelsToUse = selectedModels; - console.log(`🎯 Selected Anthropic models: ${modelsToUse.join(', ')}`); - } else if (needsClaudeCodeModels) { - console.log('🧠 Loading available Claude Code models...'); - // Claude Code has a fixed set of models - const claudeCodeModels = [ - { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet (Current)' }, - { value: 'claude-3-5-haiku-20241022', label: 'Claude 3.5 Haiku (Current)' }, - { value: 'claude-3-7-sonnet-20250219', label: 'Claude 3.7 Sonnet (Active)' }, - { value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4 (Active)' }, - { value: 'claude-opus-4-20250514', label: 'Claude Opus 4 (Active)' }, - { value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1 (Active)' }, - { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5 (Active)' }, - { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5 (Active)' } - ]; - - const selectedModels = await multiselect({ - message: 'Choose Claude Code models:', - options: claudeCodeModels, - required: true - }); - - if (isCancel(selectedModels)) { - cancel('Operation cancelled.'); - return; - } - - modelsToUse = selectedModels; - console.log(`🎯 Selected Claude Code models: ${modelsToUse.join(', ')}`); - } - - - // Ask for JSON output - const includeJson = await confirm({ - message: 'Include JSON output?', - initialValue: true - }); - - // Calculate total combinations for automatic parallel decision - const totalCombinations = suitesToUse.length * scenariosToUse.length * tiersToUse.length * agentsToUse.length * modelsToUse.length; - - // Automatically determine parallel execution based on number of benchmarks - const useParallel = totalCombinations >= 3; // Enable parallel for 3+ benchmarks - let concurrency = 3; - - if (useParallel) { - // Smart concurrency based on number of benchmarks - if (totalCombinations <= 5) { - concurrency = 2; // Conservative for small batches - } else if (totalCombinations <= 15) { - concurrency = 3; // Balanced for medium batches - } else if (totalCombinations <= 30) { - concurrency = 5; // Aggressive for large batches - } else { - concurrency = 8; // Maximum for very large batches - } - } - - // Show summary of what will be executed - console.log(`\n${chalk.green('►')} Will run ${chalk.bold(totalCombinations.toString())} benchmark combination(s)`); - console.log(` ${chalk.cyan('Suites:')} ${suitesToUse.join(', ')}`); - console.log(` ${chalk.cyan('Scenarios:')} ${scenariosToUse.join(', ')}`); - console.log(` ${chalk.cyan('Tiers:')} ${tiersToUse.join(', ')}`); - console.log(` ${chalk.cyan('Agents:')} ${agentsToUse.join(', ')}`); - if (needsOpenRouterModels || needsAnthropicModels || needsClaudeCodeModels) { - console.log(` ${chalk.cyan('Models:')} ${modelsToUse.join(', ')}`); - } - console.log(` ${chalk.cyan('JSON output:')} ${includeJson ? 'Yes' : 'No'}`); - console.log(` ${chalk.cyan('Parallel execution:')} ${useParallel ? `Yes (concurrency: ${concurrency})` : 'No'}`); - - // Show title before execution - console.log(chalk.cyan(createTitle())); - - // Execute all benchmark combinations - await executeMultipleBenchmarks( - suitesToUse, - scenariosToUse, - tiersToUse, - agentsToUse, - modelsToUse, - !includeJson - ); -} - -// ============================================================================ -// SECTION 6: INTERACTIVE COMMANDS - HISTORY & STATS -// ============================================================================ - -async function runInteractiveHistory() { - // Start dev server for web viewing - try { - const serverUrl = await startDevServer(); - // Note: Database is now created directly in public/ directory - console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); - } catch (err) { - // Continue without web server - } - - const logger = BenchmarkLogger.getInstance(); - - try { - const limit = await select({ - message: 'How many recent runs to show?', - options: [ - { value: 5, label: '5 runs' }, - { value: 10, label: '10 runs' }, - { value: 20, label: '20 runs' }, - { value: 50, label: '50 runs' } - ] - }) as number; - - // Execute history command - const runHistory = logger.getRunHistory(limit); - - if (runHistory.length === 0) { - log.warning('No benchmark runs found'); - outro(chalk.yellow('Run a benchmark first')); - return; - } - - intro(chalk.bgCyan(' Benchmark History ')); - - // Use common display function - runHistory.forEach((run, index) => displayRunInfo(run, index)); - - outro(chalk.green(`Showing ${runHistory.length} recent runs`)); - - } catch (error) { - log.error(chalk.red('Failed to fetch history:')); - console.error(chalk.dim(error instanceof Error ? error.message : String(error))); - } finally { - logger.close(); - } -} - -async function runInteractiveSuiteStats() { - // Start dev server for web viewing - try { - const serverUrl = await startDevServer(); - // Note: Database is now created directly in public/ directory - console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); - console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); - console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); - } catch (err) { - // Continue without web server - console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); - } - - const logger = BenchmarkLogger.getInstance(); - - try { - // Get available suites - const root = findRepoRoot(); - const suitesDir = join(root, 'suites'); - const suites = readdirSync(suitesDir).filter(dir => - existsSync(join(suitesDir, dir, 'scenarios')) - ); - - if (suites.length === 0) { - log.error(chalk.red('No suites found')); - return; - } - - const selectedSuite = await select({ - message: 'Choose a suite:', - options: suites.map(suite => ({ value: suite, label: suite })) - }) as string; - - // Execute suite stats - const stats = logger.getSuiteStats(selectedSuite); - console.log(chalk.bold.bgBlue(` Suite: ${selectedSuite} `)); - console.log('\n' + chalk.underline('Overview')); - console.log(formatStats('Total Runs', stats.totalRuns)); - console.log(formatStats('Success Rate', `${stats.totalRuns > 0 ? ((stats.successfulRuns / stats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); - console.log(formatStats('Avg Score', stats.avgScore.toFixed(4), 'yellow')); - console.log(formatStats('Avg Weighted', stats.avgWeightedScore.toFixed(4), 'yellow')); - console.log(formatStats('Avg Duration', `${(stats.avgDuration / 1000).toFixed(2)}s`, 'blue')); - - if (stats.scenarioBreakdown.length > 0) { - console.log('\n' + chalk.underline('Scenario Breakdown')); - stats.scenarioBreakdown.forEach(scenario => { - console.log(` ${chalk.cyan('•')} ${chalk.bold(scenario.scenario)}: ${chalk.yellow(scenario.avgScore.toFixed(4))} ${chalk.gray(`(${scenario.runs} runs)`)}`); - }); - } - - } catch (error) { - log.error(chalk.red('Failed to fetch suite statistics:')); - console.error(chalk.dim(error instanceof Error ? error.message : String(error))); - } finally { - logger.close(); - } -} - -async function runInteractiveScenarioStats() { - // Start dev server for web viewing - try { - const serverUrl = await startDevServer(); - // Note: Database is now created directly in public/ directory - console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); - console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); - console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); - } catch (err) { - // Continue without web server - console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); - } - - const logger = BenchmarkLogger.getInstance(); - - try { - // Get available suites and scenarios - const root = findRepoRoot(); - const suitesDir = join(root, 'suites'); - const suites = readdirSync(suitesDir).filter(dir => - existsSync(join(suitesDir, dir, 'scenarios')) - ); - - const selectedSuite = await select({ - message: 'Choose a suite:', - options: suites.map(suite => ({ value: suite, label: suite })) - }) as string; - - const scenariosDir = join(suitesDir, selectedSuite, 'scenarios'); - const scenarios = readdirSync(scenariosDir).filter(dir => - existsSync(join(scenariosDir, dir, 'scenario.yaml')) - ); - - const selectedScenario = await select({ - message: 'Choose a scenario:', - options: scenarios.map(scenario => ({ value: scenario, label: scenario })) - }) as string; - - // Execute scenario stats - const stats = logger.getScenarioStats(selectedSuite, selectedScenario); - console.log(chalk.bold.bgMagenta(` ${selectedSuite}/${selectedScenario} `)); - - // Score range with visual bar - const scorePercent = (stats.avgWeightedScore / 10) * 100; - const barLength = 20; - const filled = Math.round((scorePercent / 100) * barLength); - const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(barLength - filled)); - console.log(`\n${bar} ${chalk.bold(stats.avgWeightedScore.toFixed(2))}/10`); - - console.log('\n' + chalk.underline('Overview')); - console.log(formatStats('Total Runs', stats.totalRuns)); - console.log(formatStats('Success Rate', `${stats.totalRuns > 0 ? ((stats.successfulRuns / stats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); - console.log(formatStats('Avg Score', stats.avgScore.toFixed(4), 'yellow')); - console.log(formatStats('Score Range', `${stats.minScore.toFixed(4)} - ${stats.maxScore.toFixed(4)}`, 'blue')); - - // Agent comparison table - if (stats.agentComparison.length > 0) { - console.log('\n' + chalk.underline('Agent Performance')); - stats.agentComparison.forEach((agent, i) => { - const rank = i + 1; - const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; - console.log(` ${rankDisplay} ${chalk.cyan(agent.agent.padEnd(15))} ${chalk.yellow(agent.avgScore.toFixed(4))} ${chalk.gray(`(${agent.runs} runs)`)}`); - }); - } - - } catch (error) { - log.error(chalk.red('Failed to fetch scenario statistics:')); - console.error(chalk.dim(error instanceof Error ? error.message : String(error))); - } finally { - logger.close(); - } -} - -async function runInteractiveRunStats() { - // Start dev server for web viewing - try { - const serverUrl = await startDevServer(); - // Note: Database is now created directly in public/ directory - console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); - console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); - console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); - } catch (err) { - // Continue without web server - console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); - } - - const logger = BenchmarkLogger.getInstance(); - - try { - // Get recent runs to choose from - const runHistory = logger.getRunHistory(10); - - if (runHistory.length === 0) { - log.warning('No benchmark runs found'); - outro(chalk.yellow('Run a benchmark first')); - return; - } - - const selectedRun = await select({ - message: 'Choose a run to view details:', - options: runHistory.map((run, index) => ({ - value: run.runId, - label: `${index + 1}. ${run.suite}/${run.scenario} (${run.tier}) - ${run.agent} - ${run.weightedScore?.toFixed(4) || 'N/A'}` - })) - }) as string; - - // Execute run stats - const stats = logger.getDetailedRunStats(selectedRun); - console.log(chalk.bold.bgGreen(` Run Details `)); - console.log(`\n${chalk.gray('ID:')} ${chalk.dim(selectedRun.substring(0, 8))}...`); - console.log(formatStats('Suite', stats.run.suite)); - console.log(formatStats('Scenario', stats.run.scenario)); - console.log(formatStats('Tier', stats.run.tier)); - console.log(formatStats('Agent', stats.run.agent + (stats.run.model ? ` (${stats.run.model})` : ''))); - console.log(formatStats('Status', stats.run.status, stats.run.status === 'completed' ? 'green' : stats.run.status === 'failed' ? 'red' : 'yellow')); - console.log(formatStats('Started', new Date(stats.run.startedAt).toLocaleString())); - if (stats.run.completedAt) { - console.log(formatStats('Completed', new Date(stats.run.completedAt).toLocaleString())); - } - if (stats.run.totalScore !== null && stats.run.totalScore !== undefined) { - console.log(formatStats('Total Score', stats.run.totalScore.toFixed(4), 'green')); - } - if (stats.run.weightedScore !== null && stats.run.weightedScore !== undefined) { - console.log(formatStats('Weighted Score', stats.run.weightedScore.toFixed(4), 'green')); - } - - // Evaluation breakdown with progress bars - if (stats.evaluationBreakdown.length > 0) { - console.log('\n' + chalk.underline('Evaluations')); - stats.evaluationBreakdown.forEach(evaluation => { - const percent = evaluation.percentage; - const color = percent === 100 ? 'green' : percent >= 80 ? 'yellow' : 'red'; - const barLength = 15; - const filled = Math.round((percent / 100) * barLength); - const bar = chalk[color]('█'.repeat(filled)) + chalk.gray('░'.repeat(barLength - filled)); - - console.log(` ${evaluation.name.padEnd(30)} ${bar} ${chalk[color](percent.toFixed(1) + '%')}`); - }); - } - - if (stats.telemetrySummary) { - console.log('\n' + chalk.underline('Telemetry')); - console.log(formatStats('Tool Calls', stats.telemetrySummary.toolCalls, 'blue')); - console.log(formatStats('Tokens', stats.telemetrySummary.tokens, 'blue')); - console.log(formatStats('Cost', `$${stats.telemetrySummary.cost.toFixed(6)}`, 'blue')); - console.log(formatStats('Duration', `${(stats.telemetrySummary.duration / 1000).toFixed(2)}s`, 'blue')); - } - - } catch (error) { - log.error(chalk.red('Failed to fetch run statistics:')); - console.error(chalk.dim(error instanceof Error ? error.message : String(error))); - } finally { - logger.close(); - } -} - -async function runInteractiveBatchStats() { - // Start dev server for web viewing - try { - const serverUrl = await startDevServer(); - // Note: Database is now created directly in public/ directory - console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); - } catch (err) { - // Continue without web server - } - - const logger = BenchmarkLogger.getInstance(); - - try { - // Get recent batches to choose from - const batchHistory = logger.getBatchHistory(10); - - if (batchHistory.length === 0) { - log.warning('No batch runs found'); - outro(chalk.yellow('Run some benchmarks first')); - return; - } - - const selectedBatch = await select({ - message: 'Choose a batch to view details:', - options: batchHistory.map((batch, index) => ({ - value: batch.batchId, - label: `${index + 1}. Batch ${batch.batchId.substring(0, 8)}... - ${batch.successfulRuns}/${batch.totalRuns} runs - ${batch.avgWeightedScore?.toFixed(4) || 'N/A'}` - })) - }) as string; - - // Execute batch stats - const batchStats = logger.getBatchDetails(selectedBatch); - if (!batchStats) { - log.error('Batch not found'); - return; - } - - console.log(chalk.bold.bgGreen(` Batch Details `)); - console.log(`\n${chalk.gray('ID:')} ${chalk.dim(selectedBatch.substring(0, 8))}...`); - console.log(formatStats('Total Runs', batchStats.totalRuns)); - console.log(formatStats('Successful', batchStats.successfulRuns, 'green')); - console.log(formatStats('Success Rate', `${batchStats.totalRuns > 0 ? ((batchStats.successfulRuns / batchStats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); - console.log(formatStats('Avg Score', batchStats.avgScore.toFixed(4), 'yellow')); - console.log(formatStats('Avg Weighted', batchStats.avgWeightedScore.toFixed(4), 'yellow')); - console.log(formatStats('Duration', `${(batchStats.duration / 1000).toFixed(2)}s`, 'blue')); - console.log(formatStats('Started', new Date(batchStats.createdAt).toLocaleString())); - if (batchStats.completedAt) { - console.log(formatStats('Completed', new Date(batchStats.completedAt).toLocaleString())); - } - - // Show runs in batch with ranking - if (batchStats.runs.length > 0) { - console.log('\n' + chalk.underline('Runs in Batch')); - const sortedRuns = batchStats.runs - .filter(run => run.weightedScore !== null && run.weightedScore !== undefined) - .sort((a, b) => (b.weightedScore || 0) - (a.weightedScore || 0)); - - sortedRuns.forEach((run, index) => { - const rank = index + 1; - const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; - const status = run.status === 'completed' - ? chalk.green('✓') - : run.status === 'failed' - ? chalk.red('✗') - : run.status === 'incomplete' - ? chalk.yellow('◐') - : chalk.blue('○'); - console.log(` ${rankDisplay} ${status} ${chalk.cyan(run.suite)}/${chalk.cyan(run.scenario)} ${chalk.gray(`(${run.tier})`)} ${chalk.cyan(run.agent)} ${chalk.yellow(run.weightedScore?.toFixed(4) || 'N/A')}`); - }); - } - - } catch (error) { - log.error(chalk.red('Failed to fetch batch statistics:')); - console.error(chalk.dim(error instanceof Error ? error.message : String(error))); - } finally { - logger.close(); - } -} - - -async function runInteractiveEvaluators() { - // Start dev server for web viewing - try { - const serverUrl = await startDevServer(); - // Note: Database is now created directly in public/ directory - console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); - } catch (err) { - // Continue without web server - } - - const logger = BenchmarkLogger.getInstance(); - - try { - intro(chalk.bgYellow(' Evaluator Performance ')); - - const stats = logger.getStats(); - - if (Object.keys(stats.evaluatorStats).length === 0) { - log.warning('No evaluator data available'); - outro(chalk.yellow('Run some benchmarks first')); - return; - } - - // Sort by performance - const sorted = Object.entries(stats.evaluatorStats).sort((a, b) => b[1].averageScore - a[1].averageScore); - - console.log('\n' + chalk.underline('Performance Ranking')); - sorted.forEach(([name, stat], index) => { - const rank = index + 1; - const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; - const percent = (stat.averageScore * 100).toFixed(1); - const color = stat.averageScore >= 0.9 ? 'green' : stat.averageScore >= 0.7 ? 'yellow' : 'red'; - - console.log(` ${rankDisplay} ${chalk.bold(name.padEnd(30))} ${chalk[color](percent + '%')} ${chalk.gray(`(${stat.count} runs)`)}`); - }); - - // Show best and worst performers - const best = sorted[0]; - const worst = sorted[sorted.length - 1]; - - console.log('\n' + chalk.underline('Performance Summary')); - console.log(` ${chalk.green('Best:')} ${chalk.bold(best[0])} ${chalk.green((best[1].averageScore * 100).toFixed(1) + '%')}`); - console.log(` ${chalk.red('Needs Work:')} ${chalk.bold(worst[0])} ${chalk.red((worst[1].averageScore * 100).toFixed(1) + '%')}`); - - // Show model performance using common function - const modelStats = logger.getModelPerformanceStats(); - displayModelPerformance(modelStats); - - outro(chalk.green('Analysis complete')); - - } catch (error) { - log.error(chalk.red('Failed to fetch evaluator data:')); - console.error(chalk.dim(error instanceof Error ? error.message : String(error))); - } finally { - logger.close(); - } + return { valid: true }; } -async function runInteractiveBatchHistory() { - // Start dev server for web viewing - try { - const serverUrl = await startDevServer(); - // Note: Database is now created directly in public/ directory - console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); - console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); - console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); - } catch (err) { - // Continue without web server - console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); - } - - const logger = BenchmarkLogger.getInstance(); - - try { - const limit = await select({ - message: 'How many recent batches to show?', - options: [ - { value: 5, label: '5 batches' }, - { value: 10, label: '10 batches' }, - { value: 20, label: '20 batches' }, - { value: 50, label: '50 batches' } - ] - }) as number; - - // Execute batch history command - const batchHistory = logger.getBatchHistory(limit); - - if (batchHistory.length === 0) { - log.warning('No batch runs found'); - outro(chalk.yellow('Run some benchmarks first')); - return; - } - - intro(chalk.bgCyan(' Batch History ')); - - batchHistory.forEach((batch, index) => { - const status = batch.completedAt - ? chalk.green('✓') - : chalk.yellow('○'); - - console.log(`\n${chalk.bold(`${index + 1}.`)} ${status} ${chalk.cyan('Batch')} ${chalk.dim(batch.batchId.substring(0, 8))}...`); - console.log(` ${formatStats('Runs', `${batch.successfulRuns}/${batch.totalRuns}`, 'green')}`); - console.log(` ${formatStats('Avg Score', batch.avgWeightedScore?.toFixed(4) || 'N/A', 'yellow')}`); - console.log(` ${chalk.gray(new Date(batch.createdAt).toLocaleString())}`); - if (batch.completedAt) { - const duration = (batch.completedAt - batch.createdAt) / 1000; - console.log(` ${formatStats('Duration', `${duration.toFixed(2)}s`, 'blue')}`); - } - }); - - outro(chalk.green(`Showing ${batchHistory.length} recent batches`)); - - } catch (error) { - log.error(chalk.red('Failed to fetch batch history:')); - console.error(chalk.dim(error instanceof Error ? error.message : String(error))); - } finally { - logger.close(); - } -} - -// ============================================================================ -// SECTION 7: INTERACTIVE COMMANDS - UTILITIES -// ============================================================================ - -async function runInteractiveClear() { - const logger = BenchmarkLogger.getInstance(); - - try { - intro(chalk.bgRed(' Clear Database ')); - - // Get count before clearing - const stats = logger.getStats(); - log.warning(`Found ${chalk.bold(stats.totalRuns)} benchmark runs`); - - const shouldClear = await confirm({ - message: 'Are you sure you want to clear all data?', - initialValue: false - }); - - if (shouldClear) { - const s = spinner(); - s.start('Clearing database...'); - logger.clearDatabase(); - s.stop('Database cleared'); - outro(chalk.green('✓ All data removed')); - } else { - outro(chalk.yellow('Cancelled')); - } - - } catch (error) { - log.error(chalk.red('Failed to clear database:')); - console.error(chalk.dim(error instanceof Error ? error.message : String(error))); - } finally { - logger.close(); - } -} - -// ============================================================================ -// SECTION 8: SUCCESS CALCULATION LOGIC -// ============================================================================ - -interface CommandResult { - tool: 'shell'; - type: 'install' | 'test' | 'lint' | 'typecheck'; - raw: string; - exitCode: number; - stdout: string; - stderr: string; - durationMs: number; -} - -interface SuccessCalculationResult { - isSuccessful: boolean; - successMetric: number; -} - -function calculateSuccess( - commandLog: CommandResult[], - scores: Record, - scenario: any -): SuccessCalculationResult { - - // Validation score (commands passed / total commands) - const passedCommands = commandLog.filter(cmd => cmd.exitCode === 0).length; - const validationScore = commandLog.length > 0 - ? passedCommands / commandLog.length - : 0; - - // Critical commands (install, build, test must pass) - const criticalCommands = ['install', 'test']; // Note: 'build' is not in CommandKind, using 'test' as critical - const criticalPassed = criticalCommands.every(cmd => { - const result = commandLog.find(c => c.type === cmd); - return result && result.exitCode === 0; - }); - - // Evaluator average score - const evaluatorScore = Object.values(scores).length > 0 - ? Object.values(scores).reduce((sum, s) => sum + s, 0) / Object.values(scores).length - : 0; - - // LLM judge score (if available) - const llmJudgeScore = scores.llm_judge || 0; - - // Weighted metric (from scenario.yaml or default) - const weights = scenario.success_weights || { - validation: 0.4, - evaluators: 0.3, - llm_judge: 0.3 - }; - - const successMetric = ( - validationScore * weights.validation + - evaluatorScore * weights.evaluators + - llmJudgeScore * weights.llm_judge - ); - - // Success criteria: critical commands must pass AND success metric >= 0.7 - const isSuccessful = criticalPassed && successMetric >= 0.7; - - return { isSuccessful, successMetric }; +function checkSuiteExists(suiteName: string): boolean { + const root = findRepoRoot(); + return existsSync(join(root, 'suites', suiteName)); } -// ============================================================================ -// SECTION 9: MAIN BENCHMARK RUNNER -// ============================================================================ - -async function executeBenchmark(suite: string, scenario: string, tier: string, agent: string, model?: string, noJson?: boolean, batchId?: string, quiet?: boolean) { - // Initialize logger - const logger = BenchmarkLogger.getInstance(); - const runId = logger.startRun(suite, scenario, tier, agent, model, batchId); - const startTime = Date.now(); - - // Timeout watchdog based on scenario timeout_minutes (default 60) - let timeoutId: NodeJS.Timeout | null = null; - - // Initialize progress tracker (only if not in quiet mode) - const progress = quiet ? null : createProgress(); - - try { - // Stage 1: Setup - if (progress) updateProgress(progress, 1, 'Loading scenario configuration'); - const scenarioCfg = loadScenario(suite, scenario); - - // Start timeout watchdog after loading scenario config - const scenarioTimeoutMin = Number(scenarioCfg.timeout_minutes || 60); - const timeoutMs = Math.max(1, scenarioTimeoutMin) * 60 * 1000; - timeoutId = setTimeout(() => { - try { - logger.markRunIncomplete(`Run exceeded timeout (${scenarioTimeoutMin} minutes)`, 'timeout'); - if (!quiet) console.log(chalk.yellow(`⚠ Run timed out after ${scenarioTimeoutMin} minutes`)); - } catch {} - }, timeoutMs); - - if (progress) updateProgress(progress, 1, 'Loading prompt'); - const promptContent = loadPrompt(suite, scenario, tier); - - // Early failure check: prompt missing for non-echo agents - if (!promptContent && agent !== 'echo') { - logger.failRun('Prompt file not found', 'prompt'); - if (!quiet) console.log(chalk.red('✗ Prompt file not found')); - if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: Prompt file not found`)); - return; - } - - // Stage 2: Workspace - if (progress) updateProgress(progress, 2, 'Preparing workspace'); - const workspacePrep = prepareWorkspaceFromFixture(suite, scenario); - - // Early failure check: workspace preparation failed - if (!workspacePrep) { - logger.failRun('Workspace preparation failed', 'workspace'); - if (!quiet) console.log(chalk.red('✗ Workspace preparation failed')); - if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: Workspace preparation failed`)); - return; - } - - const workspaceDir = workspacePrep.workspaceDir; - const fixtureDir = workspacePrep.fixtureDir; - - // Initialize result structure - const result = { - suite, - scenario, - tier, - agent, - model: model || 'default', - agent_response: '', - scores: { - install_success: 0, - tests_nonregression: 0, - manager_correctness: 0, - dependency_targets: 0, - integrity_guard: 0, - }, - totals: { weighted: 0, max: 10 }, - telemetry: { - toolCalls: 0, - tokens: { in: 0, out: 0 }, - cost_usd: 0, - workspaceDir - } - }; - - - // Stage 3: Agent Execution - if (promptContent && agent !== 'echo') { - if (progress) updateProgress(progress, 3, 'Agent working...'); - try { - // Create agent adapter - const agentAdapter = createAgentAdapter(agent, model); - - // Show selected model info - ALWAYS for OpenRouter - if (agent === 'openrouter' && 'getModel' in agentAdapter) { - const adapterModel = (agentAdapter as any).getModel(); - const modelSource = 'getModelSource' in agentAdapter - ? (agentAdapter as any).getModelSource() - : 'unknown'; - - if (modelSource === 'default') { - console.log(chalk.yellow(` ⚠️ Using default model: ${chalk.cyan(adapterModel)}`)); - console.log(chalk.gray(` Tip: Search and select a model in interactive mode or use --model flag`)); - } else if (modelSource === 'environment') { - console.log(chalk.blue(` ℹ️ Using model from environment: ${chalk.cyan(adapterModel)}`)); - } else if (model && modelSource === 'parameter') { - console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(model)}`)); - - // Verify match - if (adapterModel !== model) { - console.log(chalk.yellow(` ⚠️ Warning: Model mismatch - requested: ${model}, adapter: ${adapterModel}`)); - } else { - console.log(chalk.green(` ✅ Model confirmed: ${adapterModel}`)); - } - } else { - console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(adapterModel)}`)); - } - } else if (model && agent !== 'openrouter') { - // For non-OpenRouter agents, show model if provided - console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(model)}`)); - } - - // Agent info is shown in progress bar - - // Load oracle if available - let oracle: Oracle | undefined; - const oracleFile = scenarioCfg.oracle?.answers_file; - if (oracleFile) { - const scenarioDir = getScenarioDir(suite, scenario); - const oraclePath = join(scenarioDir, oracleFile); - if (existsSync(oraclePath)) { - oracle = new Oracle(oraclePath); - if (progress) updateProgress(progress, 3, 'Agent with oracle support enabled'); - } - } - - // Build system prompt with tool usage guidance - const systemPrompt = agent === 'anthropic' - ? `You are working on a ${scenarioCfg.title}. The task is: ${scenarioCfg.description || 'Complete the development task.'}\n\nIMPORTANT: You are working in the directory: ${workspaceDir}\nThis is a prepared workspace with the files you need to modify.\n\nAvailable Tools:\n- readFile: Read any file in the workspace\n- writeFile: Modify files (e.g., package.json files)\n- runCommand: Execute shell commands (e.g., pnpm install, pnpm outdated)\n- listFiles: Explore directory structure\n- askUser: Ask questions when you need clarification or approval for major changes\n\nWork efficiently: read files to understand the current state, make necessary changes, run commands to validate, and ask questions only when truly needed for important decisions.` - : `You are working on a ${scenarioCfg.title}. The task is: ${scenarioCfg.description || 'Complete the development task.'}\n\nIMPORTANT: You are working in the directory: ${workspaceDir}\nThis is a prepared workspace with the files you need to modify.`; - - // Build the request - const request: AgentRequest = { - messages: [ - { - role: 'system' as const, - content: systemPrompt - }, - { - role: 'user' as const, - content: promptContent - } - ], - ...(workspaceDir && { workspaceDir }), - }; - - // Add tools if agent supports them (Anthropic and OpenRouter) - if ((agent === 'anthropic' || agent === 'openrouter') && workspaceDir) { - // Create workspace tool handlers - const workspaceHandlers = createWorkspaceToolHandlers(workspaceDir); - - // Start with workspace tools - const tools = getAllWorkspaceTools(); - const toolHandlers = workspaceHandlers; - - // Add askUser tool if oracle is available - if (oracle) { - tools.push(createAskUserToolDefinition()); - toolHandlers.set('askUser', createAskUserHandler(oracle)); - // Tools: readFile, writeFile, runCommand, listFiles, askUser - } else { - // Tools: readFile, writeFile, runCommand, listFiles - } - - // Convert tools to adapter-specific format - if (agent === 'openrouter') { - // Convert ToolDefinition to OpenRouter format - (request as any).tools = tools.map(tool => ({ - type: 'function', - function: { - name: tool.name, - description: tool.description, - parameters: tool.input_schema // Map input_schema → parameters - } - })); - } else { - // Anthropic uses ToolDefinition format directly - request.tools = tools; - } - - request.toolHandlers = toolHandlers; - } - - // Execute agent request - const response = await agentAdapter.send(request); - - // Show summary after agent completes - console.log(chalk.gray(` ✓ Tokens: ${response.tokensIn || 0} in, ${response.tokensOut || 0} out | Cost: $${(response.costUsd || 0).toFixed(4)}`)); - - // Update result with agent response - result.agent_response = response.content; - result.telemetry.tokens.in = response.tokensIn || 0; - result.telemetry.tokens.out = response.tokensOut || 0; - result.telemetry.cost_usd = response.costUsd || 0; - result.telemetry.toolCalls = response.toolCalls ?? 0; - - // Log telemetry to database - const duration = Date.now() - startTime; - logger.logTelemetry( - response.toolCalls ?? 0, - response.tokensIn || 0, - response.tokensOut || 0, - response.costUsd || 0, - duration, - workspaceDir - ); - - // Log oracle usage if available - if (oracle) { - const questionLog = oracle.getQuestionLog(); - if (questionLog.length > 0) { - // Oracle questions logged - (result as any).oracle_questions = questionLog; - } - } - - // Telemetry is shown in final results - - } catch (error) { - logger.failRun(error instanceof Error ? error.message : String(error), 'agent'); - if (!quiet) console.log(chalk.red('✗ Agent execution failed')); - if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: ${error instanceof Error ? error.message : String(error)}`)); - return; // Early exit - don't continue to evaluation - } - } else if (!promptContent) { - // No prompt loaded, skipping agent execution - } else { - // Using echo agent (no actual execution) - } - - // Stage 4: Validation - if (progress) updateProgress(progress, 4, 'Running validation commands'); - const commandLog = workspaceDir ? runValidationCommands(workspaceDir, scenarioCfg.validation?.commands) : []; - const diffArtifacts = workspaceDir && fixtureDir ? buildDiffArtifacts(fixtureDir, workspaceDir) : { diffSummary: [], depsDelta: [] }; - - const passedCommands = commandLog.filter(cmd => cmd.exitCode === 0).length; - if (!quiet) console.log(chalk.gray(` ✓ ${passedCommands}/${commandLog.length} commands passed`)); - - // Stage 5: Evaluation - if (progress) updateProgress(progress, 5, 'Computing scores'); - - try { - if (workspaceDir) { - // Actually run evaluators - const ctx = { - scenario: scenarioCfg, - workspaceDir, - agentResponse: result.agent_response, - commandLog, - diffSummary: diffArtifacts.diffSummary, - depsDelta: diffArtifacts.depsDelta, - }; - const { scoreCard, results: evaluatorResults } = await runEvaluators(ctx); - result.scores = { ...result.scores, ...scoreCard }; - result.totals = computeWeightedTotals(result.scores, scenarioCfg); - (result as any).evaluator_results = evaluatorResults; - (result as any).diff_summary = diffArtifacts.diffSummary; - (result as any).deps_delta = diffArtifacts.depsDelta; - - // Log evaluation results to database - for (const evalResult of evaluatorResults) { - logger.logEvaluation( - evalResult.name, - evalResult.score, - 1.0, // max score - evalResult.details - ); - } - - // Show evaluator summary - const avgScore = Object.values(scoreCard).reduce((sum, score) => sum + (score as number), 0) / Object.keys(scoreCard).length; - if (!quiet) console.log(chalk.gray(` ✓ Average score: ${(avgScore * 100).toFixed(1)}%`)); - } - } catch (e) { - // Evaluator run failed - } - - // Complete the run in database - const totalScore = Object.values(result.scores || {}).reduce((sum, score) => sum + (typeof score === 'number' ? score : 0), 0) / Object.keys(result.scores || {}).length; - - // Calculate success based on validation commands and evaluator scores - const { isSuccessful, successMetric } = calculateSuccess(commandLog, result.scores || {}, scenarioCfg); - - // Extract package manager and test results - let packageManager: string | undefined; - if (workspaceDir) { - packageManager = detectPackageManager(workspaceDir); - if (packageManager === 'unknown') { - packageManager = extractPackageManagerFromCommand(commandLog); - } - } - - const testResults = extractTestResults(commandLog); - const testResultsJson = testResults ? JSON.stringify(testResults) : undefined; - - logger.completeRun( - totalScore, - result.totals?.weighted, - { - diffSummary: diffArtifacts.diffSummary, - depsDelta: diffArtifacts.depsDelta, - oracleQuestions: (result as any).oracle_questions - }, - isSuccessful, - successMetric, - packageManager, - testResultsJson - ); - - // Stage 6: Results - if (progress) updateProgress(progress, 6, 'Preparing results'); - - if (progress) completeProgress(progress); - - // Display results in table format (only if not in quiet mode) - const duration = (Date.now() - startTime) / 1000; - const weightedScore = result.totals?.weighted || 0; - - // In quiet mode, show compact one-line output - if (quiet) { - const status = isSuccessful ? chalk.green('✓') : chalk.red('✗'); - const modelStr = model ? ` [${model}]` : ''; - const successStr = isSuccessful ? 'SUCCESS' : 'FAILED'; - console.log(`${status} ${suite}/${scenario} (${tier}) ${agent}${modelStr} - ${weightedScore.toFixed(2)}/10 [${successStr}]`); - return; - } - - console.log(`\n${chalk.bold.underline('Benchmark Results')}`); - console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); - console.log(`│ ${chalk.bold('Agent:')} ${chalk.cyan(agent.padEnd(15))} ${chalk.bold('Tier:')} ${chalk.cyan(tier.padEnd(8))} ${chalk.bold('Duration:')} ${chalk.blue(duration.toFixed(2) + 's')} │`); - console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); - console.log(`│ ${chalk.bold('Score (mean ± σ):')} ${chalk.green(weightedScore.toFixed(4))} ± ${chalk.green('0.0000')} ${chalk.gray('(out of 10.0)')} │`); - console.log(`│ ${chalk.bold('Range (min ... max):')} ${chalk.green(weightedScore.toFixed(4))} ${chalk.white('...')} ${chalk.red(weightedScore.toFixed(4))} ${chalk.gray('(1 run)')} │`); - console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); - - // Print evaluation breakdown in table format - if (result.scores) { - console.log(`\n${chalk.bold.underline('Evaluation Breakdown')}`); - console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); - console.log(`│ ${chalk.bold('Evaluator'.padEnd(25))} ${chalk.bold('Score'.padEnd(10))} ${chalk.bold('Status'.padEnd(15))} │`); - console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); - - Object.entries(result.scores).forEach(([name, score]) => { - const percent = (score as number) * 100; - const color = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; - const status = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'Excellent' : percent >= SCORE_THRESHOLDS.GOOD ? 'Good' : 'Needs Work'; - const statusColor = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; - - // Special handling for LLM Judge Evaluator - const displayName = name === 'LLMJudgeEvaluator' ? 'LLM Judge' : name; - - console.log(`│ ${chalk.cyan(displayName.padEnd(25))} ${chalk[color](score.toFixed(4).padEnd(10))} ${chalk[statusColor](status.padEnd(15))} │`); - }); - - console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); - - // Show detailed LLM Judge scores if available - displayLLMJudgeScores(result); - } - - // Print telemetry in table format - if (result.telemetry) { - console.log(`\n${chalk.bold.underline('Telemetry')}`); - console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); - console.log(`│ ${chalk.bold('Metric'.padEnd(20))} ${chalk.bold('Value'.padEnd(20))} ${chalk.bold('Unit'.padEnd(15))} │`); - console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); - console.log(`│ ${chalk.cyan('Tool Calls'.padEnd(20))} ${chalk.green((result.telemetry.toolCalls || 0).toString().padEnd(20))} ${chalk.gray('calls'.padEnd(15))} │`); - console.log(`│ ${chalk.cyan('Tokens In'.padEnd(20))} ${chalk.green((result.telemetry.tokens?.in || 0).toString().padEnd(20))} ${chalk.gray('tokens'.padEnd(15))} │`); - console.log(`│ ${chalk.cyan('Tokens Out'.padEnd(20))} ${chalk.green((result.telemetry.tokens?.out || 0).toString().padEnd(20))} ${chalk.gray('tokens'.padEnd(15))} │`); - console.log(`│ ${chalk.cyan('Cost'.padEnd(20))} ${chalk.green(`$${(result.telemetry.cost_usd || 0).toFixed(6)}`.padEnd(20))} ${chalk.gray('USD'.padEnd(15))} │`); - console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); - } - - // Show database summary in table format - try { - const stats = logger.getStats(); - console.log(`\n${chalk.bold.underline('Database Summary')}`); - console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); - console.log(`│ ${chalk.bold('Metric'.padEnd(25))} ${chalk.bold('Value'.padEnd(20))} ${chalk.bold('Status'.padEnd(10))} │`); - console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); - console.log(`│ ${chalk.cyan('Total Runs'.padEnd(25))} ${chalk.blue(stats.totalRuns.toString().padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); - console.log(`│ ${chalk.cyan('Success Rate'.padEnd(25))} ${chalk.green(`${(stats.successRate * 100).toFixed(1)}%`.padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); - console.log(`│ ${chalk.cyan('Average Score'.padEnd(25))} ${chalk.green(stats.averageWeightedScore.toFixed(4).padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); - console.log(`│ ${chalk.cyan('Database'.padEnd(25))} ${chalk.blue('benchmark-report/public/benchmarks.db'.padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); - console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); - - } catch (dbError) { - log.warning(chalk.yellow('Database query failed:')); - console.error(chalk.dim(dbError instanceof Error ? dbError.message : String(dbError))); - } - - // Optionally write JSON - if (!noJson) { - writeResult(result, suite, scenario); - console.log(`\n${chalk.green('✓')} Results saved to database and JSON`); - } else { - console.log(`\n${chalk.yellow('⚠')} JSON output disabled, results saved to database only`); - } - - // Note: Database is now created directly in public/ directory - - // Show completion outro - console.log(`\n${chalk.green('✓')} Benchmark completed successfully`); - - } catch (error) { - // Catch-all for unexpected errors - logger.failRun(error instanceof Error ? error.message : String(error), 'unknown'); - if (!quiet) console.log(chalk.red('✗ Unexpected error')); - if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: ${error instanceof Error ? error.message : String(error)}`)); - } finally { - if (progress) completeProgress(progress); - // Clear timeout watchdog if set - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = null; - } - } +function checkScenarioExists(suiteName: string, scenarioName: string): boolean { + const root = findRepoRoot(); + return existsSync(join(root, 'suites', suiteName, 'scenarios', scenarioName)); } -async function validateEnvironment() { - const missingVars: string[] = []; - - // Check for API keys based on available agents - if (!process.env.OPENROUTER_API_KEY && !process.env.ANTHROPIC_API_KEY) { - missingVars.push('OPENROUTER_API_KEY or ANTHROPIC_API_KEY'); - } - - if (missingVars.length > 0) { - console.log(chalk.red('❌ Missing required environment variables:')); - console.log(chalk.yellow(` ${missingVars.join(', ')}`)); - console.log('\n' + chalk.cyan('Setup Instructions:')); - console.log(chalk.gray('1. Get API keys from:')); - console.log(chalk.gray(' - OpenRouter: https://openrouter.ai/keys')); - console.log(chalk.gray(' - Anthropic: https://console.anthropic.com/settings/keys')); - console.log(chalk.gray('2. Create a .env file in the project root:')); - console.log(chalk.gray(' cp .env.example .env')); - console.log(chalk.gray('3. Edit .env and add your API keys:')); - console.log(chalk.gray(' OPENROUTER_API_KEY=your_key_here')); - console.log(chalk.gray(' ANTHROPIC_API_KEY=your_key_here')); - console.log(chalk.gray('4. Or set environment variables directly:')); - console.log(chalk.gray(' Windows: set OPENROUTER_API_KEY=your_key_here')); - console.log(chalk.gray(' Linux/Mac: export OPENROUTER_API_KEY=your_key_here')); - console.log('\n' + chalk.red('Please set up your environment variables and try again.')); - process.exit(1); - } -} +// Export for use by other modules +export { findRepoRoot, validateName, checkSuiteExists, checkScenarioExists }; // ============================================================================ -// SECTION 9: MAIN ENTRY POINT +// MAIN ENTRY POINT // ============================================================================ async function run() { + // Log entry point immediately (FIRST THING) + console.log(chalk.gray('[DEBUG] Benchmark CLI started')); + console.log(chalk.gray(` Args: ${JSON.stringify(process.argv)}`)); + console.log(chalk.gray(` CWD: ${process.cwd()}`)); + const parsedArgs = parseArgs(process.argv); - + console.log(chalk.gray(` Parsed args: ${JSON.stringify(parsedArgs)}`)); + // Skip environment validation for creation commands (they don't need API keys) const skipValidation = parsedArgs.cmd === 'new-suite' || parsedArgs.cmd === 'new-scenario'; - + // Check for required environment variables first (unless skipping for creation commands) if (!skipValidation) { await validateEnvironment(); } - + // Dev server will be started only when viewing statistics - + // If no arguments provided, show interactive menu if (process.argv.length <= 2) { await showInteractiveMenu(); return; } - + // Handle stats command if (parsedArgs.cmd === 'stats') { const logger = BenchmarkLogger.getInstance(); @@ -2611,21 +131,21 @@ async function run() { return; } await runInteractiveSuiteStats(); - + } else if (level === 'scenario') { if (!identifier[0] || !identifier[1]) { log.warning('Usage: pnpm bench --stats scenario '); return; } await runInteractiveScenarioStats(); - + } else if (level === 'run') { if (!identifier[0]) { log.warning('Usage: pnpm bench --stats run '); return; } await runInteractiveRunStats(); - + } else { log.warning('Usage: pnpm bench --stats '); console.log(' pnpm bench --stats suite '); @@ -2641,38 +161,38 @@ async function run() { } return; } - + // Handle clear-db command if (parsedArgs.cmd === 'clear-db') { await runInteractiveClear(); return; } - + // Handle evaluators command if (parsedArgs.cmd === 'evaluators') { await runInteractiveEvaluators(); return; } - + // Handle history command if (parsedArgs.cmd === 'history') { const logger = BenchmarkLogger.getInstance(); const limit = parsedArgs.limit; - + intro(chalk.bgCyan(' Benchmark History ')); - + try { const runHistory = logger.getRunHistory(limit); - + if (runHistory.length === 0) { log.warning('No benchmark runs found'); outro(chalk.yellow('Run a benchmark first: pnpm bench ')); return; } - + // Use common display function runHistory.forEach((run, index) => displayRunInfo(run, index)); - + // Show overall stats const stats = logger.getStats(); console.log('\n' + chalk.underline('Overall Statistics')); @@ -2681,9 +201,9 @@ async function run() { console.log(formatStats('Avg Score', stats.averageScore.toFixed(4), 'yellow')); console.log(formatStats('Avg Weighted', stats.averageWeightedScore.toFixed(4), 'yellow')); console.log(formatStats('Avg Duration', `${(stats.averageDuration / 1000).toFixed(2)}s`, 'blue')); - + outro(chalk.green(`Showing ${runHistory.length} recent runs`)); - + } catch (error) { log.error(chalk.red('Failed to fetch history:')); console.error(chalk.dim(error instanceof Error ? error.message : String(error))); @@ -2693,39 +213,39 @@ async function run() { } return; } - + // Handle batches command if (parsedArgs.cmd === 'batches') { const logger = BenchmarkLogger.getInstance(); const limit = parsedArgs.limit; - + try { const batches = logger.getAllBatches({ limit }); - + if (batches.length === 0) { log.warning('No batches found'); outro(chalk.yellow('Run some benchmarks first')); return; } - + intro(chalk.bgCyan(' Batch History ')); - + batches.forEach((batch, index) => { - const status = batch.completedAt - ? chalk.green('✓') + const status = batch.completedAt + ? chalk.green('✓') : chalk.yellow('○'); const duration = batch.duration ? `${(batch.duration / 1000).toFixed(2)}s` : 'Running...'; const successRate = batch.totalRuns > 0 ? ((batch.successfulRuns / batch.totalRuns) * 100).toFixed(0) : 0; - + console.log(`\n${chalk.bold(`${index + 1}.`)} ${status} ${chalk.cyan('Batch')} ${chalk.dim(batch.batchId.substring(0, 8))}...`); console.log(` ${formatStats('Runs', `${batch.successfulRuns}/${batch.totalRuns} (${successRate}%)`, 'green')}`); console.log(` ${formatStats('Avg Score', batch.avgWeightedScore?.toFixed(4) || 'N/A', 'yellow')}`); console.log(` ${formatStats('Duration', duration, 'blue')}`); console.log(` ${chalk.gray(new Date(batch.createdAt).toLocaleString())}`); }); - + outro(chalk.green(`Showing ${batches.length} batches`)); - + } catch (error) { log.error(chalk.red('Failed to fetch batches:')); console.error(chalk.dim(error instanceof Error ? error.message : String(error))); @@ -2735,31 +255,31 @@ async function run() { } return; } - + // Handle batch-details command if (parsedArgs.cmd === 'batch-details') { const logger = BenchmarkLogger.getInstance(); const { batchId } = parsedArgs; - + if (!batchId) { log.warning('Usage: pnpm bench --batch-details '); return; } - + try { const analytics = logger.getBatchAnalytics(batchId); - + if (!analytics) { log.error(chalk.red(`Batch ${batchId} not found`)); return; } - + intro(chalk.bgCyan(' Batch Details ')); - + const batch = analytics.batch; const duration = batch.duration / 1000; const successRate = batch.totalRuns > 0 ? ((batch.successfulRuns / batch.totalRuns) * 100).toFixed(1) : 0; - + console.log(`\n${chalk.bold('Batch ID:')} ${chalk.dim(batchId.substring(0, 16))}...`); console.log(formatStats('Status', batch.completedAt ? 'Completed' : 'Running', batch.completedAt ? 'green' : 'yellow')); console.log(formatStats('Total Runs', `${batch.totalRuns}`, 'blue')); @@ -2767,7 +287,7 @@ async function run() { console.log(formatStats('Avg Score', batch.avgWeightedScore.toFixed(4), 'yellow')); console.log(formatStats('Duration', `${duration.toFixed(2)}s`, 'blue')); console.log(formatStats('Started', new Date(batch.createdAt).toLocaleString())); - + // Suite breakdown if (analytics.suiteBreakdown.length > 0) { console.log(`\n${chalk.bold.underline('Suite Breakdown')}`); @@ -2776,7 +296,7 @@ async function run() { console.log(` ${chalk.cyan(suite.suite)}/${suite.scenario}: ${suite.avgWeightedScore.toFixed(2)}/10 ${chalk.gray(`(${rate}% success, ${suite.runs} runs)`)}`); }); } - + // Agent performance if (analytics.agentPerformance.length > 0) { console.log(`\n${chalk.bold.underline('Agent Performance')}`); @@ -2787,7 +307,7 @@ async function run() { console.log(` ${rankDisplay} ${chalk.cyan(agent.agent)}${modelStr}: ${chalk[scoreColor](agent.avgWeightedScore.toFixed(2))}/10 ${chalk.gray(`(${agent.successfulRuns}/${agent.runs})`)}`); }); } - + // Tier distribution if (analytics.tierDistribution.length > 0) { console.log(`\n${chalk.bold.underline('Tier Distribution')}`); @@ -2795,7 +315,7 @@ async function run() { console.log(` ${chalk.cyan(tier.tier)}: ${tier.avgWeightedScore.toFixed(2)}/10 ${chalk.gray(`(${tier.successfulRuns}/${tier.runs} runs)`)}`); }); } - + // Failed runs if (analytics.failedRuns.length > 0) { console.log(`\n${chalk.bold.underline(chalk.red('Failed Runs'))}`); @@ -2803,9 +323,9 @@ async function run() { console.log(` ${chalk.red('✗')} ${run.suite}/${run.scenario} (${run.tier}) ${run.agent}`); }); } - + outro(chalk.green('Batch analytics complete')); - + } catch (error) { log.error(chalk.red('Failed to fetch batch details:')); console.error(chalk.dim(error instanceof Error ? error.message : String(error))); @@ -2815,61 +335,61 @@ async function run() { } return; } - + // Handle compare-batches command if (parsedArgs.cmd === 'compare-batches') { const logger = BenchmarkLogger.getInstance(); const { batchIds } = parsedArgs; - + if (!batchIds || batchIds.length < 2) { log.warning('Usage: pnpm bench --compare-batches [batch-id3...]'); return; } - + try { const batches = logger.getBatchComparison(batchIds); - + if (batches.length === 0) { log.error(chalk.red('No batches found with the provided IDs')); return; } - + intro(chalk.bgMagenta(' Batch Comparison ')); - + console.log(`\n${chalk.bold('Comparing')} ${batches.length} batches:\n`); - + // Create comparison table - console.log(chalk.bold('Batch'.padEnd(12)) + ' | ' + - chalk.bold('Runs'.padEnd(8)) + ' | ' + - chalk.bold('Success'.padEnd(10)) + ' | ' + - chalk.bold('Avg Score'.padEnd(10)) + ' | ' + + console.log(chalk.bold('Batch'.padEnd(12)) + ' | ' + + chalk.bold('Runs'.padEnd(8)) + ' | ' + + chalk.bold('Success'.padEnd(10)) + ' | ' + + chalk.bold('Avg Score'.padEnd(10)) + ' | ' + chalk.bold('Duration')); console.log('─'.repeat(70)); - + batches.forEach(batch => { const batchIdShort = batch.batchId.substring(0, 8) + '...'; const successRate = batch.totalRuns > 0 ? ((batch.successfulRuns / batch.totalRuns) * 100).toFixed(0) + '%' : 'N/A'; const duration = batch.duration ? `${(batch.duration / 1000).toFixed(0)}s` : 'N/A'; const score = batch.avgWeightedScore?.toFixed(2) || 'N/A'; - + console.log( - chalk.dim(batchIdShort.padEnd(12)) + ' | ' + - `${batch.totalRuns}`.padEnd(8) + ' | ' + - successRate.padEnd(10) + ' | ' + - chalk.yellow(score.padEnd(10)) + ' | ' + + chalk.dim(batchIdShort.padEnd(12)) + ' | ' + + `${batch.totalRuns}`.padEnd(8) + ' | ' + + successRate.padEnd(10) + ' | ' + + chalk.yellow(score.padEnd(10)) + ' | ' + duration ); }); - + // Show best performer - const bestBatch = batches.reduce((best, current) => + const bestBatch = batches.reduce((best, current) => (current.avgWeightedScore || 0) > (best.avgWeightedScore || 0) ? current : best ); - + console.log(`\n${chalk.green('Best performing batch:')} ${chalk.dim(bestBatch.batchId.substring(0, 8))}... with score ${chalk.bold(bestBatch.avgWeightedScore?.toFixed(4) || 'N/A')}`); - + outro(chalk.green('Comparison complete')); - + } catch (error) { log.error(chalk.red('Failed to compare batches:')); console.error(chalk.dim(error instanceof Error ? error.message : String(error))); @@ -2879,13 +399,13 @@ async function run() { } return; } - + // Handle new-suite command if (parsedArgs.cmd === 'new-suite') { await createNewSuite(parsedArgs.name); return; } - + // Handle new-scenario command if (parsedArgs.cmd === 'new-scenario') { const { suite, name } = parsedArgs; @@ -2897,29 +417,94 @@ async function run() { await createNewScenario(suite, name); return; } - - const { cmd, suite, scenario, tier, agent, model, noJson } = parsedArgs; + + const { cmd, suite, scenario, tier, agent, model, batchId, specialist, skipWarmup, warmupOnly, quiet } = parsedArgs; if (cmd !== 'bench' || !suite || !scenario) { showHelp(); process.exit(1); } - - // Show modern CLI intro with hyperfine-style header - console.log(chalk.bold.underline('Demo: Benchmarking AI Agents:')); - console.log(`\n${chalk.green('►')} ${chalk.green('pnpm bench')} ${chalk.yellow(`'${suite}/${scenario}'`)} ${chalk.yellow(`'${tier}'`)} ${chalk.yellow(`'${agent}'`)}`); - - log.info(chalk.bold(`Running: ${suite}/${scenario}`)); - log.info(`${chalk.gray('Tier:')} ${chalk.cyan(tier)} ${chalk.gray('Agent:')} ${chalk.cyan(agent)}`); - - // Warn if OpenRouter agent but no model specified - if (agent === 'openrouter' && !model && !process.env.OPENROUTER_MODEL) { - console.log(chalk.yellow(`\n⚠️ Warning: No model specified for OpenRouter agent. Using default model.`)); - console.log(chalk.gray(` Tip: Use --model flag or set OPENROUTER_MODEL environment variable`)); - console.log(chalk.gray(` Example: pnpm bench ${suite}/${scenario} ${tier} ${agent} --model openai/gpt-4o-mini\n`)); + + // Log resolved paths BEFORE any benchmark operations + try { + console.log(chalk.gray('[DEBUG] Path resolution:')); + const repoRoot = findRepoRoot(); + console.log(chalk.gray(` Repo root: ${repoRoot}`)); + + // Try to resolve scenario paths + try { + const { getScenarioDir } = await import('./domain/scenario.ts'); + const scenarioDir = getScenarioDir(suite, scenario); + console.log(chalk.gray(` Scenario dir: ${scenarioDir}`)); + console.log(chalk.gray(` Scenario dir exists: ${existsSync(scenarioDir)}`)); + + const scenarioYaml = join(scenarioDir, 'scenario.yaml'); + console.log(chalk.gray(` Scenario yaml: ${scenarioYaml}`)); + console.log(chalk.gray(` Scenario yaml exists: ${existsSync(scenarioYaml)}`)); + } catch (pathErr) { + console.error(chalk.red(`[DEBUG] Failed to resolve scenario paths: ${pathErr instanceof Error ? pathErr.message : String(pathErr)}`)); + } + } catch (rootErr) { + console.error(chalk.red(`[DEBUG] Failed to resolve repo root: ${rootErr instanceof Error ? rootErr.message : String(rootErr)}`)); + } + + // Handle warmup-only mode + if (warmupOnly) { + console.log(chalk.blue('Running warmup only...')); + console.log(chalk.blue(`Suite: ${suite}, Scenario: ${scenario}`)); + + // Import necessary modules + const { loadScenario } = await import('./domain/scenario.ts'); + const { executeWarmup } = await import('./domain/warmup.ts'); + const { createAgentAdapter } = await import('./domain/agent.ts'); + + try { + const scenarioCfg = loadScenario(suite, scenario); + const warmupResult = await executeWarmup(suite, scenario, scenarioCfg, createAgentAdapter, false); + + if (!warmupResult.success) { + console.error(chalk.red(`\n❌ Warmup failed: ${warmupResult.error}`)); + if (warmupResult.agentError) { + console.error(chalk.red(`Agent error: ${warmupResult.agentError}`)); + } + process.exit(1); + } + + console.log(chalk.green('\n✓ Warmup completed successfully')); + console.log(chalk.blue(`Control folder: ${warmupResult.controlPath}`)); + if (warmupResult.controlContents && warmupResult.controlContents.length > 0) { + console.log(chalk.blue(`Contents (${warmupResult.controlContents.length} items):`)); + warmupResult.controlContents.forEach(item => { + console.log(chalk.gray(` - ${item}`)); + }); + } + process.exit(0); + } catch (error) { + console.error(chalk.red(`\n❌ Warmup exception: ${error instanceof Error ? error.message : String(error)}`)); + if (error instanceof Error && error.stack) { + console.error(chalk.gray(error.stack)); + } + process.exit(1); + } } - + + // Show modern CLI intro with hyperfine-style header (skip if quiet mode) + if (!quiet) { + console.log(chalk.bold.underline('Demo: Benchmarking AI Agents:')); + console.log(`\n${chalk.green('►')} ${chalk.green('pnpm bench')} ${chalk.yellow(`'${suite}/${scenario}'`)} ${chalk.yellow(`'${tier}'`)} ${chalk.yellow(`'${agent}'`)}`); + + log.info(chalk.bold(`Running: ${suite}/${scenario}`)); + log.info(`${chalk.gray('Tier:')} ${chalk.cyan(tier)} ${chalk.gray('Agent:')} ${chalk.cyan(agent)}`); + + // Warn if OpenRouter agent but no model specified + if (agent === 'openrouter' && !model && !process.env.OPENROUTER_MODEL) { + console.log(chalk.yellow(`\n⚠️ Warning: No model specified for OpenRouter agent. Using default model.`)); + console.log(chalk.gray(` Tip: Use --model flag or set OPENROUTER_MODEL environment variable`)); + console.log(chalk.gray(` Example: pnpm bench ${suite}/${scenario} ${tier} ${agent} --model openai/gpt-4o-mini\n`)); + } + } + // Execute the benchmark - await executeBenchmark(suite, scenario, tier, agent, model, noJson); + await executeBenchmark(suite, scenario, tier, agent, model, batchId, quiet, specialist, skipWarmup); } // Cleanup handlers @@ -2963,8 +548,14 @@ process.on('unhandledRejection', (reason, promise) => { }); run().catch((err) => { + console.error(chalk.red('\n[DEBUG] CLI run() caught exception')); + console.error(chalk.red(` Error: ${err instanceof Error ? err.message : String(err)}`)); + if (err instanceof Error && err.stack) { + console.error(chalk.gray(` Stack trace:\n${err.stack}`)); + } + console.log(`\n${chalk.red('✗')} Benchmark failed: ${err instanceof Error ? err.message : String(err)}`); - + // Try to log the error to database if logger is available try { const logger = BenchmarkLogger.getInstance(); @@ -2972,7 +563,7 @@ run().catch((err) => { } catch (logErr) { console.log(`${chalk.yellow('⚠')} Failed to log error to database: ${logErr instanceof Error ? logErr.message : String(logErr)}`); } - + stopDevServer(); process.exit(1); }); diff --git a/packages/harness/src/cli.ts.backup b/packages/harness/src/cli.ts.backup new file mode 100644 index 0000000..0951f54 --- /dev/null +++ b/packages/harness/src/cli.ts.backup @@ -0,0 +1,3200 @@ +#!/usr/bin/env tsx +import { config } from 'dotenv'; +import { readFileSync, writeFileSync, mkdirSync, mkdtempSync, existsSync, cpSync, readdirSync, statSync, unlinkSync, rmSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import YAML from 'yaml'; + +// Load environment variables from .env file in project root +// Find workspace root by looking for pnpm-workspace.yaml +// In case of nested workspaces, find the topmost one +function findWorkspaceRoot(startDir: string): string { + let currentDir = startDir; + let lastWorkspaceRoot = startDir; + + while (currentDir !== resolve(currentDir, '..')) { + if (existsSync(join(currentDir, 'pnpm-workspace.yaml'))) { + lastWorkspaceRoot = currentDir; + } + currentDir = resolve(currentDir, '..'); + } + + return lastWorkspaceRoot; +} + +const workspaceRoot = findWorkspaceRoot(process.cwd()); +const envPath = resolve(workspaceRoot, '.env'); +config({ path: envPath }); +import { EchoAgent, ClaudeCodeAdapter, OpenRouterAdapter, AnthropicAdapter, SpecialistAdapter, type AgentAdapter, type AgentRequest } from '../../agent-adapters/src/index.ts'; +import { OpenAI } from 'openai'; +import { runEvaluators } from '../../evaluators/src/index.ts'; +import { runValidationCommands } from './runtime/validation.ts'; +import { buildDiffArtifacts } from './runtime/diff.ts'; +import { detectPackageManager, extractPackageManagerFromCommand, extractTestResults } from './runtime/extractors.ts'; +import { Oracle } from './runtime/oracle.ts'; +import { createAskUserToolDefinition, createAskUserHandler } from './runtime/ask-user-tool.ts'; +import { getAllWorkspaceTools, createWorkspaceToolHandlers } from './runtime/workspace-tools.ts'; +import { BenchmarkLogger } from '@ze/database'; +import { intro, outro, spinner, log, select, confirm, multiselect, isCancel, cancel, text } from '@clack/prompts'; +import chalk from 'chalk'; +import figlet from 'figlet'; +import { startDevServer, getServerUrl, stopDevServer } from './dev-server.ts'; +import { OpenRouterAPI } from './lib/openrouter-api.ts'; + +// ============================================================================ +// DYNAMIC MODEL LOADING +// ============================================================================ + +async function getAvailableAgents(): Promise> { + const agents = [ + { value: '__ALL__', label: 'All agents' }, + { value: 'echo', label: 'Echo (Test Agent)' }, + { value: 'openrouter', label: 'OpenRouter (Any Model)' }, + { value: 'anthropic', label: 'Anthropic Claude (Direct API)' }, + { value: 'claude-code', label: 'Claude Code' } + ]; + + return agents; +} + +// ============================================================================ +// CONSTANTS +// ============================================================================ + +const TABLE_WIDTH = 60; +const SCORE_THRESHOLDS = { + EXCELLENT: 90, + GOOD: 70, + NEEDS_WORK: 60 +} as const; + +// Simple progress state and helpers +const TOTAL_STAGES = 6; + +interface ProgressState { + spinner: any; + currentStage: number; +} + +function createProgress(): ProgressState { + return { + spinner: spinner(), + currentStage: 0 + }; +} + +function updateProgress(state: ProgressState, stage: number, description: string) { + const percent = Math.round((stage / TOTAL_STAGES) * 100); + const message = `[${stage}/${TOTAL_STAGES}] ${percent}% - ${description}`; + + if (state.currentStage === 0) { + // First time - start the spinner + state.spinner.start(message); + } else { + // Update existing spinner message + state.spinner.message(message); + } + + state.currentStage = stage; +} + +function completeProgress(state: ProgressState) { + state.spinner.stop('Benchmark complete'); +} + +// ============================================================================ +// SECTION 1: UTILITY FUNCTIONS +// ============================================================================ +function formatStats(label: string, value: string | number, color: 'green' | 'blue' | 'yellow' | 'red' = 'blue') { + return `${chalk.gray(label)}: ${chalk[color](value)}`; +} + +function createTitle() { + return figlet.textSync('ze-benchmarks', { + font: 'ANSI Shadow', + horizontalLayout: 'fitted', + verticalLayout: 'default' + }); +} + +// LLM Judge display function +function displayLLMJudgeScores(result: { scores?: Record; evaluator_results?: Array<{ name: string; details?: string }> }) { + const llmJudgeScore = (result.scores as any)['LLMJudgeEvaluator']; + const evaluatorResults = (result as any).evaluator_results; + + let llmJudgeResult = null; + if (evaluatorResults && Array.isArray(evaluatorResults)) { + llmJudgeResult = evaluatorResults.find((r: any) => r.name === 'LLMJudgeEvaluator'); + } + + if (!llmJudgeResult && !llmJudgeScore) return; + + const details = llmJudgeResult?.details || llmJudgeScore?.details; + if (!details) return; + + try { + const parsedDetails = JSON.parse(details); + if (parsedDetails.scores && Array.isArray(parsedDetails.scores)) { + console.log(`\n${chalk.bold.underline('LLM Judge Detailed Scores')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Category'.padEnd(20))} ${chalk.bold('Score'.padEnd(8))} ${chalk.bold('Weight'.padEnd(8))} ${chalk.bold('Status'.padEnd(15))} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + + const weights: Record = { + 'dependency_quality': 25, + 'safety_stability': 20, + 'best_practices': 15, + 'monorepo_coordination': 15, + 'technical_execution': 10, + 'communication_transparency': 10, + 'long_term_maintainability': 5 + }; + + const expectedCategories = [ + 'dependency_quality', + 'safety_stability', + 'best_practices', + 'monorepo_coordination', + 'technical_execution', + 'communication_transparency', + 'long_term_maintainability', + 'overall_integrity' + ]; + + const scoreMap = new Map(); + parsedDetails.scores.forEach((score: any) => { + if (score.category && score.score !== undefined) { + scoreMap.set(score.category, score); + } + }); + + expectedCategories.forEach(category => { + const score = scoreMap.get(category); + const weight = weights[category] || 0; + + if (score) { + const percent = (score.score / 5) * 100; + const color = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; + const status = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'Excellent' : percent >= SCORE_THRESHOLDS.GOOD ? 'Good' : 'Needs Work'; + const statusColor = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; + const categoryName = category.replace(/_/g, ' ').replace(/\\b\\w/g, (l: string) => l.toUpperCase()); + + console.log(`│ ${chalk.cyan(categoryName.padEnd(20))} ${chalk[color]((score.score.toFixed(1)).padEnd(8))} ${chalk.gray((weight + '%').padEnd(8))} ${chalk[statusColor](status.padEnd(15))} │`); + } else { + const categoryName = category.replace(/_/g, ' ').replace(/\\b\\w/g, (l: string) => l.toUpperCase()); + console.log(`│ ${chalk.red(categoryName.padEnd(20))} ${chalk.red('N/A'.padEnd(8))} ${chalk.gray((weight + '%').padEnd(8))} ${chalk.red('Missing'.padEnd(15))} │`); + } + }); + + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + if (parsedDetails.overall_assessment) { + console.log(`\n${chalk.bold('LLM Judge Assessment:')}`); + console.log(chalk.gray(parsedDetails.overall_assessment)); + } + + if (parsedDetails.input_tokens) { + console.log(`\n${chalk.bold('Token Usage:')}`); + console.log(chalk.blue(`Input tokens: ${parsedDetails.input_tokens}`)); + } + } + } catch (error) { + console.log(`\n${chalk.bold('LLM Judge Details:')}`); + console.log(chalk.gray(details)); + } +} + +// Common display functions +function displayRunInfo(run: { status: string; suite: string; scenario: string; tier: string; agent: string; model?: string; weightedScore?: number | null; runId: string; startedAt: string | number }, index: number) { + const status = run.status === 'completed' + ? chalk.green('✓') + : run.status === 'failed' + ? chalk.red('✗') + : run.status === 'incomplete' + ? chalk.yellow('◐') + : chalk.blue('○'); + + console.log(`\n${chalk.bold(`${index + 1}.`)} ${status} ${chalk.cyan(run.suite)}/${chalk.cyan(run.scenario)} ${chalk.gray(`(${run.tier})`)}`); + console.log(` ${formatStats('Agent', run.agent + (run.model ? ` (${run.model})` : ''))}`); + console.log(` ${formatStats('Score', run.weightedScore?.toFixed(4) || 'N/A', 'green')}`); + console.log(` ${chalk.gray(new Date(run.startedAt).toLocaleString())}`); + console.log(` ${chalk.dim(`ID: ${run.runId.substring(0, 8)}...`)}`); +} + + +function displayModelPerformance(modelStats: Array<{ model: string; avgScore: number; runs: number }>) { + if (modelStats.length === 0) return; + + console.log('\n' + chalk.underline('Model Performance')); + modelStats.forEach((model, index) => { + const rank = index + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + const percent = model.avgScore > 1 ? model.avgScore.toFixed(1) : (model.avgScore * 100).toFixed(1); + const color = model.avgScore >= 0.9 ? 'green' : model.avgScore >= 0.7 ? 'yellow' : 'red'; + + console.log(` ${rankDisplay} ${chalk.bold(model.model.padEnd(35))} ${chalk[color](percent + '%')} ${chalk.gray(`(${model.runs} runs)`)}`); + }); + + const bestModel = modelStats[0]; + const bestPercent = bestModel.avgScore > 1 ? bestModel.avgScore.toFixed(1) : (bestModel.avgScore * 100).toFixed(1); + console.log(`\n ${chalk.cyan('Top Model:')} ${chalk.bold(bestModel.model)} ${chalk.green(bestPercent + '%')} ${chalk.gray(`(${bestModel.runs} runs)`)}`); +} + +// ============================================================================ +// SECTION 2: CORE DOMAIN FUNCTIONS +// ============================================================================ + +function computeWeightedTotals( + scores: Record, + scenarioCfg: { rubric_overrides?: { weights?: Record } }, +) { + const baseWeights: Record = { + install_success: 1.5, + tests_nonregression: 2.5, + manager_correctness: 1, + dependency_targets: 2, + integrity_guard: 1.5, + }; + + const overrideWeights = scenarioCfg.rubric_overrides?.weights ?? {}; + + let totalWeight = 0; + let achieved = 0; + + for (const [metric, score] of Object.entries(scores || {})) { + const weight = overrideWeights[metric] ?? baseWeights[metric] ?? 1; + if (weight <= 0) continue; + totalWeight += weight; + achieved += (typeof score === 'number' ? score : 0) * weight; + } + + const weighted = totalWeight > 0 ? (achieved / totalWeight) * 10 : 0; + return { weighted: Number(weighted.toFixed(4)), max: 10 }; +} + +function findRepoRoot(): string { + return resolve(fileURLToPath(import.meta.url), '../../../..'); +} + +// ============================================================================ +// SECTION 2.5: VALIDATION HELPERS FOR SUITE/SCENARIO CREATION +// ============================================================================ + +function validateName(name: string, type: 'suite' | 'scenario'): { valid: boolean; error?: string } { + if (!name || name.trim().length === 0) { + return { valid: false, error: `${type} name cannot be empty` }; + } + + // Check kebab-case: lowercase, alphanumeric + hyphens, no spaces or special chars + const kebabCasePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; + if (!kebabCasePattern.test(name)) { + return { + valid: false, + error: `${type} name must be in kebab-case (lowercase, alphanumeric and hyphens only, e.g., "my-benchmark")` + }; + } + + return { valid: true }; +} + +function checkSuiteExists(suiteName: string): boolean { + const root = findRepoRoot(); + const suitePath = join(root, 'suites', suiteName); + return existsSync(suitePath); +} + +function checkScenarioExists(suiteName: string, scenarioName: string): boolean { + const root = findRepoRoot(); + const scenarioPath = join(root, 'suites', suiteName, 'scenarios', scenarioName); + return existsSync(scenarioPath); +} + +async function createNewSuite(name?: string): Promise { + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + + // Get suite name from argument or prompt + let suiteName: string | undefined = name; + if (!suiteName) { + intro(chalk.bgGreen(' Create New Suite ')); + const input = await text({ + message: 'Enter suite name (kebab-case):', + placeholder: 'e.g., my-benchmark-suite', + validate: (value) => { + const validation = validateName(value, 'suite'); + if (!validation.valid) { + return validation.error; + } + if (checkSuiteExists(value)) { + return `Suite "${value}" already exists`; + } + return; + } + }); + + if (isCancel(input)) { + cancel('Operation cancelled.'); + return; + } + suiteName = input as string; + } + + // Validate name - TypeScript guard + if (!suiteName) { + log.error('Suite name is required'); + return; + } + + // TypeScript type narrowing - suiteName is guaranteed to be string here + const finalSuiteName = suiteName as string; + const validation = validateName(finalSuiteName, 'suite'); + if (!validation.valid) { + log.error(validation.error || 'Invalid suite name'); + return; + } + + // Check if suite exists + if (checkSuiteExists(finalSuiteName)) { + log.error(`Suite "${finalSuiteName}" already exists at suites/${finalSuiteName}/`); + return; + } + + // Create directory structure + const suitePath = join(suitesDir, finalSuiteName); + const promptsPath = join(suitePath, 'prompts'); + const scenariosPath = join(suitePath, 'scenarios'); + + const s = spinner(); + s.start('Creating suite directory structure...'); + + try { + mkdirSync(suitePath, { recursive: true }); + mkdirSync(promptsPath, { recursive: true }); + mkdirSync(scenariosPath, { recursive: true }); + s.stop('Suite created'); + + // Show relative path + const relativePath = join('suites', finalSuiteName); + console.log(`\n${chalk.green('✓')} Suite created at: ${chalk.cyan(relativePath)}`); + console.log(` ${chalk.gray('Structure:')} ${relativePath}/{prompts,scenarios}`); + + outro(chalk.green(`Suite "${finalSuiteName}" created successfully`)); + } catch (error) { + s.stop('Failed to create suite'); + log.error(`Failed to create suite: ${error instanceof Error ? error.message : String(error)}`); + } +} + +async function createNewScenario(suite?: string, scenarioName?: string): Promise { + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + + // Get suite name + let suiteName = suite; + if (!suiteName) { + intro(chalk.bgGreen(' Create New Scenario ')); + + // Check if any suites exist + if (!existsSync(suitesDir)) { + log.error('No suites directory found. Create a suite first.'); + return; + } + + const existingSuites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + + if (existingSuites.length === 0) { + const shouldCreate = await confirm({ + message: 'No existing suites found. Create a new suite first?', + initialValue: true + }); + + if (isCancel(shouldCreate) || !shouldCreate) { + cancel('Operation cancelled.'); + return; + } + + await createNewSuite(); + // Refresh suites list + const refreshedSuites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + if (refreshedSuites.length === 0) { + log.error('Failed to create suite or no suites available'); + return; + } + existingSuites.push(...refreshedSuites); + } + + const selectedSuite = await select({ + message: 'Select suite to add scenario to:', + options: existingSuites.map(s => ({ value: s, label: s })) + }); + + if (isCancel(selectedSuite)) { + cancel('Operation cancelled.'); + return; + } + suiteName = selectedSuite as string; + } + + // TypeScript guard - suiteName must be string at this point + if (!suiteName) { + log.error('Suite name is required'); + return; + } + + // Validate suite exists + if (!checkSuiteExists(suiteName)) { + log.error(`Suite "${suiteName}" does not exist. Create it first with --new-suite.`); + return; + } + + // Get scenario name + let name: string | undefined = scenarioName; + if (!name) { + const input = await text({ + message: 'Enter scenario name (kebab-case):', + placeholder: 'e.g., my-test-scenario', + validate: (value) => { + const validation = validateName(value, 'scenario'); + if (!validation.valid) { + return validation.error; + } + if (checkScenarioExists(suiteName, value)) { + return `Scenario "${value}" already exists in suite "${suiteName}"`; + } + return; + } + }); + + if (isCancel(input)) { + cancel('Operation cancelled.'); + return; + } + name = input as string; + } + + // Validate name - TypeScript guard + if (!name) { + log.error('Scenario name is required'); + return; + } + + // TypeScript type narrowing - name is guaranteed to be string here + const finalName = name as string; + const validation = validateName(finalName, 'scenario'); + if (!validation.valid) { + log.error(validation.error || 'Invalid scenario name'); + return; + } + + // Check if scenario exists + if (checkScenarioExists(suiteName, finalName)) { + log.error(`Scenario "${finalName}" already exists in suite "${suiteName}" at suites/${suiteName}/scenarios/${finalName}/`); + return; + } + + const s = spinner(); + s.start('Creating scenario structure...'); + + try { + // Create scenario directory + const scenarioPath = join(suitesDir, suiteName, 'scenarios', finalName); + mkdirSync(scenarioPath, { recursive: true }); + + // Create prompts directory for this scenario + const promptsPath = join(suitesDir, suiteName, 'prompts', finalName); + mkdirSync(promptsPath, { recursive: true }); + + s.message('Copying scenario template...'); + + // Copy and customize scenario.yaml template + const templatePath = join(root, 'docs', 'templates', 'scenario.yaml'); + if (!existsSync(templatePath)) { + throw new Error(`Template file not found: ${templatePath}`); + } + + let templateContent = readFileSync(templatePath, 'utf8'); + // Replace placeholders + templateContent = templateContent.replace(/^id: my-scenario$/m, `id: ${finalName}`); + templateContent = templateContent.replace(/^suite: my-suite$/m, `suite: ${suiteName}`); + + const scenarioYamlPath = join(scenarioPath, 'scenario.yaml'); + writeFileSync(scenarioYamlPath, templateContent); + + s.message('Creating oracle-answers.json...'); + + // Create oracle-answers.json + const oraclePath = join(scenarioPath, 'oracle-answers.json'); + writeFileSync(oraclePath, '{}\n'); + + s.message('Creating repo-fixture directory...'); + + // Create repo-fixture directory + const repoFixturePath = join(scenarioPath, 'repo-fixture'); + mkdirSync(repoFixturePath, { recursive: true }); + + s.message('Copying repo-fixture guide to README.md...'); + + // Copy repo-fixture.md template content into README.md inside repo-fixture + const repoFixtureTemplatePath = join(root, 'docs', 'templates', 'repo-fixture.md'); + if (existsSync(repoFixtureTemplatePath)) { + const templateContent = readFileSync(repoFixtureTemplatePath, 'utf8'); + const repoFixtureReadmePath = join(repoFixturePath, 'README.md'); + writeFileSync(repoFixtureReadmePath, templateContent); + } else { + log.warning(`Template file not found: ${repoFixtureTemplatePath}`); + // Create a basic README if template is missing + const repoFixtureReadmePath = join(repoFixturePath, 'README.md'); + const basicContent = `# Repository Fixture + +This directory contains the starting codebase state for this scenario. + +## Setup Instructions + +1. Add a \`package.json\` file with your project dependencies +2. Include source files, tests, and configuration files +3. Ensure the fixture represents the starting state before the agent performs the task +4. Test that baseline commands (install, build, test) work correctly +`; + writeFileSync(repoFixtureReadmePath, basicContent); + } + + s.message('Creating default prompt tiers...'); + + // Create default prompt tier files + const promptTiers = [ + { file: 'L0-minimal.md', content: 'Complete the task with minimal guidance.\n' }, + { file: 'L1-basic.md', content: 'Complete the task with basic context and requirements.\n\nConstraints and goals:\n- Follow best practices\n- Ensure correctness\n' }, + { file: 'L2-directed.md', content: 'Complete the task with detailed guidance.\n\nConstraints and goals:\n- Follow all specified requirements\n- Maintain code quality\n- Ensure tests pass\n- Handle edge cases appropriately\n\nIf major changes are required, ask before proceeding.\n' } + ]; + + for (const tier of promptTiers) { + const tierPath = join(promptsPath, tier.file); + writeFileSync(tierPath, tier.content); + } + + s.stop('Scenario created'); + + // Show relative paths + const scenarioRelativePath = join('suites', suiteName, 'scenarios', finalName); + const promptsRelativePath = join('suites', suiteName, 'prompts', finalName); + + console.log(`\n${chalk.green('✓')} Scenario created:`); + console.log(` ${chalk.cyan('Scenario:')} ${scenarioRelativePath}/`); + console.log(` ${chalk.cyan('Prompts:')} ${promptsRelativePath}/`); + const repoFixtureRelativePath = join('suites', suiteName, 'scenarios', finalName, 'repo-fixture'); + + console.log(`\n${chalk.gray('Created files:')}`); + console.log(` - ${scenarioRelativePath}/scenario.yaml`); + console.log(` - ${scenarioRelativePath}/oracle-answers.json`); + console.log(` - ${repoFixtureRelativePath}/README.md ${chalk.dim('(setup guide)')}`); + console.log(` - ${repoFixtureRelativePath}/ ${chalk.dim('(empty - add your fixture files here)')}`); + console.log(` - ${promptsRelativePath}/L0-minimal.md`); + console.log(` - ${promptsRelativePath}/L1-basic.md`); + console.log(` - ${promptsRelativePath}/L2-directed.md`); + console.log(`\n${chalk.yellow('Next steps:')}`); + console.log(` ${chalk.cyan('1.')} Read ${chalk.bold('repo-fixture/README.md')} for setup instructions`); + console.log(` ${chalk.cyan('2.')} Add your starting codebase to ${chalk.bold('repo-fixture/')} directory`); + console.log(` ${chalk.cyan('3.')} Customize ${chalk.bold('scenario.yaml')} with your scenario configuration`); + console.log(` ${chalk.cyan('4.')} Update prompt files in ${chalk.bold('prompts/')} directory`); + + outro(chalk.green(`Scenario "${finalName}" created successfully in suite "${suiteName}"`)); + } catch (error) { + s.stop('Failed to create scenario'); + log.error(`Failed to create scenario: ${error instanceof Error ? error.message : String(error)}`); + } +} + +function loadScenario(suite: string, scenario: string) { + const root = findRepoRoot(); + const scenarioPath = join(root, 'suites', suite, 'scenarios', scenario, 'scenario.yaml'); + const yamlText = readFileSync(scenarioPath, 'utf8'); + return YAML.parse(yamlText); +} + +function getScenarioDir(suite: string, scenario: string) { + const root = findRepoRoot(); + return join(root, 'suites', suite, 'scenarios', scenario); +} + +// ============================================================================ +// DYNAMIC TIER LOADING +// ============================================================================ + +function getTierLabel(tier: string): string { + const labels: Record = { + 'L0': 'L0 - Minimal', + 'L1': 'L1 - Basic', + 'L2': 'L2 - Directed', + 'L3': 'L3 - Migration', + 'Lx': 'Lx - Adversarial' + }; + return labels[tier] || tier; +} + +function getAvailableTiers(suite: string, scenario: string): Array<{value: string, label: string}> { + const root = findRepoRoot(); + const promptDir = join(root, 'suites', suite, 'prompts', scenario); + + if (!existsSync(promptDir)) { + return []; + } + + const files = readdirSync(promptDir); + const tierPattern = /^(L\d+|Lx)(-.*)?\.md$/; + + const tiers = new Set(); + files.forEach(file => { + const match = file.match(tierPattern); + if (match) { + tiers.add(match[1]); + } + }); + + return Array.from(tiers).sort().map(tier => ({ + value: tier, + label: getTierLabel(tier) + })); +} + +async function executeWarmup( + suite: string, + scenario: string, + scenarioCfg: any, + quiet: boolean = false +): Promise<{ success: boolean; error?: string }> { + // Check if warmup is enabled + if (!scenarioCfg.warmup || !scenarioCfg.warmup.enabled) { + return { success: true }; // No warmup configured, continue normally + } + + const warmupCfg = scenarioCfg.warmup; + const scenarioDir = getScenarioDir(suite, scenario); + const workingDir = join(scenarioDir, warmupCfg.working_dir || './repo-fixture'); + + if (!quiet) { + console.log(chalk.blue('🔥 Executing warmup phase...')); + console.log(chalk.gray(` Working directory: ${workingDir}`)); + } + + // Create working directory if it doesn't exist, or clean it if it does + if (existsSync(workingDir)) { + // Clean existing directory + try { + const files = readdirSync(workingDir); + for (const file of files) { + const filePath = join(workingDir, file); + const stat = statSync(filePath); + if (stat.isDirectory()) { + rmSync(filePath, { recursive: true, force: true }); + } else { + unlinkSync(filePath); + } + } + } catch (err) { + return { success: false, error: `Failed to clean warmup directory: ${err}` }; + } + } else { + mkdirSync(workingDir, { recursive: true }); + } + + // Execute based on warmup type + if (warmupCfg.type === 'agent') { + // Agent-driven warmup + const agentCfg = warmupCfg.agent; + const provider = agentCfg.provider || 'openrouter'; + const model = agentCfg.model; + const prompt = agentCfg.prompt; + + if (!prompt) { + return { success: false, error: 'Warmup agent prompt is required' }; + } + + if (!quiet) { + console.log(chalk.gray(` Provider: ${provider}`)); + console.log(chalk.gray(` Model: ${model || 'default'}`)); + } + + try { + // Create agent adapter + const agentAdapter = createAgentAdapter(provider, model); + + // Build the request + const messages: Array<{ role: 'system' | 'user'; content: string }> = [ + { + role: 'user' as const, + content: prompt + } + ]; + + const request: AgentRequest = { + messages, + workspaceDir: workingDir, + }; + + // Add tools if agent supports them + if (provider === 'anthropic' || provider === 'openrouter') { + const { createWorkspaceToolHandlers, getAllWorkspaceTools } = await import('./runtime/workspace-tools.ts'); + const workspaceHandlers = createWorkspaceToolHandlers(workingDir); + const tools = getAllWorkspaceTools(); + + request.tools = tools; + request.toolHandlers = workspaceHandlers; + } + + // Execute the agent + if (!quiet) console.log(chalk.blue(' 🤖 Running warmup agent...')); + + const response = await agentAdapter.execute(request); + + if (!quiet) { + console.log(chalk.green(' ✓ Warmup completed successfully')); + if (response.content) { + console.log(chalk.gray(` Agent response: ${response.content.substring(0, 100)}...`)); + } + } + + return { success: true }; + } catch (err: any) { + return { success: false, error: `Warmup agent execution failed: ${err.message || err}` }; + } + } else if (warmupCfg.type === 'scripted') { + // Scripted warmup + const commands = warmupCfg.commands || []; + + if (!quiet) console.log(chalk.blue(` Running ${commands.length} warmup commands...`)); + + for (const cmdCfg of commands) { + const cmd = cmdCfg.cmd; + const description = cmdCfg.description || cmd; + + if (!quiet) console.log(chalk.gray(` → ${description}`)); + + try { + const { execSync } = await import('child_process'); + execSync(cmd, { + cwd: workingDir, + stdio: quiet ? 'ignore' : 'inherit', + encoding: 'utf8' + }); + } catch (err: any) { + return { success: false, error: `Warmup command failed: ${cmd}\n${err.message || err}` }; + } + } + + if (!quiet) console.log(chalk.green(' ✓ Warmup commands completed')); + return { success: true }; + } else { + return { success: false, error: `Unknown warmup type: ${warmupCfg.type}` }; + } +} + +function prepareWorkspaceFromFixture(suite: string, scenario: string): { workspaceDir: string; fixtureDir: string } | undefined { + const scenarioDir = getScenarioDir(suite, scenario); + const candidates = ['repo', 'repo-fixture']; + let fixtureDir: string | null = null; + for (const name of candidates) { + const dir = join(scenarioDir, name); + if (existsSync(dir)) { fixtureDir = dir; break; } + } + if (!fixtureDir) { + log.warning(`No raw fixture directory found (looked for ${candidates.join(', ')}) in ${scenarioDir}`); + return; + } + const root = findRepoRoot(); + const workspacesDir = join(root, 'results', 'workspaces'); + mkdirSync(workspacesDir, { recursive: true }); + const workspaceDir = mkdtempSync(join(workspacesDir, `${suite}-${scenario}-`)); + try { + // Copy fixture directory while excluding README.md files + cpSync(fixtureDir, workspaceDir, { + recursive: true, + filter: (src: string) => { + // Exclude README.md files from being copied (check filename and path) + const fileName = src.split(/[/\\]/).pop() || ''; + return fileName !== 'README.md'; + } + }); + return { workspaceDir, fixtureDir }; + } catch (err) { + console.error('Failed to copy fixture directory:', err); + return; + } +} + +function loadPrompt(suite: string, scenario: string, tier: string): string | null { + const root = findRepoRoot(); + const promptDir = join(root, 'suites', suite, 'prompts', scenario); + + if (!existsSync(promptDir)) { + log.warning(`Prompt directory not found: ${promptDir}`); + return null; + } + + // Look for files that start with the tier (e.g., L1-basic.md, L1.md) + try { + const files = readdirSync(promptDir); + const promptFile = files.find((file: string) => file.startsWith(`${tier}-`) || file === `${tier}.md`); + + if (!promptFile) { + log.warning(`No prompt file found for tier ${tier} in ${promptDir}`); + return null; + } + + const promptPath = join(promptDir, promptFile); + return readFileSync(promptPath, 'utf8'); + } catch (err) { + log.error('Failed to load prompt file:'); + console.error(chalk.dim(err instanceof Error ? err.message : String(err))); + return null; + } +} + +/** + * Resolve specialist name to template file path + * + * Converts a specialist name (e.g., @zephyr-cloud/shadcn-specialist) to the + * corresponding template file path in starting_from_outcome/ + * + * @param specialistName - Specialist name (with or without namespace) + * @returns Absolute path to template file + * @throws Error if template file doesn't exist + */ +function resolveSpecialistTemplatePath(specialistName: string): string { + // Strip namespace prefix if present (e.g., @zephyr-cloud/) + const templateName = specialistName.replace(/^@[^/]+\//, ''); + + // Construct template path relative to workspace root + const templatePath = `starting_from_outcome/${templateName}.json5`; + const absolutePath = resolve(workspaceRoot, templatePath); + + // Verify template exists + if (!existsSync(absolutePath)) { + throw new Error( + `Specialist template not found: ${templatePath}\n` + + ` Specialist: ${specialistName}\n` + + ` Expected path: ${absolutePath}\n` + + ` Tip: Ensure the template file exists in starting_from_outcome/` + ); + } + + // Return absolute path to avoid cwd-related issues when harness is spawned + // from different directories (similar to mint:snapshot fix) + return absolutePath; +} + +function createAgentAdapter(agentName: string, model?: string, specialistName?: string): AgentAdapter { + // Create base adapter + let baseAdapter: AgentAdapter; + + switch (agentName) { + case 'openrouter': + // Pass model directly to constructor instead of using environment variable + baseAdapter = new OpenRouterAdapter(process.env.OPENROUTER_API_KEY, model); + break; + case 'anthropic': + if (model) { + process.env.CLAUDE_MODEL = model; + } + baseAdapter = new AnthropicAdapter(); + break; + case 'claude-code': + baseAdapter = new ClaudeCodeAdapter(model); + break; + case 'echo': + default: + baseAdapter = new EchoAgent(); + break; + } + + // Wrap with SpecialistAdapter if specialist name provided + if (specialistName) { + const templatePath = resolveSpecialistTemplatePath(specialistName); + console.log(chalk.blue(` ℹ️ Using specialist: ${chalk.cyan(specialistName)}`)); + console.log(chalk.gray(` Template: ${templatePath}`)); + return new SpecialistAdapter(baseAdapter, templatePath); + } + + return baseAdapter; +} + + + +// ============================================================================ +// SECTION 3: COMMAND-LINE ARGUMENT PARSING +// ============================================================================ + +function parseArgs(argv: string[]) { + // Skip node, script path - arguments are directly suite and scenario + const args = argv.slice(2); + + // Check for history command + if (args[0] === '--history') { + const limit = args[1] ? parseInt(args[1], 10) : 10; + return { cmd: 'history', limit: isNaN(limit) ? 10 : limit } as const; + } + + // Check for evaluators command + if (args[0] === '--evaluators') { + return { cmd: 'evaluators' } as const; + } + + // Check for clear-db command + if (args[0] === '--clear-db') { + return { cmd: 'clear-db' } as const; + } + + // Check for stats command + if (args[0] === '--stats') { + const level = args[1]; // 'run', 'suite', or 'scenario' + const identifier = args.slice(2); // suite name, scenario name, or run ID + return { cmd: 'stats', level, identifier } as const; + } + + // Check for batches command + if (args[0] === '--batches') { + const limit = args[1] ? parseInt(args[1], 10) : 20; + return { cmd: 'batches', limit: isNaN(limit) ? 20 : limit } as const; + } + + // Check for batch-details command + if (args[0] === '--batch-details') { + const batchId = args[1]; + return { cmd: 'batch-details', batchId } as const; + } + + // Check for compare-batches command + if (args[0] === '--compare-batches') { + const batchIds = args.slice(1); + return { cmd: 'compare-batches', batchIds } as const; + } + + // Check for new-suite command + if (args[0] === '--new-suite') { + const name = args[1]; + return { cmd: 'new-suite', name } as const; + } + + // Check for new-scenario command + if (args[0] === '--new-scenario') { + const suite = args[1]; + const name = args[2]; + return { cmd: 'new-scenario', suite, name } as const; + } + + const cmd = 'bench'; + const suite = args[0]; + const scenario = args[1]; + const rest = args.slice(2); + + const tierIndex = rest.indexOf('--tier'); + const tier = tierIndex !== -1 ? rest[tierIndex + 1] : 'L0'; + + const agentIndex = rest.indexOf('--agent'); + const agent = agentIndex !== -1 ? rest[agentIndex + 1] : 'echo'; + + const modelIndex = rest.indexOf('--model'); + const model = modelIndex !== -1 ? rest[modelIndex + 1] : undefined; + + const batchIdIndex = rest.indexOf('--batch-id'); + const batchId = batchIdIndex !== -1 ? rest[batchIdIndex + 1] : undefined; + + const specialistIndex = rest.indexOf('--specialist'); + const specialist = specialistIndex !== -1 ? rest[specialistIndex + 1] : undefined; + + return { cmd, suite, scenario, tier, agent, model, batchId, specialist } as const; +} + +function showHelp() { + console.log(chalk.cyan(createTitle())); + intro(chalk.bgBlue(' CLI Help ')); + + console.log('\n' + chalk.bold('Usage:')); + console.log(` ${chalk.cyan('pnpm bench')} [options]`); + + console.log('\n' + chalk.bold('Commands:')); + console.log(` ${chalk.cyan('--history')} [limit] Show recent runs`); + console.log(` ${chalk.cyan('--evaluators')} Show evaluator stats`); + console.log(` ${chalk.cyan('--stats')} suite Suite statistics`); + console.log(` ${chalk.cyan('--stats')} scenario Scenario statistics`); + console.log(` ${chalk.cyan('--stats')} run Run details`); + console.log(` ${chalk.cyan('--batches')} [limit] List recent batches`); + console.log(` ${chalk.cyan('--batch-details')} Detailed batch analytics`); + console.log(` ${chalk.cyan('--compare-batches')} Compare multiple batches`); + console.log(` ${chalk.cyan('--new-suite')} [name] Create a new benchmark suite`); + console.log(` ${chalk.cyan('--new-scenario')} [suite] [name] Create a new scenario in a suite`); + console.log(` ${chalk.cyan('--clear-db')} Clear database`); + + console.log('\n' + chalk.bold('Options:')); + console.log(` ${chalk.cyan('--tier')} Difficulty tier (varies by scenario)`); + console.log(` ${chalk.cyan('--agent')} Agent to use`); + console.log(` ${chalk.cyan('--model')} Model name`); + console.log(` ${chalk.cyan('--specialist')} Specialist name (e.g., @zephyr-cloud/shadcn-specialist)`); + + console.log('\n' + chalk.bold('Model Selection:')); + console.log(` ${chalk.cyan('OpenRouter Models:')} Search-based selection from 200+ models`); + console.log(` ${chalk.gray('Search by:')} model name, provider, or description`); + console.log(` ${chalk.gray('Example searches:')} \"gpt-4o\", \"llama-3\", \"gemma free\", \"claude sonnet\"`); + + console.log('\n' + chalk.bold('Web Dashboard:')); + console.log(` ${chalk.blue('http://localhost:3000')} ${chalk.gray('- Interactive charts and analytics')}`); + console.log(` ${chalk.gray('Run:')} ${chalk.yellow('pnpm dev')} ${chalk.gray('to start the web server')}`); + + outro(chalk.gray('Run any command to get started')); +} + +// ============================================================================ +// SECTION 4: INTERACTIVE MENU SYSTEM +// ============================================================================ + +async function showInteractiveMenu() { + // Check environment variables for interactive mode + await validateEnvironment(); + + console.log(chalk.cyan(createTitle())); + intro(chalk.bgBlue(' Interactive Mode ')); + + // Show web dashboard info + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')} ${chalk.blue('http://localhost:3000')} ${chalk.gray('- Interactive charts and analytics')}`); + console.log(` ${chalk.gray('Run:')} ${chalk.yellow('pnpm dev')} ${chalk.gray('to start the web server')}\n`); + + while (true) { + const action = await select({ + message: 'What would you like to do?', + options: [ + { value: 'benchmark', label: 'Run Benchmarks' }, + { value: 'history', label: 'History' }, + { value: 'statistics', label: 'Statistics' }, + { value: 'new-suite', label: 'Create New Suite' }, + { value: 'new-scenario', label: 'Create New Scenario' }, + { value: 'clear', label: 'Clear Database' }, + { value: 'help', label: 'Show Help' }, + { value: 'exit', label: 'Exit' } + ] + }); + + switch (action) { + case 'benchmark': + await runInteractiveBenchmark(); + break; + case 'history': + await runInteractiveHistoryMenu(); + break; + case 'statistics': + await runInteractiveStatisticsMenu(); + break; + case 'new-suite': + await createNewSuite(); + break; + case 'new-scenario': + await createNewScenario(); + break; + case 'clear': + await runInteractiveClear(); + break; + case 'help': + showHelp(); + break; + case 'exit': + outro(chalk.green('Goodbye!')); + process.exit(0); + break; + } + + // Add a small pause before showing the menu again + console.log('\n'); + } +} + +// ============================================================================ +// SECTION 5: INTERACTIVE COMMANDS - BENCHMARKS +// ============================================================================ + +async function executeMultipleBenchmarks( + suites: string[], + scenarios: string[], + tiers: string[], + agents: string[], + models: (string | undefined)[] +) { + // Initialize batch tracking + const logger = BenchmarkLogger.getInstance(); + const batchId = logger.startBatch(); + + // Calculate total combinations + const combinations: Array<{ + suite: string; + scenario: string; + tier: string; + agent: string; + model?: string; + }> = []; + + for (const suite of suites) { + for (const scenario of scenarios) { + const availableTiers = getAvailableTiers(suite, scenario); + const availableTierValues = availableTiers.map(t => t.value); + const validTiers = tiers.filter(tier => availableTierValues.includes(tier)); + + if (validTiers.length === 0) { + console.log(chalk.yellow(`⚠ Skipping ${suite}/${scenario}: no valid tiers (available: ${availableTierValues.join(', ')})`)); + continue; + } + + // Log if some tiers are being skipped + const skippedTiers = tiers.filter(tier => !availableTierValues.includes(tier)); + if (skippedTiers.length > 0) { + console.log(chalk.gray(` Skipping tiers for ${suite}/${scenario}: ${skippedTiers.join(', ')}`)); + } + + for (const tier of validTiers) { + for (const agent of agents) { + // Handle model selection per agent + const agentModels = (agent === 'anthropic' || agent === 'claude-code' || agent === 'openrouter') + ? models.filter(m => m !== undefined) + : [undefined]; + + for (const model of agentModels) { + combinations.push({ suite, scenario, tier, agent, model }); + } + } + } + } + } + + // Automatically determine parallel execution based on number of benchmarks + const useParallel = combinations.length >= 3; // Enable parallel for 3+ benchmarks + let concurrency = 3; + + if (useParallel) { + // Smart concurrency based on number of benchmarks + if (combinations.length <= 5) { + concurrency = 2; // Conservative for small batches + } else if (combinations.length <= 15) { + concurrency = 3; // Balanced for medium batches + } else if (combinations.length <= 30) { + concurrency = 5; // Aggressive for large batches + } else { + concurrency = 8; // Maximum for very large batches + } + } + + // Show summary + console.log(chalk.bold.underline(`\nRunning ${combinations.length} benchmark(s):`)); + if (useParallel) { + console.log(chalk.gray(`Parallel execution with concurrency: ${concurrency}`)); + } + + // Track batch statistics + let successfulRuns = 0; + let totalScore = 0; + let totalWeightedScore = 0; + const startTime = Date.now(); + + if (useParallel) { + // Parallel execution + await executeWithConcurrency( + combinations, + concurrency, + async (combo, i) => { + const { suite, scenario, tier, agent, model } = combo; + console.log(`${chalk.bold.cyan(`[${i + 1}/${combinations.length}]`)} ${suite}/${scenario} ${chalk.gray(`(${tier}) ${agent}${model ? ` [${model}]` : ''}`)}`); + await executeBenchmark(suite, scenario, tier, agent, model, batchId, true); // quiet mode + } + ); + } else { + // Sequential execution + for (let i = 0; i < combinations.length; i++) { + const { suite, scenario, tier, agent, model } = combinations[i]; + + console.log(`${chalk.bold.cyan(`[${i + 1}/${combinations.length}]`)} ${suite}/${scenario} ${chalk.gray(`(${tier}) ${agent}${model ? ` [${model}]` : ''}`)}`); + + await executeBenchmark(suite, scenario, tier, agent, model, batchId, true); // quiet mode + } + } + + // Complete batch tracking + const endTime = Date.now(); + const duration = endTime - startTime; + + // Calculate batch statistics + const batchStats = logger.getBatchDetails(batchId); + if (batchStats) { + // Calculate successful runs directly from individual runs in the batch + // This ensures we use the new is_successful field + successfulRuns = logger.getBatchSuccessfulRunsCount(batchId); + + // Calculate average scores directly from individual runs in the batch + const scoreStats = logger.getBatchScoreStats(batchId); + totalScore = scoreStats.avgScore; + totalWeightedScore = scoreStats.avgWeightedScore; + } + + logger.completeBatch(batchId, { + totalRuns: combinations.length, + successfulRuns, + avgScore: totalScore, + avgWeightedScore: totalWeightedScore, + metadata: { + suites, + scenarios, + tiers, + agents, + models: models.filter(m => m !== undefined), + duration + } + }); + + // Note: Database is now created directly in public/ directory + + // Get comprehensive batch analytics + const analytics = logger.getBatchAnalytics(batchId); + + // Show batch summary header + console.log('\n' + chalk.bold.underline('Batch Summary')); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Batch ID:')} ${chalk.dim(batchId.substring(0, 8))}...`); + + // Show model if all runs used the same model + const uniqueModels = [...new Set(combinations.map(c => c.model).filter(m => m))]; + if (uniqueModels.length === 1) { + console.log(`│ ${chalk.bold('Model:')} ${chalk.cyan(uniqueModels[0])}`); + } + + console.log(`│ ${chalk.bold('Total Runs:')} ${combinations.length}`); + console.log(`│ ${chalk.bold('Completed:')} ${successfulRuns} (${combinations.length > 0 ? ((successfulRuns / combinations.length) * 100).toFixed(1) : 0}%)`); + + // Show failed runs breakdown + const failedRuns = combinations.length - successfulRuns; + if (failedRuns > 0) { + console.log(`│ ${chalk.bold('Failed:')} ${chalk.red(failedRuns)} (${combinations.length > 0 ? ((failedRuns / combinations.length) * 100).toFixed(1) : 0}%)`); + + // Get failure breakdown + const failureBreakdown = logger.getFailureBreakdown(batchId); + if (failureBreakdown.length > 0) { + const failureReasons = failureBreakdown.map(f => `${f.errorType}: ${f.count}`).join(', '); + console.log(`│ ${chalk.bold('Failure Reasons:')} ${chalk.red(failureReasons)}`); + } + } + + console.log(`│ ${chalk.bold('Avg Score:')} ${combinations.length > 0 ? (totalWeightedScore / combinations.length).toFixed(4) : 0} / 10.0`); + console.log(`│ ${chalk.bold('Duration:')} ${(duration / 1000).toFixed(2)}s`); + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + // Show suite breakdown if analytics available + if (analytics && analytics.suiteBreakdown.length > 0) { + console.log(`\n${chalk.bold.underline('Suite Breakdown')}`); + analytics.suiteBreakdown.forEach(suite => { + const successRate = suite.runs > 0 ? ((suite.successfulRuns / suite.runs) * 100).toFixed(0) : 0; + console.log(` ${chalk.cyan(suite.suite)}/${suite.scenario}: ${suite.avgWeightedScore.toFixed(2)}/10 ${chalk.gray(`(${successRate}% success, ${suite.runs} runs)`)}`); + }); + } + + // Show agent performance + if (analytics && analytics.agentPerformance.length > 0) { + console.log(`\n${chalk.bold.underline('Agent Performance')}`); + analytics.agentPerformance.forEach((agent, index) => { + const rank = index + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + const modelStr = agent.model && agent.model !== 'default' ? ` [${agent.model}]` : ''; + const scoreColor = agent.avgWeightedScore >= 9 ? 'green' : agent.avgWeightedScore >= 7 ? 'yellow' : 'red'; + console.log(` ${rankDisplay} ${chalk.cyan(agent.agent)}${modelStr}: ${chalk[scoreColor](agent.avgWeightedScore.toFixed(2))}/10 ${chalk.gray(`(${agent.successfulRuns}/${agent.runs} runs)`)}`); + }); + } + + // Show failed runs if any + if (analytics && analytics.failedRuns.length > 0) { + console.log(`\n${chalk.bold.underline(chalk.red('Failed Runs'))}`); + analytics.failedRuns.forEach(run => { + console.log(` ${chalk.red('✗')} ${run.suite}/${run.scenario} (${run.tier}) ${run.agent} - ${run.error || 'Unknown error'}`); + }); + } + + // Show completion summary + console.log('\n' + chalk.green('✓') + chalk.bold(` Completed all ${combinations.length} benchmark(s)!`)); + + // Note: Database is now created directly in public/ directory +} + +// ============================================================================ +// PARALLEL EXECUTION HELPER +// ============================================================================ + +async function executeWithConcurrency( + items: T[], + concurrency: number, + executor: (item: T, index: number) => Promise +): Promise { + const results: Promise[] = []; + let currentIndex = 0; + + async function runNext(): Promise { + const index = currentIndex++; + if (index >= items.length) return; + + await executor(items[index], index); + await runNext(); + } + + // Start initial batch of concurrent executions + for (let i = 0; i < Math.min(concurrency, items.length); i++) { + results.push(runNext()); + } + + await Promise.all(results); +} + +async function runInteractiveHistoryMenu() { + const historyType = await select({ + message: 'What history would you like to view?', + options: [ + { value: 'run-history', label: 'Run History' }, + { value: 'batch-history', label: 'Batch History' } + ] + }) as string; + + switch (historyType) { + case 'run-history': + await runInteractiveHistory(); + break; + case 'batch-history': + await runInteractiveBatchHistory(); + break; + } +} + +async function runInteractiveStatisticsMenu() { + const statsType = await select({ + message: 'What statistics would you like to view?', + options: [ + { value: 'suite-stats', label: 'Suite Statistics' }, + { value: 'scenario-stats', label: 'Scenario Statistics' }, + { value: 'run-stats', label: 'Run Details' }, + { value: 'batch-stats', label: 'Batch Statistics' }, + { value: 'evaluators', label: 'Evaluator Performance' } + ] + }) as string; + + switch (statsType) { + case 'suite-stats': + await runInteractiveSuiteStats(); + break; + case 'scenario-stats': + await runInteractiveScenarioStats(); + break; + case 'run-stats': + await runInteractiveRunStats(); + break; + case 'batch-stats': + await runInteractiveBatchStats(); + break; + case 'evaluators': + await runInteractiveEvaluators(); + break; + } +} + +async function runInteractiveBenchmark() { + console.log(chalk.bold.underline('Demo: Benchmarking AI Agents:')); + + // Get available suites and scenarios + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + + if (!existsSync(suitesDir)) { + log.error(chalk.red('No suites directory found')); + return; + } + + const suites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + + if (suites.length === 0) { + log.error(chalk.red('No suites found')); + return; + } + + // Select suites (multiselect) + const selectedSuites = await multiselect({ + message: 'Choose suites:', + options: [ + { value: '__ALL__', label: 'All suites' }, + ...suites.map(suite => ({ value: suite, label: suite })) + ], + required: true + }); + + if (isCancel(selectedSuites)) { + cancel('Operation cancelled.'); + return; + } + + // Expand \"All\" selection + const suitesToUse = selectedSuites.includes('__ALL__') ? suites : selectedSuites; + + // Get scenarios for all selected suites + const allScenarios: Array<{ value: string; label: string; suite: string }> = []; + for (const suite of suitesToUse) { + const scenariosDir = join(suitesDir, suite, 'scenarios'); + if (existsSync(scenariosDir)) { + const scenarios = readdirSync(scenariosDir).filter(dir => + existsSync(join(scenariosDir, dir, 'scenario.yaml')) + ); + scenarios.forEach(scenario => { + allScenarios.push({ + value: scenario, + label: `${scenario} (${suite})`, + suite + }); + }); + } + } + + if (allScenarios.length === 0) { + log.error(chalk.red('No scenarios found for selected suites')); + return; + } + + // Select scenarios (multiselect) + const selectedScenarios = await multiselect({ + message: 'Choose scenarios:', + options: [ + { value: '__ALL__', label: 'All scenarios' }, + ...allScenarios + ], + required: true + }); + + if (isCancel(selectedScenarios)) { + cancel('Operation cancelled.'); + return; + } + + // Expand \"All\" selection and filter by suite + const scenariosToUse = selectedScenarios.includes('__ALL__') + ? allScenarios.map(s => s.value) + : selectedScenarios; + + // Collect available tiers from all selected scenarios + console.log('🔍 Scanning available tiers for selected scenarios...'); + const availableTiersSet = new Set(); + const scenarioTierMap = new Map(); + + for (const suite of suitesToUse) { + for (const scenario of scenariosToUse) { + const tiers = getAvailableTiers(suite, scenario); + const tierValues = tiers.map(t => t.value); + scenarioTierMap.set(`${suite}/${scenario}`, tierValues); + tiers.forEach(tier => availableTiersSet.add(tier.value)); + } + } + + // Show what tiers are available for each scenario + scenarioTierMap.forEach((tiers, scenario) => { + console.log(` ${scenario}: ${tiers.join(', ')}`); + }); + + const tierOptions = [ + { value: '__ALL__', label: 'All available tiers' }, + ...Array.from(availableTiersSet).sort().map(tier => ({ + value: tier, + label: getTierLabel(tier) + })) + ]; + + console.log(`✅ Found ${availableTiersSet.size} unique tiers across all scenarios`); + + // Select tiers (multiselect) + const selectedTiers = await multiselect({ + message: 'Choose difficulty tiers:', + options: tierOptions, + required: true + }); + + if (isCancel(selectedTiers)) { + cancel('Operation cancelled.'); + return; + } + + // Expand \"All\" selection + const tiersToUse = selectedTiers.includes('__ALL__') + ? Array.from(availableTiersSet).sort() + : selectedTiers; + + // Select agents (multiselect) - dynamically loaded + console.log('Loading available agents...'); + const agentOptions = await getAvailableAgents(); + console.log(`✅ Loaded ${agentOptions.length} agent options`); + + const selectedAgents = await multiselect({ + message: 'Choose agents:', + options: agentOptions, + required: true + }); + + if (isCancel(selectedAgents)) { + cancel('Operation cancelled.'); + return; + } + + // Expand \"All\" selection + const agentsToUse = selectedAgents.includes('__ALL__') + ? ['echo', 'openrouter', 'anthropic', 'claude-code'] + : selectedAgents; + + console.log(`🎯 Selected agents: ${agentsToUse.join(', ')}`); + + // Ask for models if needed + let modelsToUse: (string | undefined)[] = [undefined]; + const needsOpenRouterModels = agentsToUse.some(agent => agent === 'openrouter'); + const needsAnthropicModels = agentsToUse.some(agent => agent === 'anthropic'); + const needsClaudeCodeModels = agentsToUse.some(agent => agent === 'claude-code'); + + if (needsOpenRouterModels) { + console.log('🔍 Loading OpenRouter models with tool support...'); + + const openrouterAPI = new OpenRouterAPI(process.env.OPENROUTER_API_KEY || ''); + const toolModels = await openrouterAPI.getModelsWithToolSupport(); + + console.log(`✅ Found ${toolModels.length} models with tool support`); + + // Quick shortcuts for common models + const QUICK_MODELS = { + 'free': 'Filter by free models only', + 'gpt': 'OpenAI GPT models', + 'claude': 'Anthropic Claude models', + 'llama': 'Meta Llama models', + 'gemma': 'Google Gemma models', + 'mistral': 'Mistral AI models' + }; + + // Show shortcuts + console.log(`\n${chalk.gray('Quick searches:')} ${Object.keys(QUICK_MODELS).join(', ')}`); + + // Text-based search instead of dropdown + const modelSearch = await text({ + message: 'Search for OpenRouter model (type to filter):', + placeholder: 'e.g., gpt-4o, llama, gemma, claude', + validate: (value) => { + if (!value || value.length < 2) { + return 'Please enter at least 2 characters to search'; + } + } + }); + + if (isCancel(modelSearch)) { + cancel('Operation cancelled.'); + return; + } + + // Search and display results + const searchResults = openrouterAPI.searchModels(toolModels, modelSearch); + + if (searchResults.length === 0) { + log.warning(`No models found matching \"${modelSearch}\"`); + log.info('Try searching for: gpt-4o, llama, gemma, claude, mistral'); + return; + } + + // Show search results with pricing info + console.log(`\n📋 Found ${searchResults.length} matching models:\n`); + + const selectedModel = await select({ + message: 'Choose a model:', + options: searchResults.map(model => { + const promptCost = parseFloat(model.pricing.prompt); + const isFree = promptCost === 0; + const costLabel = isFree ? '(FREE)' : `($${promptCost}/1K tokens)`; + + return { + value: model.id, + label: `${model.name} ${costLabel}`, + hint: model.description?.substring(0, 80) + }; + }) + }); + + if (isCancel(selectedModel)) { + cancel('Operation cancelled.'); + return; + } + + modelsToUse = [selectedModel]; + console.log(`🎯 Selected model: ${selectedModel}`); + + // Display selected model details + const selectedModelInfo = toolModels.find(m => m.id === selectedModel); + + if (selectedModelInfo) { + console.log(`\n${chalk.bold.cyan('Model Details:')}`); + console.log(` ${chalk.gray('Name:')} ${selectedModelInfo.name}`); + console.log(` ${chalk.gray('ID:')} ${selectedModelInfo.id}`); + console.log(` ${chalk.gray('Context:')} ${selectedModelInfo.context_length.toLocaleString()} tokens`); + console.log(` ${chalk.gray('Cost:')} $${selectedModelInfo.pricing.prompt}/1K prompt, $${selectedModelInfo.pricing.completion}/1K completion`); + + const isFree = parseFloat(selectedModelInfo.pricing.prompt) === 0; + if (isFree) { + console.log(` ${chalk.green('✓ FREE MODEL')}`); + } + } + } else if (needsAnthropicModels) { + console.log('🧠 Loading available Anthropic models...'); + // Anthropic has a fixed set of models + const anthropicModels = [ + { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet (Current)' }, + { value: 'claude-3-5-haiku-20241022', label: 'Claude 3.5 Haiku (Current)' }, + { value: 'claude-3-7-sonnet-20250219', label: 'Claude 3.7 Sonnet (Active)' }, + { value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4 (Active)' }, + { value: 'claude-opus-4-20250514', label: 'Claude Opus 4 (Active)' }, + { value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1 (Active)' }, + { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5 (Active)' }, + { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5 (Active)' } + ]; + + const selectedModels = await multiselect({ + message: 'Choose Anthropic models:', + options: anthropicModels, + required: true + }); + + if (isCancel(selectedModels)) { + cancel('Operation cancelled.'); + return; + } + + modelsToUse = selectedModels; + console.log(`🎯 Selected Anthropic models: ${modelsToUse.join(', ')}`); + } else if (needsClaudeCodeModels) { + console.log('🧠 Loading available Claude Code models...'); + // Claude Code has a fixed set of models + const claudeCodeModels = [ + { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet (Current)' }, + { value: 'claude-3-5-haiku-20241022', label: 'Claude 3.5 Haiku (Current)' }, + { value: 'claude-3-7-sonnet-20250219', label: 'Claude 3.7 Sonnet (Active)' }, + { value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4 (Active)' }, + { value: 'claude-opus-4-20250514', label: 'Claude Opus 4 (Active)' }, + { value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1 (Active)' }, + { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5 (Active)' }, + { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5 (Active)' } + ]; + + const selectedModels = await multiselect({ + message: 'Choose Claude Code models:', + options: claudeCodeModels, + required: true + }); + + if (isCancel(selectedModels)) { + cancel('Operation cancelled.'); + return; + } + + modelsToUse = selectedModels; + console.log(`🎯 Selected Claude Code models: ${modelsToUse.join(', ')}`); + } + + + // Calculate total combinations for automatic parallel decision + const totalCombinations = suitesToUse.length * scenariosToUse.length * tiersToUse.length * agentsToUse.length * modelsToUse.length; + + // Automatically determine parallel execution based on number of benchmarks + const useParallel = totalCombinations >= 3; // Enable parallel for 3+ benchmarks + let concurrency = 3; + + if (useParallel) { + // Smart concurrency based on number of benchmarks + if (totalCombinations <= 5) { + concurrency = 2; // Conservative for small batches + } else if (totalCombinations <= 15) { + concurrency = 3; // Balanced for medium batches + } else if (totalCombinations <= 30) { + concurrency = 5; // Aggressive for large batches + } else { + concurrency = 8; // Maximum for very large batches + } + } + + // Show summary of what will be executed + console.log(`\n${chalk.green('►')} Will run ${chalk.bold(totalCombinations.toString())} benchmark combination(s)`); + console.log(` ${chalk.cyan('Suites:')} ${suitesToUse.join(', ')}`); + console.log(` ${chalk.cyan('Scenarios:')} ${scenariosToUse.join(', ')}`); + console.log(` ${chalk.cyan('Tiers:')} ${tiersToUse.join(', ')}`); + console.log(` ${chalk.cyan('Agents:')} ${agentsToUse.join(', ')}`); + if (needsOpenRouterModels || needsAnthropicModels || needsClaudeCodeModels) { + console.log(` ${chalk.cyan('Models:')} ${modelsToUse.join(', ')}`); + } + console.log(` ${chalk.cyan('Parallel execution:')} ${useParallel ? `Yes (concurrency: ${concurrency})` : 'No'}`); + + // Show title before execution + console.log(chalk.cyan(createTitle())); + + // Execute all benchmark combinations + await executeMultipleBenchmarks( + suitesToUse, + scenariosToUse, + tiersToUse, + agentsToUse, + modelsToUse + ); +} + +// ============================================================================ +// SECTION 6: INTERACTIVE COMMANDS - HISTORY & STATS +// ============================================================================ + +async function runInteractiveHistory() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); + } catch (err) { + // Continue without web server + } + + const logger = BenchmarkLogger.getInstance(); + + try { + const limit = await select({ + message: 'How many recent runs to show?', + options: [ + { value: 5, label: '5 runs' }, + { value: 10, label: '10 runs' }, + { value: 20, label: '20 runs' }, + { value: 50, label: '50 runs' } + ] + }) as number; + + // Execute history command + const runHistory = logger.getRunHistory(limit); + + if (runHistory.length === 0) { + log.warning('No benchmark runs found'); + outro(chalk.yellow('Run a benchmark first')); + return; + } + + intro(chalk.bgCyan(' Benchmark History ')); + + // Use common display function + runHistory.forEach((run, index) => displayRunInfo(run, index)); + + outro(chalk.green(`Showing ${runHistory.length} recent runs`)); + + } catch (error) { + log.error(chalk.red('Failed to fetch history:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +async function runInteractiveSuiteStats() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); + console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); + console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); + } catch (err) { + // Continue without web server + console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); + } + + const logger = BenchmarkLogger.getInstance(); + + try { + // Get available suites + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + const suites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + + if (suites.length === 0) { + log.error(chalk.red('No suites found')); + return; + } + + const selectedSuite = await select({ + message: 'Choose a suite:', + options: suites.map(suite => ({ value: suite, label: suite })) + }) as string; + + // Execute suite stats + const stats = logger.getSuiteStats(selectedSuite); + console.log(chalk.bold.bgBlue(` Suite: ${selectedSuite} `)); + console.log('\n' + chalk.underline('Overview')); + console.log(formatStats('Total Runs', stats.totalRuns)); + console.log(formatStats('Success Rate', `${stats.totalRuns > 0 ? ((stats.successfulRuns / stats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); + console.log(formatStats('Avg Score', stats.avgScore.toFixed(4), 'yellow')); + console.log(formatStats('Avg Weighted', stats.avgWeightedScore.toFixed(4), 'yellow')); + console.log(formatStats('Avg Duration', `${(stats.avgDuration / 1000).toFixed(2)}s`, 'blue')); + + if (stats.scenarioBreakdown.length > 0) { + console.log('\n' + chalk.underline('Scenario Breakdown')); + stats.scenarioBreakdown.forEach(scenario => { + console.log(` ${chalk.cyan('•')} ${chalk.bold(scenario.scenario)}: ${chalk.yellow(scenario.avgScore.toFixed(4))} ${chalk.gray(`(${scenario.runs} runs)`)}`); + }); + } + + } catch (error) { + log.error(chalk.red('Failed to fetch suite statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +async function runInteractiveScenarioStats() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); + console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); + console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); + } catch (err) { + // Continue without web server + console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); + } + + const logger = BenchmarkLogger.getInstance(); + + try { + // Get available suites and scenarios + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + const suites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + + const selectedSuite = await select({ + message: 'Choose a suite:', + options: suites.map(suite => ({ value: suite, label: suite })) + }) as string; + + const scenariosDir = join(suitesDir, selectedSuite, 'scenarios'); + const scenarios = readdirSync(scenariosDir).filter(dir => + existsSync(join(scenariosDir, dir, 'scenario.yaml')) + ); + + const selectedScenario = await select({ + message: 'Choose a scenario:', + options: scenarios.map(scenario => ({ value: scenario, label: scenario })) + }) as string; + + // Execute scenario stats + const stats = logger.getScenarioStats(selectedSuite, selectedScenario); + console.log(chalk.bold.bgMagenta(` ${selectedSuite}/${selectedScenario} `)); + + // Score range with visual bar + const scorePercent = (stats.avgWeightedScore / 10) * 100; + const barLength = 20; + const filled = Math.round((scorePercent / 100) * barLength); + const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(barLength - filled)); + console.log(`\n${bar} ${chalk.bold(stats.avgWeightedScore.toFixed(2))}/10`); + + console.log('\n' + chalk.underline('Overview')); + console.log(formatStats('Total Runs', stats.totalRuns)); + console.log(formatStats('Success Rate', `${stats.totalRuns > 0 ? ((stats.successfulRuns / stats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); + console.log(formatStats('Avg Score', stats.avgScore.toFixed(4), 'yellow')); + console.log(formatStats('Score Range', `${stats.minScore.toFixed(4)} - ${stats.maxScore.toFixed(4)}`, 'blue')); + + // Agent comparison table + if (stats.agentComparison.length > 0) { + console.log('\n' + chalk.underline('Agent Performance')); + stats.agentComparison.forEach((agent, i) => { + const rank = i + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + console.log(` ${rankDisplay} ${chalk.cyan(agent.agent.padEnd(15))} ${chalk.yellow(agent.avgScore.toFixed(4))} ${chalk.gray(`(${agent.runs} runs)`)}`); + }); + } + + } catch (error) { + log.error(chalk.red('Failed to fetch scenario statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +async function runInteractiveRunStats() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); + console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); + console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); + } catch (err) { + // Continue without web server + console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); + } + + const logger = BenchmarkLogger.getInstance(); + + try { + // Get recent runs to choose from + const runHistory = logger.getRunHistory(10); + + if (runHistory.length === 0) { + log.warning('No benchmark runs found'); + outro(chalk.yellow('Run a benchmark first')); + return; + } + + const selectedRun = await select({ + message: 'Choose a run to view details:', + options: runHistory.map((run, index) => ({ + value: run.runId, + label: `${index + 1}. ${run.suite}/${run.scenario} (${run.tier}) - ${run.agent} - ${run.weightedScore?.toFixed(4) || 'N/A'}` + })) + }) as string; + + // Execute run stats + const stats = logger.getDetailedRunStats(selectedRun); + console.log(chalk.bold.bgGreen(` Run Details `)); + console.log(`\n${chalk.gray('ID:')} ${chalk.dim(selectedRun.substring(0, 8))}...`); + console.log(formatStats('Suite', stats.run.suite)); + console.log(formatStats('Scenario', stats.run.scenario)); + console.log(formatStats('Tier', stats.run.tier)); + console.log(formatStats('Agent', stats.run.agent + (stats.run.model ? ` (${stats.run.model})` : ''))); + console.log(formatStats('Status', stats.run.status, stats.run.status === 'completed' ? 'green' : stats.run.status === 'failed' ? 'red' : 'yellow')); + console.log(formatStats('Started', new Date(stats.run.startedAt).toLocaleString())); + if (stats.run.completedAt) { + console.log(formatStats('Completed', new Date(stats.run.completedAt).toLocaleString())); + } + if (stats.run.totalScore !== null && stats.run.totalScore !== undefined) { + console.log(formatStats('Total Score', stats.run.totalScore.toFixed(4), 'green')); + } + if (stats.run.weightedScore !== null && stats.run.weightedScore !== undefined) { + console.log(formatStats('Weighted Score', stats.run.weightedScore.toFixed(4), 'green')); + } + + // Evaluation breakdown with progress bars + if (stats.evaluationBreakdown.length > 0) { + console.log('\n' + chalk.underline('Evaluations')); + stats.evaluationBreakdown.forEach(evaluation => { + const percent = evaluation.percentage; + const color = percent === 100 ? 'green' : percent >= 80 ? 'yellow' : 'red'; + const barLength = 15; + const filled = Math.round((percent / 100) * barLength); + const bar = chalk[color]('█'.repeat(filled)) + chalk.gray('░'.repeat(barLength - filled)); + + console.log(` ${evaluation.name.padEnd(30)} ${bar} ${chalk[color](percent.toFixed(1) + '%')}`); + }); + } + + if (stats.telemetrySummary) { + console.log('\n' + chalk.underline('Telemetry')); + console.log(formatStats('Tool Calls', stats.telemetrySummary.toolCalls, 'blue')); + console.log(formatStats('Tokens', stats.telemetrySummary.tokens, 'blue')); + console.log(formatStats('Cost', `$${stats.telemetrySummary.cost.toFixed(6)}`, 'blue')); + console.log(formatStats('Duration', `${(stats.telemetrySummary.duration / 1000).toFixed(2)}s`, 'blue')); + } + + } catch (error) { + log.error(chalk.red('Failed to fetch run statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +async function runInteractiveBatchStats() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); + } catch (err) { + // Continue without web server + } + + const logger = BenchmarkLogger.getInstance(); + + try { + // Get recent batches to choose from + const batchHistory = logger.getBatchHistory(10); + + if (batchHistory.length === 0) { + log.warning('No batch runs found'); + outro(chalk.yellow('Run some benchmarks first')); + return; + } + + const selectedBatch = await select({ + message: 'Choose a batch to view details:', + options: batchHistory.map((batch, index) => ({ + value: batch.batchId, + label: `${index + 1}. Batch ${batch.batchId.substring(0, 8)}... - ${batch.successfulRuns}/${batch.totalRuns} runs - ${batch.avgWeightedScore?.toFixed(4) || 'N/A'}` + })) + }) as string; + + // Execute batch stats + const batchStats = logger.getBatchDetails(selectedBatch); + if (!batchStats) { + log.error('Batch not found'); + return; + } + + console.log(chalk.bold.bgGreen(` Batch Details `)); + console.log(`\n${chalk.gray('ID:')} ${chalk.dim(selectedBatch.substring(0, 8))}...`); + console.log(formatStats('Total Runs', batchStats.totalRuns)); + console.log(formatStats('Successful', batchStats.successfulRuns, 'green')); + console.log(formatStats('Success Rate', `${batchStats.totalRuns > 0 ? ((batchStats.successfulRuns / batchStats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); + console.log(formatStats('Avg Score', batchStats.avgScore.toFixed(4), 'yellow')); + console.log(formatStats('Avg Weighted', batchStats.avgWeightedScore.toFixed(4), 'yellow')); + console.log(formatStats('Duration', `${(batchStats.duration / 1000).toFixed(2)}s`, 'blue')); + console.log(formatStats('Started', new Date(batchStats.createdAt).toLocaleString())); + if (batchStats.completedAt) { + console.log(formatStats('Completed', new Date(batchStats.completedAt).toLocaleString())); + } + + // Show runs in batch with ranking + if (batchStats.runs.length > 0) { + console.log('\n' + chalk.underline('Runs in Batch')); + const sortedRuns = batchStats.runs + .filter(run => run.weightedScore !== null && run.weightedScore !== undefined) + .sort((a, b) => (b.weightedScore || 0) - (a.weightedScore || 0)); + + sortedRuns.forEach((run, index) => { + const rank = index + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + const status = run.status === 'completed' + ? chalk.green('✓') + : run.status === 'failed' + ? chalk.red('✗') + : run.status === 'incomplete' + ? chalk.yellow('◐') + : chalk.blue('○'); + console.log(` ${rankDisplay} ${status} ${chalk.cyan(run.suite)}/${chalk.cyan(run.scenario)} ${chalk.gray(`(${run.tier})`)} ${chalk.cyan(run.agent)} ${chalk.yellow(run.weightedScore?.toFixed(4) || 'N/A')}`); + }); + } + + } catch (error) { + log.error(chalk.red('Failed to fetch batch statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + + +async function runInteractiveEvaluators() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); + } catch (err) { + // Continue without web server + } + + const logger = BenchmarkLogger.getInstance(); + + try { + intro(chalk.bgYellow(' Evaluator Performance ')); + + const stats = logger.getStats(); + + if (Object.keys(stats.evaluatorStats).length === 0) { + log.warning('No evaluator data available'); + outro(chalk.yellow('Run some benchmarks first')); + return; + } + + // Sort by performance + const sorted = Object.entries(stats.evaluatorStats).sort((a, b) => b[1].averageScore - a[1].averageScore); + + console.log('\n' + chalk.underline('Performance Ranking')); + sorted.forEach(([name, stat], index) => { + const rank = index + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + const percent = (stat.averageScore * 100).toFixed(1); + const color = stat.averageScore >= 0.9 ? 'green' : stat.averageScore >= 0.7 ? 'yellow' : 'red'; + + console.log(` ${rankDisplay} ${chalk.bold(name.padEnd(30))} ${chalk[color](percent + '%')} ${chalk.gray(`(${stat.count} runs)`)}`); + }); + + // Show best and worst performers + const best = sorted[0]; + const worst = sorted[sorted.length - 1]; + + console.log('\n' + chalk.underline('Performance Summary')); + console.log(` ${chalk.green('Best:')} ${chalk.bold(best[0])} ${chalk.green((best[1].averageScore * 100).toFixed(1) + '%')}`); + console.log(` ${chalk.red('Needs Work:')} ${chalk.bold(worst[0])} ${chalk.red((worst[1].averageScore * 100).toFixed(1) + '%')}`); + + // Show model performance using common function + const modelStats = logger.getModelPerformanceStats(); + displayModelPerformance(modelStats); + + outro(chalk.green('Analysis complete')); + + } catch (error) { + log.error(chalk.red('Failed to fetch evaluator data:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +async function runInteractiveBatchHistory() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); + console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); + console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); + } catch (err) { + // Continue without web server + console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); + } + + const logger = BenchmarkLogger.getInstance(); + + try { + const limit = await select({ + message: 'How many recent batches to show?', + options: [ + { value: 5, label: '5 batches' }, + { value: 10, label: '10 batches' }, + { value: 20, label: '20 batches' }, + { value: 50, label: '50 batches' } + ] + }) as number; + + // Execute batch history command + const batchHistory = logger.getBatchHistory(limit); + + if (batchHistory.length === 0) { + log.warning('No batch runs found'); + outro(chalk.yellow('Run some benchmarks first')); + return; + } + + intro(chalk.bgCyan(' Batch History ')); + + batchHistory.forEach((batch, index) => { + const status = batch.completedAt + ? chalk.green('✓') + : chalk.yellow('○'); + + console.log(`\n${chalk.bold(`${index + 1}.`)} ${status} ${chalk.cyan('Batch')} ${chalk.dim(batch.batchId.substring(0, 8))}...`); + console.log(` ${formatStats('Runs', `${batch.successfulRuns}/${batch.totalRuns}`, 'green')}`); + console.log(` ${formatStats('Avg Score', batch.avgWeightedScore?.toFixed(4) || 'N/A', 'yellow')}`); + console.log(` ${chalk.gray(new Date(batch.createdAt).toLocaleString())}`); + if (batch.completedAt) { + const duration = (batch.completedAt - batch.createdAt) / 1000; + console.log(` ${formatStats('Duration', `${duration.toFixed(2)}s`, 'blue')}`); + } + }); + + outro(chalk.green(`Showing ${batchHistory.length} recent batches`)); + + } catch (error) { + log.error(chalk.red('Failed to fetch batch history:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +// ============================================================================ +// SECTION 7: INTERACTIVE COMMANDS - UTILITIES +// ============================================================================ + +async function runInteractiveClear() { + const logger = BenchmarkLogger.getInstance(); + + try { + intro(chalk.bgRed(' Clear Database ')); + + // Get count before clearing + const stats = logger.getStats(); + log.warning(`Found ${chalk.bold(stats.totalRuns)} benchmark runs`); + + const shouldClear = await confirm({ + message: 'Are you sure you want to clear all data?', + initialValue: false + }); + + if (shouldClear) { + const s = spinner(); + s.start('Clearing database...'); + logger.clearDatabase(); + s.stop('Database cleared'); + outro(chalk.green('✓ All data removed')); + } else { + outro(chalk.yellow('Cancelled')); + } + + } catch (error) { + log.error(chalk.red('Failed to clear database:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +// ============================================================================ +// SECTION 8: SUCCESS CALCULATION LOGIC +// ============================================================================ + +interface CommandResult { + tool: 'shell'; + type: 'install' | 'test' | 'lint' | 'typecheck'; + raw: string; + exitCode: number; + stdout: string; + stderr: string; + durationMs: number; +} + +interface SuccessCalculationResult { + isSuccessful: boolean; + successMetric: number; +} + +function calculateSuccess( + commandLog: CommandResult[], + scores: Record, + scenario: any +): SuccessCalculationResult { + + // Validation score (commands passed / total commands) + const passedCommands = commandLog.filter(cmd => cmd.exitCode === 0).length; + const validationScore = commandLog.length > 0 + ? passedCommands / commandLog.length + : 0; + + // Critical commands (install, build, test must pass) + const criticalCommands = ['install', 'test']; // Note: 'build' is not in CommandKind, using 'test' as critical + const criticalPassed = criticalCommands.every(cmd => { + const result = commandLog.find(c => c.type === cmd); + return result && result.exitCode === 0; + }); + + // Evaluator average score + const evaluatorScore = Object.values(scores).length > 0 + ? Object.values(scores).reduce((sum, s) => sum + s, 0) / Object.values(scores).length + : 0; + + // LLM judge score (if available) + const llmJudgeScore = scores.llm_judge || 0; + + // Weighted metric (from scenario.yaml or default) + const weights = scenario.success_weights || { + validation: 0.4, + evaluators: 0.3, + llm_judge: 0.3 + }; + + const successMetric = ( + validationScore * weights.validation + + evaluatorScore * weights.evaluators + + llmJudgeScore * weights.llm_judge + ); + + // Success criteria: critical commands must pass AND success metric >= 0.7 + const isSuccessful = criticalPassed && successMetric >= 0.7; + + return { isSuccessful, successMetric }; +} + +// ============================================================================ +// SECTION 9: MAIN BENCHMARK RUNNER +// ============================================================================ + +async function executeBenchmark(suite: string, scenario: string, tier: string, agent: string, model?: string, batchId?: string, quiet?: boolean, specialist?: string) { + // Initialize logger + const logger = BenchmarkLogger.getInstance(); + + // Determine the agent name to log (will be updated if specialist is used) + let agentName = agent; + const runId = logger.startRun(suite, scenario, tier, agentName, model, batchId, !!specialist); + const startTime = Date.now(); + + // Timeout watchdog based on scenario timeout_minutes (default 60) + let timeoutId: NodeJS.Timeout | null = null; + + // Initialize progress tracker (only if not in quiet mode) + const progress = quiet ? null : createProgress(); + + try { + // Stage 1: Setup + if (progress) updateProgress(progress, 1, 'Loading scenario configuration'); + const scenarioCfg = loadScenario(suite, scenario); + + // Start timeout watchdog after loading scenario config + const scenarioTimeoutMin = Number(scenarioCfg.timeout_minutes || 60); + const timeoutMs = Math.max(1, scenarioTimeoutMin) * 60 * 1000; + timeoutId = setTimeout(() => { + try { + logger.markRunIncomplete(`Run exceeded timeout (${scenarioTimeoutMin} minutes)`, 'timeout'); + if (!quiet) console.log(chalk.yellow(`⚠ Run timed out after ${scenarioTimeoutMin} minutes`)); + } catch {} + }, timeoutMs); + + if (progress) updateProgress(progress, 1, 'Loading prompt'); + const promptContent = loadPrompt(suite, scenario, tier); + + // Early failure check: prompt missing for non-echo agents + if (!promptContent && agent !== 'echo') { + logger.failRun('Prompt file not found', 'prompt'); + if (!quiet) console.log(chalk.red('✗ Prompt file not found')); + if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: Prompt file not found`)); + return; + } + + // Stage 1.5: Warmup (if configured) + if (progress) updateProgress(progress, 1, 'Running warmup phase'); + const warmupResult = await executeWarmup(suite, scenario, scenarioCfg, quiet); + if (!warmupResult.success) { + logger.failRun(`Warmup failed: ${warmupResult.error}`, 'warmup'); + if (!quiet) console.log(chalk.red(`✗ Warmup failed: ${warmupResult.error}`)); + if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: Warmup failed`)); + return; + } + + // Stage 2: Workspace + if (progress) updateProgress(progress, 2, 'Preparing workspace'); + const workspacePrep = prepareWorkspaceFromFixture(suite, scenario); + + // Early failure check: workspace preparation failed + if (!workspacePrep) { + logger.failRun('Workspace preparation failed', 'workspace'); + if (!quiet) console.log(chalk.red('✗ Workspace preparation failed')); + if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: Workspace preparation failed`)); + return; + } + + const workspaceDir = workspacePrep.workspaceDir; + const fixtureDir = workspacePrep.fixtureDir; + + console.log(chalk.magenta('🔍 DEBUG: Workspace prepared successfully')); + console.log(chalk.magenta(`🔍 DEBUG: workspaceDir = ${workspaceDir}`)); + console.log(chalk.magenta(`🔍 DEBUG: fixtureDir = ${fixtureDir}`)); + + // Initialize result structure + const result = { + suite, + scenario, + tier, + agent, + model: model || 'default', + agent_response: '', + scores: { + install_success: 0, + tests_nonregression: 0, + manager_correctness: 0, + dependency_targets: 0, + integrity_guard: 0, + }, + totals: { weighted: 0, max: 10 }, + telemetry: { + toolCalls: 0, + tokens: { in: 0, out: 0 }, + cost_usd: 0, + workspaceDir + } + }; + + + // Stage 3: Agent Execution + console.log(chalk.magenta('🔍 DEBUG: Stage 3 check - promptContent exists:', !!promptContent, 'agent:', agent)); + if (promptContent && agent !== 'echo') { + console.log(chalk.magenta('🔍 DEBUG: Entering Stage 3 - Agent Execution')); + if (progress) updateProgress(progress, 3, 'Agent working...'); + try { + console.log(chalk.magenta('🔍 DEBUG: About to create agent adapter')); + // Create agent adapter + const agentAdapter = createAgentAdapter(agent, model, specialist); + + // Update the logged agent name with the actual adapter name + // (for specialists, this will be "specialist:template-name:base-adapter") + logger.updateAgent(agentAdapter.name, runId); + + // Show selected model info - ALWAYS for OpenRouter + if (agent === 'openrouter' && 'getModel' in agentAdapter) { + const adapterModel = (agentAdapter as any).getModel(); + const modelSource = 'getModelSource' in agentAdapter + ? (agentAdapter as any).getModelSource() + : 'unknown'; + + if (modelSource === 'default') { + console.log(chalk.yellow(` ⚠️ Using default model: ${chalk.cyan(adapterModel)}`)); + console.log(chalk.gray(` Tip: Search and select a model in interactive mode or use --model flag`)); + } else if (modelSource === 'environment') { + console.log(chalk.blue(` ℹ️ Using model from environment: ${chalk.cyan(adapterModel)}`)); + } else if (model && modelSource === 'parameter') { + console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(model)}`)); + + // Verify match + if (adapterModel !== model) { + console.log(chalk.yellow(` ⚠️ Warning: Model mismatch - requested: ${model}, adapter: ${adapterModel}`)); + } else { + console.log(chalk.green(` ✅ Model confirmed: ${adapterModel}`)); + } + } else { + console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(adapterModel)}`)); + } + } else if (model && agent !== 'openrouter') { + // For non-OpenRouter agents, show model if provided + console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(model)}`)); + } + + // Agent info is shown in progress bar + + // Load oracle if available + let oracle: Oracle | undefined; + const oracleFile = scenarioCfg.oracle?.answers_file; + if (oracleFile) { + const scenarioDir = getScenarioDir(suite, scenario); + const oraclePath = join(scenarioDir, oracleFile); + if (existsSync(oraclePath)) { + oracle = new Oracle(oraclePath); + if (progress) updateProgress(progress, 3, 'Agent with oracle support enabled'); + } + } + + // Build the request messages + // For vanilla runs: send ONLY user message (no system prompt for true baseline testing) + // For specialist runs: SpecialistAdapter will add the specialist system prompt from template + const messages: Array<{ role: 'system' | 'user'; content: string }> = [ + { + role: 'user' as const, + content: promptContent + } + ]; + + // Build the request + const request: AgentRequest = { + messages, + ...(workspaceDir && { workspaceDir }), + }; + + // Add tools if agent supports them (Anthropic and OpenRouter) + if ((agent === 'anthropic' || agent === 'openrouter') && workspaceDir) { + // Create workspace tool handlers + const workspaceHandlers = createWorkspaceToolHandlers(workspaceDir); + + // Start with workspace tools + const tools = getAllWorkspaceTools(); + const toolHandlers = workspaceHandlers; + + // Add askUser tool if oracle is available + if (oracle) { + tools.push(createAskUserToolDefinition()); + toolHandlers.set('askUser', createAskUserHandler(oracle)); + // Tools: readFile, writeFile, runCommand, listFiles, askUser + } else { + // Tools: readFile, writeFile, runCommand, listFiles + } + + // Convert tools to adapter-specific format + if (agent === 'openrouter') { + // Convert ToolDefinition to OpenRouter format + (request as any).tools = tools.map(tool => ({ + type: 'function', + function: { + name: tool.name, + description: tool.description, + parameters: tool.input_schema // Map input_schema → parameters + } + })); + } else { + // Anthropic uses ToolDefinition format directly + request.tools = tools; + } + + request.toolHandlers = toolHandlers; + } + + // Extract the prompt being sent for logging + const promptSent = JSON.stringify(request.messages); + + // Execute agent request + const response = await agentAdapter.send(request); + + // Show summary after agent completes + console.log(chalk.gray(` ✓ Tokens: ${response.tokensIn || 0} in, ${response.tokensOut || 0} out | Cost: $${(response.costUsd || 0).toFixed(4)}`)); + + // Update result with agent response + result.agent_response = response.content; + result.telemetry.tokens.in = response.tokensIn || 0; + result.telemetry.tokens.out = response.tokensOut || 0; + result.telemetry.cost_usd = response.costUsd || 0; + result.telemetry.toolCalls = response.toolCalls ?? 0; + + // Log telemetry to database + const duration = Date.now() - startTime; + logger.logTelemetry( + response.toolCalls ?? 0, + response.tokensIn || 0, + response.tokensOut || 0, + response.costUsd || 0, + duration, + workspaceDir, + promptSent + ); + + // Log oracle usage if available + if (oracle) { + const questionLog = oracle.getQuestionLog(); + if (questionLog.length > 0) { + // Oracle questions logged + (result as any).oracle_questions = questionLog; + } + } + + // Telemetry is shown in final results + + } catch (error) { + console.log(chalk.magenta('🔍 DEBUG: Agent execution threw error:', error)); + logger.failRun(error instanceof Error ? error.message : String(error), 'agent'); + if (!quiet) console.log(chalk.red('✗ Agent execution failed')); + if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: ${error instanceof Error ? error.message : String(error)}`)); + return; // Early exit - don't continue to evaluation + } + } else if (!promptContent) { + console.log(chalk.magenta('🔍 DEBUG: Skipping agent execution - no prompt content')); + } else { + console.log(chalk.magenta('🔍 DEBUG: Skipping agent execution - using echo agent')); + } + + // Stage 4: Validation + console.log(chalk.magenta('🔍 DEBUG: Stage 4 - Validation starting')); + if (progress) updateProgress(progress, 4, 'Running validation commands'); + const commandLog = workspaceDir ? runValidationCommands(workspaceDir, scenarioCfg.validation?.commands) : []; + const diffArtifacts = workspaceDir && fixtureDir ? buildDiffArtifacts(fixtureDir, workspaceDir) : { diffSummary: [], depsDelta: [] }; + + const passedCommands = commandLog.filter(cmd => cmd.exitCode === 0).length; + if (!quiet) console.log(chalk.gray(` ✓ ${passedCommands}/${commandLog.length} commands passed`)); + + // Stage 5: Evaluation + console.log(chalk.magenta('🔍 DEBUG: Stage 5 - Evaluation starting')); + if (progress) updateProgress(progress, 5, 'Computing scores'); + + try { + if (workspaceDir) { + // Actually run evaluators + const ctx = { + scenario: scenarioCfg, + workspaceDir, + agentResponse: result.agent_response, + commandLog, + diffSummary: diffArtifacts.diffSummary, + depsDelta: diffArtifacts.depsDelta, + }; + const { scoreCard, results: evaluatorResults } = await runEvaluators(ctx); + result.scores = { ...result.scores, ...scoreCard }; + result.totals = computeWeightedTotals(result.scores, scenarioCfg); + (result as any).evaluator_results = evaluatorResults; + (result as any).diff_summary = diffArtifacts.diffSummary; + (result as any).deps_delta = diffArtifacts.depsDelta; + + // Log evaluation results to database + for (const evalResult of evaluatorResults) { + logger.logEvaluation( + evalResult.name, + evalResult.score, + 1.0, // max score + evalResult.details + ); + } + + // Show evaluator summary + const avgScore = Object.values(scoreCard).reduce((sum, score) => sum + (score as number), 0) / Object.keys(scoreCard).length; + if (!quiet) console.log(chalk.gray(` ✓ Average score: ${(avgScore * 100).toFixed(1)}%`)); + } + } catch (e) { + // Evaluator run failed + } + + // Complete the run in database + const totalScore = Object.values(result.scores || {}).reduce((sum, score) => sum + (typeof score === 'number' ? score : 0), 0) / Object.keys(result.scores || {}).length; + + // Calculate success based on validation commands and evaluator scores + const { isSuccessful, successMetric } = calculateSuccess(commandLog, result.scores || {}, scenarioCfg); + + // Extract package manager and test results + let packageManager: string | undefined; + if (workspaceDir) { + packageManager = detectPackageManager(workspaceDir); + if (packageManager === 'unknown') { + packageManager = extractPackageManagerFromCommand(commandLog); + } + } + + const testResults = extractTestResults(commandLog); + const testResultsJson = testResults ? JSON.stringify(testResults) : undefined; + + logger.completeRun( + totalScore, + result.totals?.weighted, + { + diffSummary: diffArtifacts.diffSummary, + depsDelta: diffArtifacts.depsDelta, + oracleQuestions: (result as any).oracle_questions + }, + isSuccessful, + successMetric, + packageManager, + testResultsJson + ); + + // Stage 6: Results + if (progress) updateProgress(progress, 6, 'Preparing results'); + + if (progress) completeProgress(progress); + + // Display results in table format (only if not in quiet mode) + const duration = (Date.now() - startTime) / 1000; + const weightedScore = result.totals?.weighted || 0; + + // In quiet mode, show compact one-line output + if (quiet) { + const status = isSuccessful ? chalk.green('✓') : chalk.red('✗'); + const modelStr = model ? ` [${model}]` : ''; + const successStr = isSuccessful ? 'SUCCESS' : 'FAILED'; + console.log(`${status} ${suite}/${scenario} (${tier}) ${agent}${modelStr} - ${weightedScore.toFixed(2)}/10 [${successStr}]`); + return; + } + + console.log(`\n${chalk.bold.underline('Benchmark Results')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Agent:')} ${chalk.cyan(agent.padEnd(15))} ${chalk.bold('Tier:')} ${chalk.cyan(tier.padEnd(8))} ${chalk.bold('Duration:')} ${chalk.blue(duration.toFixed(2) + 's')} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + console.log(`│ ${chalk.bold('Score (mean ± σ):')} ${chalk.green(weightedScore.toFixed(4))} ± ${chalk.green('0.0000')} ${chalk.gray('(out of 10.0)')} │`); + console.log(`│ ${chalk.bold('Range (min ... max):')} ${chalk.green(weightedScore.toFixed(4))} ${chalk.white('...')} ${chalk.red(weightedScore.toFixed(4))} ${chalk.gray('(1 run)')} │`); + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + // Print evaluation breakdown in table format + if (result.scores) { + console.log(`\n${chalk.bold.underline('Evaluation Breakdown')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Evaluator'.padEnd(25))} ${chalk.bold('Score'.padEnd(10))} ${chalk.bold('Status'.padEnd(15))} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + + Object.entries(result.scores).forEach(([name, score]) => { + const percent = (score as number) * 100; + const color = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; + const status = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'Excellent' : percent >= SCORE_THRESHOLDS.GOOD ? 'Good' : 'Needs Work'; + const statusColor = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; + + // Special handling for LLM Judge Evaluator + const displayName = name === 'LLMJudgeEvaluator' ? 'LLM Judge' : name; + + console.log(`│ ${chalk.cyan(displayName.padEnd(25))} ${chalk[color](score.toFixed(4).padEnd(10))} ${chalk[statusColor](status.padEnd(15))} │`); + }); + + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + // Show detailed LLM Judge scores if available + displayLLMJudgeScores(result); + } + + // Print telemetry in table format + if (result.telemetry) { + console.log(`\n${chalk.bold.underline('Telemetry')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Metric'.padEnd(20))} ${chalk.bold('Value'.padEnd(20))} ${chalk.bold('Unit'.padEnd(15))} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + console.log(`│ ${chalk.cyan('Tool Calls'.padEnd(20))} ${chalk.green((result.telemetry.toolCalls || 0).toString().padEnd(20))} ${chalk.gray('calls'.padEnd(15))} │`); + console.log(`│ ${chalk.cyan('Tokens In'.padEnd(20))} ${chalk.green((result.telemetry.tokens?.in || 0).toString().padEnd(20))} ${chalk.gray('tokens'.padEnd(15))} │`); + console.log(`│ ${chalk.cyan('Tokens Out'.padEnd(20))} ${chalk.green((result.telemetry.tokens?.out || 0).toString().padEnd(20))} ${chalk.gray('tokens'.padEnd(15))} │`); + console.log(`│ ${chalk.cyan('Cost'.padEnd(20))} ${chalk.green(`$${(result.telemetry.cost_usd || 0).toFixed(6)}`.padEnd(20))} ${chalk.gray('USD'.padEnd(15))} │`); + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + } + + // Show database summary in table format + try { + const stats = logger.getStats(); + console.log(`\n${chalk.bold.underline('Database Summary')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Metric'.padEnd(25))} ${chalk.bold('Value'.padEnd(20))} ${chalk.bold('Status'.padEnd(10))} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + console.log(`│ ${chalk.cyan('Total Runs'.padEnd(25))} ${chalk.blue(stats.totalRuns.toString().padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); + console.log(`│ ${chalk.cyan('Success Rate'.padEnd(25))} ${chalk.green(`${(stats.successRate * 100).toFixed(1)}%`.padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); + console.log(`│ ${chalk.cyan('Average Score'.padEnd(25))} ${chalk.green(stats.averageWeightedScore.toFixed(4).padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); + console.log(`│ ${chalk.cyan('Database'.padEnd(25))} ${chalk.blue('benchmark-report/public/benchmarks.db'.padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + } catch (dbError) { + log.warning(chalk.yellow('Database query failed:')); + console.error(chalk.dim(dbError instanceof Error ? dbError.message : String(dbError))); + } + + // Results are saved to database only (benchmarks.db is the single source of truth) + console.log(`\n${chalk.green('✓')} Results saved to database`); + + // Note: Database is now created directly in public/ directory + + // Show completion outro + console.log(`\n${chalk.green('✓')} Benchmark completed successfully`); + + } catch (error) { + // Catch-all for unexpected errors + const errorMessage = error instanceof Error ? error.message : String(error); + const errorStack = error instanceof Error ? error.stack : undefined; + + logger.failRun(errorMessage, 'unknown'); + + if (!quiet) { + console.log(chalk.red('✗ Unexpected error')); + console.error(chalk.red('\nError details:')); + console.error(chalk.red(errorMessage)); + if (errorStack) { + console.error(chalk.dim('\nStack trace:')); + console.error(chalk.dim(errorStack)); + } + } + if (quiet) { + console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: ${errorMessage}`)); + } + } finally { + if (progress) completeProgress(progress); + // Clear timeout watchdog if set + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + } +} + +async function validateEnvironment() { + const missingVars: string[] = []; + + // Check for API keys based on available agents + if (!process.env.OPENROUTER_API_KEY && !process.env.ANTHROPIC_API_KEY) { + missingVars.push('OPENROUTER_API_KEY or ANTHROPIC_API_KEY'); + } + + if (missingVars.length > 0) { + console.log(chalk.red('❌ Missing required environment variables:')); + console.log(chalk.yellow(` ${missingVars.join(', ')}`)); + console.log('\n' + chalk.cyan('Setup Instructions:')); + console.log(chalk.gray('1. Get API keys from:')); + console.log(chalk.gray(' - OpenRouter: https://openrouter.ai/keys')); + console.log(chalk.gray(' - Anthropic: https://console.anthropic.com/settings/keys')); + console.log(chalk.gray('2. Create a .env file in the project root:')); + console.log(chalk.gray(' cp .env.example .env')); + console.log(chalk.gray('3. Edit .env and add your API keys:')); + console.log(chalk.gray(' OPENROUTER_API_KEY=your_key_here')); + console.log(chalk.gray(' ANTHROPIC_API_KEY=your_key_here')); + console.log(chalk.gray('4. Or set environment variables directly:')); + console.log(chalk.gray(' Windows: set OPENROUTER_API_KEY=your_key_here')); + console.log(chalk.gray(' Linux/Mac: export OPENROUTER_API_KEY=your_key_here')); + console.log('\n' + chalk.red('Please set up your environment variables and try again.')); + process.exit(1); + } +} + +// ============================================================================ +// SECTION 9: MAIN ENTRY POINT +// ============================================================================ + +async function run() { + const parsedArgs = parseArgs(process.argv); + + // Skip environment validation for creation commands (they don't need API keys) + const skipValidation = parsedArgs.cmd === 'new-suite' || parsedArgs.cmd === 'new-scenario'; + + // Check for required environment variables first (unless skipping for creation commands) + if (!skipValidation) { + await validateEnvironment(); + } + + // Dev server will be started only when viewing statistics + + // If no arguments provided, show interactive menu + if (process.argv.length <= 2) { + await showInteractiveMenu(); + return; + } + + // Handle stats command + if (parsedArgs.cmd === 'stats') { + const logger = BenchmarkLogger.getInstance(); + const { level, identifier } = parsedArgs; + + try { + if (level === 'suite') { + if (!identifier[0]) { + log.warning('Usage: pnpm bench --stats suite '); + return; + } + await runInteractiveSuiteStats(); + + } else if (level === 'scenario') { + if (!identifier[0] || !identifier[1]) { + log.warning('Usage: pnpm bench --stats scenario '); + return; + } + await runInteractiveScenarioStats(); + + } else if (level === 'run') { + if (!identifier[0]) { + log.warning('Usage: pnpm bench --stats run '); + return; + } + await runInteractiveRunStats(); + + } else { + log.warning('Usage: pnpm bench --stats '); + console.log(' pnpm bench --stats suite '); + console.log(' pnpm bench --stats scenario '); + console.log(' pnpm bench --stats run '); + } + } catch (error) { + log.error(chalk.red('Failed to fetch statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + process.exit(1); + } finally { + logger.close(); + } + return; + } + + // Handle clear-db command + if (parsedArgs.cmd === 'clear-db') { + await runInteractiveClear(); + return; + } + + // Handle evaluators command + if (parsedArgs.cmd === 'evaluators') { + await runInteractiveEvaluators(); + return; + } + + // Handle history command + if (parsedArgs.cmd === 'history') { + const logger = BenchmarkLogger.getInstance(); + const limit = parsedArgs.limit; + + intro(chalk.bgCyan(' Benchmark History ')); + + try { + const runHistory = logger.getRunHistory(limit); + + if (runHistory.length === 0) { + log.warning('No benchmark runs found'); + outro(chalk.yellow('Run a benchmark first: pnpm bench ')); + return; + } + + // Use common display function + runHistory.forEach((run, index) => displayRunInfo(run, index)); + + // Show overall stats + const stats = logger.getStats(); + console.log('\n' + chalk.underline('Overall Statistics')); + console.log(formatStats('Total Runs', stats.totalRuns)); + console.log(formatStats('Success Rate', `${(stats.successRate * 100).toFixed(1)}%`, 'green')); + console.log(formatStats('Avg Score', stats.averageScore.toFixed(4), 'yellow')); + console.log(formatStats('Avg Weighted', stats.averageWeightedScore.toFixed(4), 'yellow')); + console.log(formatStats('Avg Duration', `${(stats.averageDuration / 1000).toFixed(2)}s`, 'blue')); + + outro(chalk.green(`Showing ${runHistory.length} recent runs`)); + + } catch (error) { + log.error(chalk.red('Failed to fetch history:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + process.exit(1); + } finally { + logger.close(); + } + return; + } + + // Handle batches command + if (parsedArgs.cmd === 'batches') { + const logger = BenchmarkLogger.getInstance(); + const limit = parsedArgs.limit; + + try { + const batches = logger.getAllBatches({ limit }); + + if (batches.length === 0) { + log.warning('No batches found'); + outro(chalk.yellow('Run some benchmarks first')); + return; + } + + intro(chalk.bgCyan(' Batch History ')); + + batches.forEach((batch, index) => { + const status = batch.completedAt + ? chalk.green('✓') + : chalk.yellow('○'); + const duration = batch.duration ? `${(batch.duration / 1000).toFixed(2)}s` : 'Running...'; + const successRate = batch.totalRuns > 0 ? ((batch.successfulRuns / batch.totalRuns) * 100).toFixed(0) : 0; + + console.log(`\n${chalk.bold(`${index + 1}.`)} ${status} ${chalk.cyan('Batch')} ${chalk.dim(batch.batchId.substring(0, 8))}...`); + console.log(` ${formatStats('Runs', `${batch.successfulRuns}/${batch.totalRuns} (${successRate}%)`, 'green')}`); + console.log(` ${formatStats('Avg Score', batch.avgWeightedScore?.toFixed(4) || 'N/A', 'yellow')}`); + console.log(` ${formatStats('Duration', duration, 'blue')}`); + console.log(` ${chalk.gray(new Date(batch.createdAt).toLocaleString())}`); + }); + + outro(chalk.green(`Showing ${batches.length} batches`)); + + } catch (error) { + log.error(chalk.red('Failed to fetch batches:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + process.exit(1); + } finally { + logger.close(); + } + return; + } + + // Handle batch-details command + if (parsedArgs.cmd === 'batch-details') { + const logger = BenchmarkLogger.getInstance(); + const { batchId } = parsedArgs; + + if (!batchId) { + log.warning('Usage: pnpm bench --batch-details '); + return; + } + + try { + const analytics = logger.getBatchAnalytics(batchId); + + if (!analytics) { + log.error(chalk.red(`Batch ${batchId} not found`)); + return; + } + + intro(chalk.bgCyan(' Batch Details ')); + + const batch = analytics.batch; + const duration = batch.duration / 1000; + const successRate = batch.totalRuns > 0 ? ((batch.successfulRuns / batch.totalRuns) * 100).toFixed(1) : 0; + + console.log(`\n${chalk.bold('Batch ID:')} ${chalk.dim(batchId.substring(0, 16))}...`); + console.log(formatStats('Status', batch.completedAt ? 'Completed' : 'Running', batch.completedAt ? 'green' : 'yellow')); + console.log(formatStats('Total Runs', `${batch.totalRuns}`, 'blue')); + console.log(formatStats('Successful', `${batch.successfulRuns}/${batch.totalRuns} (${successRate}%)`, 'green')); + console.log(formatStats('Avg Score', batch.avgWeightedScore.toFixed(4), 'yellow')); + console.log(formatStats('Duration', `${duration.toFixed(2)}s`, 'blue')); + console.log(formatStats('Started', new Date(batch.createdAt).toLocaleString())); + + // Suite breakdown + if (analytics.suiteBreakdown.length > 0) { + console.log(`\n${chalk.bold.underline('Suite Breakdown')}`); + analytics.suiteBreakdown.forEach(suite => { + const rate = suite.runs > 0 ? ((suite.successfulRuns / suite.runs) * 100).toFixed(0) : 0; + console.log(` ${chalk.cyan(suite.suite)}/${suite.scenario}: ${suite.avgWeightedScore.toFixed(2)}/10 ${chalk.gray(`(${rate}% success, ${suite.runs} runs)`)}`); + }); + } + + // Agent performance + if (analytics.agentPerformance.length > 0) { + console.log(`\n${chalk.bold.underline('Agent Performance')}`); + analytics.agentPerformance.forEach((agent, i) => { + const rankDisplay = i < 3 ? `#${i + 1}` : `${i + 1}.`; + const modelStr = agent.model && agent.model !== 'default' ? ` [${agent.model}]` : ''; + const scoreColor = agent.avgWeightedScore >= 9 ? 'green' : agent.avgWeightedScore >= 7 ? 'yellow' : 'red'; + console.log(` ${rankDisplay} ${chalk.cyan(agent.agent)}${modelStr}: ${chalk[scoreColor](agent.avgWeightedScore.toFixed(2))}/10 ${chalk.gray(`(${agent.successfulRuns}/${agent.runs})`)}`); + }); + } + + // Tier distribution + if (analytics.tierDistribution.length > 0) { + console.log(`\n${chalk.bold.underline('Tier Distribution')}`); + analytics.tierDistribution.forEach(tier => { + console.log(` ${chalk.cyan(tier.tier)}: ${tier.avgWeightedScore.toFixed(2)}/10 ${chalk.gray(`(${tier.successfulRuns}/${tier.runs} runs)`)}`); + }); + } + + // Failed runs + if (analytics.failedRuns.length > 0) { + console.log(`\n${chalk.bold.underline(chalk.red('Failed Runs'))}`); + analytics.failedRuns.forEach(run => { + console.log(` ${chalk.red('✗')} ${run.suite}/${run.scenario} (${run.tier}) ${run.agent}`); + }); + } + + outro(chalk.green('Batch analytics complete')); + + } catch (error) { + log.error(chalk.red('Failed to fetch batch details:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + process.exit(1); + } finally { + logger.close(); + } + return; + } + + // Handle compare-batches command + if (parsedArgs.cmd === 'compare-batches') { + const logger = BenchmarkLogger.getInstance(); + const { batchIds } = parsedArgs; + + if (!batchIds || batchIds.length < 2) { + log.warning('Usage: pnpm bench --compare-batches [batch-id3...]'); + return; + } + + try { + const batches = logger.getBatchComparison(batchIds); + + if (batches.length === 0) { + log.error(chalk.red('No batches found with the provided IDs')); + return; + } + + intro(chalk.bgMagenta(' Batch Comparison ')); + + console.log(`\n${chalk.bold('Comparing')} ${batches.length} batches:\n`); + + // Create comparison table + console.log(chalk.bold('Batch'.padEnd(12)) + ' | ' + + chalk.bold('Runs'.padEnd(8)) + ' | ' + + chalk.bold('Success'.padEnd(10)) + ' | ' + + chalk.bold('Avg Score'.padEnd(10)) + ' | ' + + chalk.bold('Duration')); + console.log('─'.repeat(70)); + + batches.forEach(batch => { + const batchIdShort = batch.batchId.substring(0, 8) + '...'; + const successRate = batch.totalRuns > 0 ? ((batch.successfulRuns / batch.totalRuns) * 100).toFixed(0) + '%' : 'N/A'; + const duration = batch.duration ? `${(batch.duration / 1000).toFixed(0)}s` : 'N/A'; + const score = batch.avgWeightedScore?.toFixed(2) || 'N/A'; + + console.log( + chalk.dim(batchIdShort.padEnd(12)) + ' | ' + + `${batch.totalRuns}`.padEnd(8) + ' | ' + + successRate.padEnd(10) + ' | ' + + chalk.yellow(score.padEnd(10)) + ' | ' + + duration + ); + }); + + // Show best performer + const bestBatch = batches.reduce((best, current) => + (current.avgWeightedScore || 0) > (best.avgWeightedScore || 0) ? current : best + ); + + console.log(`\n${chalk.green('Best performing batch:')} ${chalk.dim(bestBatch.batchId.substring(0, 8))}... with score ${chalk.bold(bestBatch.avgWeightedScore?.toFixed(4) || 'N/A')}`); + + outro(chalk.green('Comparison complete')); + + } catch (error) { + log.error(chalk.red('Failed to compare batches:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + process.exit(1); + } finally { + logger.close(); + } + return; + } + + // Handle new-suite command + if (parsedArgs.cmd === 'new-suite') { + await createNewSuite(parsedArgs.name); + return; + } + + // Handle new-scenario command + if (parsedArgs.cmd === 'new-scenario') { + const { suite, name } = parsedArgs; + if (!suite || !name) { + log.warning('Usage: pnpm bench --new-scenario '); + console.log(' Or run without arguments for interactive mode'); + return; + } + await createNewScenario(suite, name); + return; + } + + const { cmd, suite, scenario, tier, agent, model, batchId, specialist } = parsedArgs; + if (cmd !== 'bench' || !suite || !scenario) { + showHelp(); + process.exit(1); + } + + // Show modern CLI intro with hyperfine-style header + console.log(chalk.bold.underline('Demo: Benchmarking AI Agents:')); + console.log(`\n${chalk.green('►')} ${chalk.green('pnpm bench')} ${chalk.yellow(`'${suite}/${scenario}'`)} ${chalk.yellow(`'${tier}'`)} ${chalk.yellow(`'${agent}'`)}`); + + log.info(chalk.bold(`Running: ${suite}/${scenario}`)); + log.info(`${chalk.gray('Tier:')} ${chalk.cyan(tier)} ${chalk.gray('Agent:')} ${chalk.cyan(agent)}`); + + // Warn if OpenRouter agent but no model specified + if (agent === 'openrouter' && !model && !process.env.OPENROUTER_MODEL) { + console.log(chalk.yellow(`\n⚠️ Warning: No model specified for OpenRouter agent. Using default model.`)); + console.log(chalk.gray(` Tip: Use --model flag or set OPENROUTER_MODEL environment variable`)); + console.log(chalk.gray(` Example: pnpm bench ${suite}/${scenario} ${tier} ${agent} --model openai/gpt-4o-mini\n`)); + } + + // Execute the benchmark + await executeBenchmark(suite, scenario, tier, agent, model, batchId, false, specialist); +} + +// Cleanup handlers +process.on('exit', () => { + stopDevServer(); +}); + +process.on('SIGINT', () => { + console.log('\nShutting down...'); + try { + const logger = BenchmarkLogger.getInstance(); + // Mark current run incomplete if any + (logger as any).markRunIncomplete?.('Interrupted by user (SIGINT)', 'signal'); + console.log(chalk.yellow('⚠ Current run marked as incomplete')); + } catch {} + stopDevServer(); + process.exit(0); +}); + +process.on('SIGTERM', () => { + console.log('\nShutting down...'); + try { + const logger = BenchmarkLogger.getInstance(); + (logger as any).markRunIncomplete?.('Interrupted by system (SIGTERM)', 'signal'); + console.log(chalk.yellow('⚠ Current run marked as incomplete')); + } catch {} + stopDevServer(); + process.exit(0); +}); + +process.on('uncaughtException', (err) => { + console.error('\nUncaught Exception:', err); + stopDevServer(); + process.exit(1); +}); + +process.on('unhandledRejection', (reason, promise) => { + console.error('\nUnhandled Rejection at:', promise, 'reason:', reason); + stopDevServer(); + process.exit(1); +}); + +run().catch((err) => { + console.log(`\n${chalk.red('✗')} Benchmark failed: ${err instanceof Error ? err.message : String(err)}`); + + // Try to log the error to database if logger is available + try { + const logger = BenchmarkLogger.getInstance(); + logger.failRun(String(err)); + } catch (logErr) { + console.log(`${chalk.yellow('⚠')} Failed to log error to database: ${logErr instanceof Error ? logErr.message : String(logErr)}`); + } + + stopDevServer(); + process.exit(1); +}); diff --git a/packages/harness/src/cli/args.ts b/packages/harness/src/cli/args.ts new file mode 100644 index 0000000..16e14d0 --- /dev/null +++ b/packages/harness/src/cli/args.ts @@ -0,0 +1,133 @@ +import chalk from 'chalk'; +import { intro, outro } from '@clack/prompts'; +import { createTitle } from '../lib/display.ts'; + +// ============================================================================ +// COMMAND-LINE ARGUMENT PARSING +// ============================================================================ + +export function parseArgs(argv: string[]) { + // Skip node, script path - arguments are directly suite and scenario + const args = argv.slice(2); + + // Check for history command + if (args[0] === '--history') { + const limit = args[1] ? parseInt(args[1], 10) : 10; + return { cmd: 'history', limit: isNaN(limit) ? 10 : limit } as const; + } + + // Check for evaluators command + if (args[0] === '--evaluators') { + return { cmd: 'evaluators' } as const; + } + + // Check for clear-db command + if (args[0] === '--clear-db') { + return { cmd: 'clear-db' } as const; + } + + // Check for stats command + if (args[0] === '--stats') { + const level = args[1]; // 'run', 'suite', or 'scenario' + const identifier = args.slice(2); // suite name, scenario name, or run ID + return { cmd: 'stats', level, identifier } as const; + } + + // Check for batches command + if (args[0] === '--batches') { + const limit = args[1] ? parseInt(args[1], 10) : 20; + return { cmd: 'batches', limit: isNaN(limit) ? 20 : limit } as const; + } + + // Check for batch-details command + if (args[0] === '--batch-details') { + const batchId = args[1]; + return { cmd: 'batch-details', batchId } as const; + } + + // Check for compare-batches command + if (args[0] === '--compare-batches') { + const batchIds = args.slice(1); + return { cmd: 'compare-batches', batchIds } as const; + } + + // Check for new-suite command + if (args[0] === '--new-suite') { + const name = args[1]; + return { cmd: 'new-suite', name } as const; + } + + // Check for new-scenario command + if (args[0] === '--new-scenario') { + const suite = args[1]; + const name = args[2]; + return { cmd: 'new-scenario', suite, name } as const; + } + + const cmd = 'bench'; + const suite = args[0]; + const scenario = args[1]; + const rest = args.slice(2); + + const tierIndex = rest.indexOf('--tier'); + const tier = tierIndex !== -1 ? rest[tierIndex + 1] : 'L0'; + + const agentIndex = rest.indexOf('--agent'); + const agent = agentIndex !== -1 ? rest[agentIndex + 1] : 'echo'; + + const modelIndex = rest.indexOf('--model'); + const model = modelIndex !== -1 ? rest[modelIndex + 1] : undefined; + + const batchIdIndex = rest.indexOf('--batch-id'); + const batchId = batchIdIndex !== -1 ? rest[batchIdIndex + 1] : undefined; + + const specialistIndex = rest.indexOf('--specialist'); + const specialist = specialistIndex !== -1 ? rest[specialistIndex + 1] : undefined; + + const skipWarmup = rest.includes('--skip-warmup'); + const warmupOnly = rest.includes('--warmup-only'); + const quiet = rest.includes('--quiet'); + + return { cmd, suite, scenario, tier, agent, model, batchId, specialist, skipWarmup, warmupOnly, quiet } as const; +} + +export function showHelp() { + console.log(chalk.cyan(createTitle())); + intro(chalk.bgBlue(' CLI Help ')); + + console.log('\n' + chalk.bold('Usage:')); + console.log(` ${chalk.cyan('pnpm bench')} [options]`); + + console.log('\n' + chalk.bold('Commands:')); + console.log(` ${chalk.cyan('--history')} [limit] Show recent runs`); + console.log(` ${chalk.cyan('--evaluators')} Show evaluator stats`); + console.log(` ${chalk.cyan('--stats')} suite Suite statistics`); + console.log(` ${chalk.cyan('--stats')} scenario Scenario statistics`); + console.log(` ${chalk.cyan('--stats')} run Run details`); + console.log(` ${chalk.cyan('--batches')} [limit] List recent batches`); + console.log(` ${chalk.cyan('--batch-details')} Detailed batch analytics`); + console.log(` ${chalk.cyan('--compare-batches')} Compare multiple batches`); + console.log(` ${chalk.cyan('--new-suite')} [name] Create a new benchmark suite`); + console.log(` ${chalk.cyan('--new-scenario')} [suite] [name] Create a new scenario in a suite`); + console.log(` ${chalk.cyan('--clear-db')} Clear database`); + + console.log('\n' + chalk.bold('Options:')); + console.log(` ${chalk.cyan('--tier')} Difficulty tier (varies by scenario)`); + console.log(` ${chalk.cyan('--agent')} Agent to use`); + console.log(` ${chalk.cyan('--model')} Model name`); + console.log(` ${chalk.cyan('--specialist')} Specialist name (e.g., @zephyr-cloud/shadcn-specialist)`); + console.log(` ${chalk.cyan('--skip-warmup')} Skip warmup phase (for parallel execution)`); + console.log(` ${chalk.cyan('--warmup-only')} Run warmup phase only (for manual validation)`); + console.log(` ${chalk.cyan('--quiet')} Minimal output (for parallel execution)`); + + console.log('\n' + chalk.bold('Model Selection:')); + console.log(` ${chalk.cyan('OpenRouter Models:')} Search-based selection from 200+ models`); + console.log(` ${chalk.gray('Search by:')} model name, provider, or description`); + console.log(` ${chalk.gray('Example searches:')} "gpt-4o", "llama-3", "gemma free", "claude sonnet"`); + + console.log('\n' + chalk.bold('Web Dashboard:')); + console.log(` ${chalk.blue('http://localhost:3000')} ${chalk.gray('- Interactive charts and analytics')}`); + console.log(` ${chalk.gray('Run:')} ${chalk.yellow('pnpm dev')} ${chalk.gray('to start the web server')}`); + + outro(chalk.gray('Run any command to get started')); +} diff --git a/packages/harness/src/cli/environment.ts b/packages/harness/src/cli/environment.ts new file mode 100644 index 0000000..02cdc77 --- /dev/null +++ b/packages/harness/src/cli/environment.ts @@ -0,0 +1,33 @@ +import chalk from 'chalk'; + +// ============================================================================ +// ENVIRONMENT VALIDATION +// ============================================================================ + +export async function validateEnvironment() { + const missingVars: string[] = []; + + // Check for API keys based on available agents + if (!process.env.OPENROUTER_API_KEY && !process.env.ANTHROPIC_API_KEY) { + missingVars.push('OPENROUTER_API_KEY or ANTHROPIC_API_KEY'); + } + + if (missingVars.length > 0) { + console.log(chalk.red('❌ Missing required environment variables:')); + console.log(chalk.yellow(` ${missingVars.join(', ')}`)); + console.log('\n' + chalk.cyan('Setup Instructions:')); + console.log(chalk.gray('1. Get API keys from:')); + console.log(chalk.gray(' - OpenRouter: https://openrouter.ai/keys')); + console.log(chalk.gray(' - Anthropic: https://console.anthropic.com/settings/keys')); + console.log(chalk.gray('2. Create a .env file in the project root:')); + console.log(chalk.gray(' cp .env.example .env')); + console.log(chalk.gray('3. Edit .env and add your API keys:')); + console.log(chalk.gray(' OPENROUTER_API_KEY=your_key_here')); + console.log(chalk.gray(' ANTHROPIC_API_KEY=your_key_here')); + console.log(chalk.gray('4. Or set environment variables directly:')); + console.log(chalk.gray(' Windows: set OPENROUTER_API_KEY=your_key_here')); + console.log(chalk.gray(' Linux/Mac: export OPENROUTER_API_KEY=your_key_here')); + console.log('\n' + chalk.red('Please set up your environment variables and try again.')); + process.exit(1); + } +} diff --git a/packages/harness/src/domain/agent.ts b/packages/harness/src/domain/agent.ts new file mode 100644 index 0000000..b5e3f03 --- /dev/null +++ b/packages/harness/src/domain/agent.ts @@ -0,0 +1,122 @@ +import { existsSync } from 'node:fs'; +import { resolve } from 'node:path'; +import chalk from 'chalk'; +import { EchoAgent, ClaudeCodeAdapter, OpenRouterAdapter, AnthropicAdapter, type AgentAdapter } from '../../../agent-adapters/src/index.ts'; + +// ============================================================================ +// AGENT MANAGEMENT +// ============================================================================ + +export async function getAvailableAgents(): Promise> { + const agents = [ + { value: '__ALL__', label: 'All agents' }, + { value: 'echo', label: 'Echo (Test Agent)' }, + { value: 'openrouter', label: 'OpenRouter (Any Model)' }, + { value: 'anthropic', label: 'Anthropic Claude (Direct API)' }, + { value: 'claude-code', label: 'Claude Code' } + ]; + + return agents; +} + +/** + * Resolve specialist name to template file path + * + * Converts a specialist name (e.g., @zephyr-cloud/shadcn-specialist) to the + * corresponding template file path in starting_from_outcome/ + * + * @param specialistName - Specialist name (with or without namespace) + * @param workspaceRoot - Workspace root directory + * @returns Absolute path to template file + * @throws Error if template file doesn't exist + */ +export function resolveSpecialistTemplatePath(specialistName: string, workspaceRoot: string): string { + // Strip namespace prefix if present (e.g., @zephyr-cloud/) + const templateName = specialistName.replace(/^@[^/]+\//, ''); + + // Construct template path relative to workspace root + const templatePath = `starting_from_outcome/${templateName}-template.json5`; + const absolutePath = resolve(workspaceRoot, templatePath); + + // Verify template exists + if (!existsSync(absolutePath)) { + throw new Error( + `Specialist template not found: ${templatePath}\n` + + ` Specialist: ${specialistName}\n` + + ` Expected path: ${absolutePath}\n` + + ` Tip: Ensure the template file exists in starting_from_outcome/` + ); + } + + // Return absolute path to avoid cwd-related issues when harness is spawned + // from different directories (similar to mint:snapshot fix) + return absolutePath; +} + +export async function createAgentAdapter(agentName: string, model?: string, specialistName?: string, workspaceRoot?: string): Promise { + // Log agent creation attempt + console.log(chalk.gray('[DEBUG] createAgentAdapter()')); + console.log(chalk.gray(` Agent: ${agentName}`)); + console.log(chalk.gray(` Model: ${model || 'default'}`)); + console.log(chalk.gray(` Specialist: ${specialistName || 'none'}`)); + console.log(chalk.gray(` Workspace root: ${workspaceRoot || 'none'}`)); + + // Create base adapter + let baseAdapter: AgentAdapter; + + try { + switch (agentName) { + case 'openrouter': + console.log(chalk.gray(` Creating OpenRouterAdapter...`)); + // Pass model directly to constructor instead of using environment variable + baseAdapter = new OpenRouterAdapter(process.env.OPENROUTER_API_KEY, model); + console.log(chalk.gray(` ✓ OpenRouterAdapter created`)); + break; + case 'anthropic': + console.log(chalk.gray(` Creating AnthropicAdapter...`)); + if (model) { + process.env.CLAUDE_MODEL = model; + } + baseAdapter = new AnthropicAdapter(); + console.log(chalk.gray(` ✓ AnthropicAdapter created`)); + break; + case 'claude-code': + console.log(chalk.gray(` Creating ClaudeCodeAdapter...`)); + baseAdapter = new ClaudeCodeAdapter(model); + console.log(chalk.gray(` ✓ ClaudeCodeAdapter created`)); + break; + case 'echo': + default: + console.log(chalk.gray(` Creating EchoAgent...`)); + baseAdapter = new EchoAgent(); + console.log(chalk.gray(` ✓ EchoAgent created`)); + break; + } + } catch (error) { + console.error(chalk.red(`[DEBUG] Failed to create base adapter: ${error instanceof Error ? error.message : String(error)}`)); + throw error; + } + + // Wrap with SpecialistAdapter if specialist name provided + if (specialistName && workspaceRoot) { + try { + console.log(chalk.gray(` Resolving specialist template path...`)); + const templatePath = resolveSpecialistTemplatePath(specialistName, workspaceRoot); + console.log(chalk.blue(` ℹ️ Using specialist: ${chalk.cyan(specialistName)}`)); + console.log(chalk.gray(` Template: ${templatePath}`)); + + // Lazy load SpecialistAdapter to avoid loading agency-prompt-creator unless needed + console.log(chalk.gray(` Loading SpecialistAdapter...`)); + const { SpecialistAdapter } = await import('../../../agent-adapters/src/specialist.ts'); + const specialistAdapter = new SpecialistAdapter(baseAdapter, templatePath); + console.log(chalk.gray(` ✓ SpecialistAdapter created and wrapped base adapter`)); + return specialistAdapter; + } catch (error) { + console.error(chalk.red(`[DEBUG] Failed to create specialist adapter: ${error instanceof Error ? error.message : String(error)}`)); + throw error; + } + } + + console.log(chalk.gray(` ✓ Agent adapter creation complete (base adapter only)`)); + return baseAdapter; +} diff --git a/packages/harness/src/domain/scenario.ts b/packages/harness/src/domain/scenario.ts new file mode 100644 index 0000000..82dfda4 --- /dev/null +++ b/packages/harness/src/domain/scenario.ts @@ -0,0 +1,138 @@ +import { existsSync, readFileSync, readdirSync } from 'node:fs'; +import { join } from 'node:path'; +import YAML from 'yaml'; +import chalk from 'chalk'; +import { log } from '@clack/prompts'; +import { findRepoRoot } from '../lib/workspace-utils.ts'; + +// ============================================================================ +// SCENARIO DOMAIN LOGIC +// ============================================================================ + +export function validateName(name: string, type: 'suite' | 'scenario'): { valid: boolean; error?: string } { + if (!name || name.trim().length === 0) { + return { valid: false, error: `${type} name cannot be empty` }; + } + + // Check kebab-case: lowercase, alphanumeric + hyphens, no spaces or special chars + const kebabCasePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; + if (!kebabCasePattern.test(name)) { + return { + valid: false, + error: `${type} name must be in kebab-case (lowercase, alphanumeric and hyphens only, e.g., "my-benchmark")` + }; + } + + return { valid: true }; +} + +export function checkSuiteExists(suiteName: string): boolean { + const root = findRepoRoot(); + const suitePath = join(root, 'suites', suiteName); + return existsSync(suitePath); +} + +export function checkScenarioExists(suiteName: string, scenarioName: string): boolean { + const root = findRepoRoot(); + const scenarioPath = join(root, 'suites', suiteName, 'scenarios', scenarioName); + return existsSync(scenarioPath); +} + +export function loadScenario(suite: string, scenario: string) { + const root = findRepoRoot(); + const scenarioPath = join(root, 'suites', suite, 'scenarios', scenario, 'scenario.yaml'); + + // Log scenario loading attempt + console.log(chalk.gray('[DEBUG] loadScenario()')); + console.log(chalk.gray(` Suite: ${suite}, Scenario: ${scenario}`)); + console.log(chalk.gray(` Scenario path: ${scenarioPath}`)); + console.log(chalk.gray(` File exists: ${existsSync(scenarioPath)}`)); + + try { + const yamlText = readFileSync(scenarioPath, 'utf8'); + console.log(chalk.gray(` YAML loaded successfully (${yamlText.length} bytes)`)); + const parsed = YAML.parse(yamlText); + console.log(chalk.gray(` YAML parsed successfully`)); + return parsed; + } catch (error) { + console.error(chalk.red(`[DEBUG] Failed to load scenario: ${error instanceof Error ? error.message : String(error)}`)); + throw error; + } +} + +export function getScenarioDir(suite: string, scenario: string) { + const root = findRepoRoot(); + const scenarioDir = join(root, 'suites', suite, 'scenarios', scenario); + + // Log scenario dir resolution + console.log(chalk.gray('[DEBUG] getScenarioDir()')); + console.log(chalk.gray(` Suite: ${suite}, Scenario: ${scenario}`)); + console.log(chalk.gray(` Computed path: ${scenarioDir}`)); + console.log(chalk.gray(` Path exists: ${existsSync(scenarioDir)}`)); + + return scenarioDir; +} + +export function getTierLabel(tier: string): string { + const labels: Record = { + 'L0': 'L0 - Minimal', + 'L1': 'L1 - Basic', + 'L2': 'L2 - Directed', + 'L3': 'L3 - Migration', + 'Lx': 'Lx - Adversarial' + }; + return labels[tier] || tier; +} + +export function getAvailableTiers(suite: string, scenario: string): Array<{value: string, label: string}> { + const root = findRepoRoot(); + const promptDir = join(root, 'suites', suite, 'prompts', scenario); + + if (!existsSync(promptDir)) { + return []; + } + + const files = readdirSync(promptDir); + const tierPattern = /^(L\d+|Lx)(-.*)?\.md$/; + + const tiers = new Set(); + files.forEach(file => { + const match = file.match(tierPattern); + if (match) { + tiers.add(match[1]); + } + }); + + return Array.from(tiers).sort().map(tier => ({ + value: tier, + label: getTierLabel(tier) + })); +} + +export function loadPrompt(suite: string, scenario: string, tier: string): string | null { + const root = findRepoRoot(); + const promptDir = join(root, 'suites', suite, 'prompts', scenario); + + if (!existsSync(promptDir)) { + log.warning(`Prompt directory not found: ${promptDir}`); + return null; + } + + // Look for files that start with the tier (e.g., L1-basic.md, L1.md) + try { + const files = readdirSync(promptDir); + const promptFile = files.find((file: string) => file.startsWith(`${tier}-`) || file === `${tier}.md`); + + if (!promptFile) { + log.warning(`No prompt file found for tier ${tier} in ${promptDir}`); + return null; + } + + const promptPath = join(promptDir, promptFile); + return readFileSync(promptPath, 'utf8'); + } catch (err) { + log.error('Failed to load prompt file:'); + console.error(chalk.dim(err instanceof Error ? err.message : String(err))); + return null; + } +} diff --git a/packages/harness/src/domain/scoring.ts b/packages/harness/src/domain/scoring.ts new file mode 100644 index 0000000..82ebd82 --- /dev/null +++ b/packages/harness/src/domain/scoring.ts @@ -0,0 +1,87 @@ +// ============================================================================ +// SCORING UTILITIES +// ============================================================================ + +export interface SuccessCalculationResult { + isSuccessful: boolean; + successMetric: number; +} + +export interface CommandResult { + type: string; + exitCode: number; +} + +export function computeWeightedTotals( + scores: Record, + scenarioCfg: { rubric_overrides?: { weights?: Record } }, +) { + const baseWeights: Record = { + install_success: 1.5, + tests_nonregression: 2.5, + manager_correctness: 1, + dependency_targets: 2, + integrity_guard: 1.5, + }; + + const overrideWeights = scenarioCfg.rubric_overrides?.weights ?? {}; + + let totalWeight = 0; + let achieved = 0; + + for (const [metric, score] of Object.entries(scores || {})) { + const weight = overrideWeights[metric] ?? baseWeights[metric] ?? 1; + if (weight <= 0) continue; + totalWeight += weight; + achieved += (typeof score === 'number' ? score : 0) * weight; + } + + const weighted = totalWeight > 0 ? (achieved / totalWeight) * 10 : 0; + return { weighted: Number(weighted.toFixed(4)), max: 10 }; +} + +export function calculateSuccess( + commandLog: CommandResult[], + scores: Record, + scenario: any +): SuccessCalculationResult { + + // Validation score (commands passed / total commands) + const passedCommands = commandLog.filter(cmd => cmd.exitCode === 0).length; + const validationScore = commandLog.length > 0 + ? passedCommands / commandLog.length + : 0; + + // Critical commands (install, build, test must pass) + const criticalCommands = ['install', 'test']; // Note: 'build' is not in CommandKind, using 'test' as critical + const criticalPassed = criticalCommands.every(cmd => { + const result = commandLog.find(c => c.type === cmd); + return result && result.exitCode === 0; + }); + + // Evaluator average score + const evaluatorScore = Object.values(scores).length > 0 + ? Object.values(scores).reduce((sum, s) => sum + s, 0) / Object.values(scores).length + : 0; + + // LLM judge score (if available) + const llmJudgeScore = scores.llm_judge || 0; + + // Weighted metric (from scenario.yaml or default) + const weights = scenario.success_weights || { + validation: 0.4, + evaluators: 0.3, + llm_judge: 0.3 + }; + + const successMetric = ( + validationScore * weights.validation + + evaluatorScore * weights.evaluators + + llmJudgeScore * weights.llm_judge + ); + + // Success criteria: critical commands must pass AND success metric >= 0.7 + const isSuccessful = criticalPassed && successMetric >= 0.7; + + return { isSuccessful, successMetric }; +} diff --git a/packages/harness/src/domain/warmup.ts b/packages/harness/src/domain/warmup.ts new file mode 100644 index 0000000..9c667d7 --- /dev/null +++ b/packages/harness/src/domain/warmup.ts @@ -0,0 +1,218 @@ +import { existsSync, readdirSync, mkdirSync, rmSync, unlinkSync, statSync } from 'node:fs'; +import { join } from 'node:path'; +import chalk from 'chalk'; +import type { AgentRequest } from '../../../agent-adapters/src/index.ts'; +import { getScenarioDir } from './scenario.ts'; + +// ============================================================================ +// WARMUP EXECUTION +// ============================================================================ + +export interface WarmupResult { + success: boolean; + error?: string; + controlPath?: string; + controlContents?: string[]; + agentError?: string; +} + +export async function executeWarmup( + suite: string, + scenario: string, + scenarioCfg: any, + createAgentAdapter: (agentName: string, model?: string, specialistName?: string) => Promise, + quiet: boolean = false +): Promise { + // Check if warmup is enabled + if (!scenarioCfg.warmup || !scenarioCfg.warmup.enabled) { + return { success: true }; // No warmup configured, continue normally + } + + const warmupCfg = scenarioCfg.warmup; + const scenarioDir = getScenarioDir(suite, scenario); + const workingDir = join(scenarioDir, warmupCfg.working_dir || './repo-fixture'); + + if (!quiet) { + console.log(chalk.blue('🔥 Executing warmup phase...')); + console.log(chalk.blue(`[Warmup] Working directory: ${workingDir}`)); + console.log(chalk.blue(`[Warmup] Expected control folder: ${join(workingDir, 'control')}`)); + } + + // Create working directory if it doesn't exist, or clean it if it does + if (existsSync(workingDir)) { + // Clean existing directory + try { + const files = readdirSync(workingDir); + if (!quiet) { + console.log(chalk.blue(`[Warmup] Cleaning existing working directory...`)); + console.log(chalk.blue(`[Warmup] Removing: [${files.join(', ')}]`)); + } + for (const file of files) { + const filePath = join(workingDir, file); + const stat = statSync(filePath); + if (stat.isDirectory()) { + rmSync(filePath, { recursive: true, force: true }); + } else { + unlinkSync(filePath); + } + } + } catch (err) { + return { success: false, error: `Failed to clean warmup directory: ${err}` }; + } + } else { + mkdirSync(workingDir, { recursive: true }); + if (!quiet) { + console.log(chalk.green(`[Warmup] Created working directory: ${workingDir}`)); + } + } + + // Execute based on warmup type + if (warmupCfg.type === 'agent') { + // Agent-driven warmup + const agentCfg = warmupCfg.agent; + const provider = agentCfg.provider || 'openrouter'; + const model = agentCfg.model; + const prompt = agentCfg.prompt; + + if (!prompt) { + return { success: false, error: 'Warmup agent prompt is required' }; + } + + if (!quiet) { + console.log(chalk.blue(`[Warmup] Provider: ${provider}`)); + console.log(chalk.blue(`[Warmup] Model: ${model || 'default'}`)); + } + + try { + // Create agent adapter + const agentAdapter = await createAgentAdapter(provider, model); + + // Build the request + const messages: Array<{ role: 'system' | 'user'; content: string }> = [ + { + role: 'user' as const, + content: prompt + } + ]; + + const request: AgentRequest = { + messages, + workspaceDir: workingDir, + }; + + // Add tools if agent supports them + if (provider === 'anthropic' || provider === 'openrouter') { + const { createWorkspaceToolHandlers, getAllWorkspaceTools } = await import('../runtime/workspace-tools.ts'); + const workspaceHandlers = createWorkspaceToolHandlers(workingDir); + const tools = getAllWorkspaceTools(); + + request.tools = tools; + request.toolHandlers = workspaceHandlers; + } + + // Execute the agent + if (!quiet) { + console.log(chalk.blue(`[Warmup] Starting agent execution...`)); + console.log(chalk.blue(`[Warmup] Agent: ${agentAdapter.constructor.name}`)); + console.log(chalk.blue(`[Warmup] Workspace: ${request.workspaceDir}`)); + } + + const response = await agentAdapter.send(request); + + if (!quiet) { + console.log(chalk.green(`[Warmup] ✓ Agent execution completed`)); + console.log(chalk.blue(`[Warmup] Tool calls made: ${response.toolCalls || 0}`)); + console.log(chalk.blue(`[Warmup] Tokens used: ${response.tokensIn} in / ${response.tokensOut} out`)); + + if (response.content) { + const preview = response.content.substring(0, 150).replace(/\n/g, ' '); + console.log(chalk.gray(`[Warmup] Final response: ${preview}${response.content.length > 150 ? '...' : ''}`)); + } + + // Warn if no tools were called + if (!response.toolCalls || response.toolCalls === 0) { + console.warn(chalk.yellow(`[Warmup] ⚠️ Warning: Agent did not use any tools`)); + console.warn(chalk.yellow(`[Warmup] Agent may not have understood the task requires tool use`)); + } + } + + // Validate control folder was created + const controlPath = join(workingDir, 'control'); + if (!quiet) { + console.log(chalk.blue(`[Warmup] Validating control folder at: ${controlPath}`)); + } + + if (!existsSync(controlPath)) { + console.error(chalk.red(`[Warmup] ❌ Control folder not found`)); + console.error(chalk.red(`[Warmup] Expected at: ${controlPath}`)); + return { + success: false, + error: `Control folder not created at ${controlPath}`, + controlPath + }; + } + + const controlContents = readdirSync(controlPath); + if (!quiet) { + console.log(chalk.green(`[Warmup] ✓ Control folder created successfully`)); + console.log(chalk.blue(`[Warmup] Contents: [${controlContents.join(', ')}]`)); + } + + if (controlContents.length === 0) { + console.warn(chalk.yellow(`[Warmup] ⚠ Warning: Control folder is empty`)); + return { + success: false, + error: `Control folder created but is empty`, + controlPath, + controlContents + }; + } + + return { + success: true, + controlPath, + controlContents + }; + + } catch (err: any) { + console.error(chalk.red(`[Warmup] ❌ Exception during agent execution`)); + console.error(chalk.red(`[Warmup] ${err.message}`)); + if (err.stack) { + console.error(chalk.gray(err.stack)); + } + return { + success: false, + error: `Exception: ${err.message}`, + agentError: err.message + }; + } + } else if (warmupCfg.type === 'scripted') { + // Scripted warmup + const commands = warmupCfg.commands || []; + + if (!quiet) console.log(chalk.blue(` Running ${commands.length} warmup commands...`)); + + for (const cmdCfg of commands) { + const cmd = cmdCfg.cmd; + const description = cmdCfg.description || cmd; + + if (!quiet) console.log(chalk.gray(` → ${description}`)); + + try { + const { execSync } = await import('child_process'); + execSync(cmd, { + cwd: workingDir, + stdio: quiet ? 'ignore' : 'inherit', + encoding: 'utf8' + }); + } catch (err: any) { + return { success: false, error: `Warmup command failed: ${cmd}\n${err.message || err}` }; + } + } + + if (!quiet) console.log(chalk.green(' ✓ Warmup commands completed')); + return { success: true }; + } else { + return { success: false, error: `Unknown warmup type: ${warmupCfg.type}` }; + } +} diff --git a/packages/harness/src/execution/benchmark.ts b/packages/harness/src/execution/benchmark.ts new file mode 100644 index 0000000..55b9f1d --- /dev/null +++ b/packages/harness/src/execution/benchmark.ts @@ -0,0 +1,566 @@ +/** + * Core Benchmark Execution Logic + * + * This module contains the main benchmark runner function that orchestrates + * the entire benchmark execution pipeline from setup through evaluation. + */ + +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; +import chalk from 'chalk'; +import type { AgentRequest } from '../../../agent-adapters/src/index.ts'; +import { BenchmarkLogger } from '@ze/database'; +import { runEvaluators } from '../../../evaluators/src/index.ts'; +import { runValidationCommands } from '../runtime/validation.ts'; +import { buildDiffArtifacts } from '../runtime/diff.ts'; +import { detectPackageManager, extractPackageManagerFromCommand, extractTestResults } from '../runtime/extractors.ts'; +import { Oracle } from '../runtime/oracle.ts'; +import { createAskUserToolDefinition, createAskUserHandler } from '../runtime/ask-user-tool.ts'; +import { getAllWorkspaceTools, createWorkspaceToolHandlers } from '../runtime/workspace-tools.ts'; + +// Import helper functions from their respective modules +import { loadScenario, loadPrompt, getScenarioDir } from '../domain/scenario.ts'; +import { executeWarmup } from '../domain/warmup.ts'; +import { prepareWorkspaceFromFixture, findWorkspaceRoot } from '../lib/workspace-utils.ts'; +import { createAgentAdapter } from '../domain/agent.ts'; +import { computeWeightedTotals, calculateSuccess } from '../domain/scoring.ts'; +import { displayLLMJudgeScores, createProgress, updateProgress, completeProgress } from '../lib/display.ts'; +import { TABLE_WIDTH, SCORE_THRESHOLDS } from '../lib/constants.ts'; + +// Get workspace root for specialist template resolution +const workspaceRoot = findWorkspaceRoot(process.cwd()); + +// Additional constants (keeping the ones not in constants.ts) +const ADDITIONAL_THRESHOLDS = { + EXCELLENT: 90, + GOOD: 70, + NEEDS_WORK: 60 +} as const; + +/** + * Execute a complete benchmark run + * + * This is the main orchestration function that runs through all stages: + * 1. Setup - Load scenario and prompt configuration + * 2. Warmup - Execute optional warmup phase + * 3. Workspace - Prepare test workspace from fixture + * 4. Agent - Execute the AI agent with the prompt + * 5. Validation - Run validation commands + * 6. Evaluation - Compute scores using evaluators + * 7. Results - Display and save results + * + * @param suite - The benchmark suite name + * @param scenario - The scenario name within the suite + * @param tier - The prompt tier (L0, L1, etc.) + * @param agent - The agent adapter to use (anthropic, openrouter, etc.) + * @param model - Optional specific model identifier + * @param batchId - Optional batch ID for grouping runs + * @param quiet - Whether to suppress verbose output + * @param specialist - Optional specialist template to use + */ +export async function executeBenchmark( + suite: string, + scenario: string, + tier: string, + agent: string, + model?: string, + batchId?: string, + quiet?: boolean, + specialist?: string, + skipWarmup?: boolean +) { + // Initialize logger + const logger = BenchmarkLogger.getInstance(); + + // Determine the agent name to log (will be updated if specialist is used) + let agentName = agent; + const runId = logger.startRun(suite, scenario, tier, agentName, model, batchId, !!specialist); + const startTime = Date.now(); + + // Timeout watchdog based on scenario timeout_minutes (default 60) + let timeoutId: NodeJS.Timeout | null = null; + + // Initialize progress tracker (only if not in quiet mode) + const progress = quiet ? null : createProgress(); + + try { + // Stage 1: Setup + if (progress) updateProgress(progress, 1, 'Loading scenario configuration'); + const scenarioCfg = loadScenario(suite, scenario); + + // Start timeout watchdog after loading scenario config + const scenarioTimeoutMin = Number(scenarioCfg.timeout_minutes || 60); + const timeoutMs = Math.max(1, scenarioTimeoutMin) * 60 * 1000; + timeoutId = setTimeout(() => { + try { + logger.markRunIncomplete(`Run exceeded timeout (${scenarioTimeoutMin} minutes)`, 'timeout'); + if (!quiet) console.log(chalk.yellow(`⚠ Run timed out after ${scenarioTimeoutMin} minutes`)); + } catch {} + }, timeoutMs); + + if (progress) updateProgress(progress, 1, 'Loading prompt'); + const promptContent = loadPrompt(suite, scenario, tier); + + // Early failure check: prompt missing for non-echo agents + if (!promptContent && agent !== 'echo') { + logger.failRun('Prompt file not found', 'prompt'); + if (!quiet) console.log(chalk.red('✗ Prompt file not found')); + if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: Prompt file not found`)); + return; + } + + // Stage 1.5: Warmup (if configured and not skipped) + if (!skipWarmup) { + if (progress) updateProgress(progress, 1, 'Running warmup phase'); + if (!quiet) { + console.log(chalk.blue('[Benchmark] Running warmup phase...')); + } + const warmupResult = await executeWarmup(suite, scenario, scenarioCfg, createAgentAdapter, quiet); + if (!warmupResult.success) { + logger.failRun(`Warmup failed: ${warmupResult.error}`, 'warmup'); + if (!quiet) { + console.log(chalk.red(`[Benchmark] ✗ Warmup failed: ${warmupResult.error}`)); + if (warmupResult.agentError) { + console.log(chalk.red(`[Benchmark] Agent error: ${warmupResult.agentError}`)); + } + } + if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: Warmup failed`)); + return; + } + if (!quiet) { + console.log(chalk.green('[Benchmark] ✓ Warmup completed successfully')); + } + } else { + if (!quiet) { + console.log(chalk.yellow('[Benchmark] Skipping warmup (--skip-warmup flag set)')); + console.log(chalk.gray('[Benchmark] Note: Not validating control folder structure - it varies by scenario')); + } + } + + // Stage 2: Workspace + if (progress) updateProgress(progress, 2, 'Preparing workspace'); + const workspacePrep = prepareWorkspaceFromFixture(suite, scenario, getScenarioDir); + + // Early failure check: workspace preparation failed + if (!workspacePrep) { + logger.failRun('Workspace preparation failed', 'workspace'); + if (!quiet) console.log(chalk.red('✗ Workspace preparation failed')); + if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: Workspace preparation failed`)); + return; + } + + const workspaceDir = workspacePrep.workspaceDir; + const fixtureDir = workspacePrep.fixtureDir; + + console.log(chalk.magenta('🔍 DEBUG: Workspace prepared successfully')); + console.log(chalk.magenta(`🔍 DEBUG: workspaceDir = ${workspaceDir}`)); + console.log(chalk.magenta(`🔍 DEBUG: fixtureDir = ${fixtureDir}`)); + + // Initialize result structure + const result = { + suite, + scenario, + tier, + agent, + model: model || 'default', + agent_response: '', + scores: { + install_success: 0, + tests_nonregression: 0, + manager_correctness: 0, + dependency_targets: 0, + integrity_guard: 0, + }, + totals: { weighted: 0, max: 10 }, + telemetry: { + toolCalls: 0, + tokens: { in: 0, out: 0 }, + cost_usd: 0, + workspaceDir + } + }; + + + // Stage 3: Agent Execution + console.log(chalk.magenta('🔍 DEBUG: Stage 3 check - promptContent exists:', !!promptContent, 'agent:', agent)); + if (promptContent && agent !== 'echo') { + console.log(chalk.magenta('🔍 DEBUG: Entering Stage 3 - Agent Execution')); + if (progress) updateProgress(progress, 3, 'Agent working...'); + try { + console.log(chalk.magenta('🔍 DEBUG: About to create agent adapter')); + // Create agent adapter + const agentAdapter = await createAgentAdapter(agent, model, specialist, workspaceRoot); + + // Update the logged agent name with the actual adapter name + // (for specialists, this will be "specialist:template-name:base-adapter") + logger.updateAgent(agentAdapter.name, runId); + + // Show selected model info - ALWAYS for OpenRouter + if (agent === 'openrouter' && 'getModel' in agentAdapter) { + const adapterModel = (agentAdapter as any).getModel(); + const modelSource = 'getModelSource' in agentAdapter + ? (agentAdapter as any).getModelSource() + : 'unknown'; + + if (modelSource === 'default') { + console.log(chalk.yellow(` ⚠️ Using default model: ${chalk.cyan(adapterModel)}`)); + console.log(chalk.gray(` Tip: Search and select a model in interactive mode or use --model flag`)); + } else if (modelSource === 'environment') { + console.log(chalk.blue(` ℹ️ Using model from environment: ${chalk.cyan(adapterModel)}`)); + } else if (model && modelSource === 'parameter') { + console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(model)}`)); + + // Verify match + if (adapterModel !== model) { + console.log(chalk.yellow(` ⚠️ Warning: Model mismatch - requested: ${model}, adapter: ${adapterModel}`)); + } else { + console.log(chalk.green(` ✅ Model confirmed: ${adapterModel}`)); + } + } else { + console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(adapterModel)}`)); + } + } else if (model && agent !== 'openrouter') { + // For non-OpenRouter agents, show model if provided + console.log(chalk.gray(` 📋 Using model: ${chalk.cyan(model)}`)); + } + + // Agent info is shown in progress bar + + // Load oracle if available + let oracle: Oracle | undefined; + const oracleFile = scenarioCfg.oracle?.answers_file; + if (oracleFile) { + const scenarioDir = getScenarioDir(suite, scenario); + const oraclePath = join(scenarioDir, oracleFile); + if (existsSync(oraclePath)) { + oracle = new Oracle(oraclePath); + if (progress) updateProgress(progress, 3, 'Agent with oracle support enabled'); + } + } + + // Build the request messages + // For vanilla runs: send ONLY user message (no system prompt for true baseline testing) + // For specialist runs: SpecialistAdapter will add the specialist system prompt from template + const messages: Array<{ role: 'system' | 'user'; content: string }> = [ + { + role: 'user' as const, + content: promptContent + } + ]; + + // Build the request + const request: AgentRequest = { + messages, + ...(workspaceDir && { workspaceDir }), + }; + + // Add tools if agent supports them (Anthropic and OpenRouter) + if ((agent === 'anthropic' || agent === 'openrouter') && workspaceDir) { + // Create workspace tool handlers + const workspaceHandlers = createWorkspaceToolHandlers(workspaceDir); + + // Start with workspace tools + const tools = getAllWorkspaceTools(); + const toolHandlers = workspaceHandlers; + + // Add askUser tool if oracle is available + if (oracle) { + tools.push(createAskUserToolDefinition()); + toolHandlers.set('askUser', createAskUserHandler(oracle)); + // Tools: readFile, writeFile, runCommand, listFiles, askUser + } else { + // Tools: readFile, writeFile, runCommand, listFiles + } + + // Convert tools to adapter-specific format + if (agent === 'openrouter') { + // Convert ToolDefinition to OpenRouter format + (request as any).tools = tools.map(tool => ({ + type: 'function', + function: { + name: tool.name, + description: tool.description, + parameters: tool.input_schema // Map input_schema → parameters + } + })); + } else { + // Anthropic uses ToolDefinition format directly + request.tools = tools; + } + + request.toolHandlers = toolHandlers; + } + + // Execute agent request + const response = await agentAdapter.send(request); + + // Extract the prompt being sent for logging + // For specialist adapters, get the transformed messages (with system prompt) + // For vanilla adapters, use the original request messages + let messagesForLogging = request.messages; + if ('getLastTransformedMessages' in agentAdapter) { + const transformed = (agentAdapter as any).getLastTransformedMessages(); + if (transformed) { + messagesForLogging = transformed; + } + } + const promptSent = JSON.stringify(messagesForLogging); + + // Show summary after agent completes + console.log(chalk.gray(` ✓ Tokens: ${response.tokensIn || 0} in, ${response.tokensOut || 0} out | Cost: $${(response.costUsd || 0).toFixed(4)}`)); + + // Update result with agent response + result.agent_response = response.content; + result.telemetry.tokens.in = response.tokensIn || 0; + result.telemetry.tokens.out = response.tokensOut || 0; + result.telemetry.cost_usd = response.costUsd || 0; + result.telemetry.toolCalls = response.toolCalls ?? 0; + + // Log telemetry to database + const duration = Date.now() - startTime; + logger.logTelemetry( + response.toolCalls ?? 0, + response.tokensIn || 0, + response.tokensOut || 0, + response.costUsd || 0, + duration, + workspaceDir, + promptSent + ); + + // Log oracle usage if available + if (oracle) { + const questionLog = oracle.getQuestionLog(); + if (questionLog.length > 0) { + // Oracle questions logged + (result as any).oracle_questions = questionLog; + } + } + + // Telemetry is shown in final results + + } catch (error) { + console.log(chalk.magenta('🔍 DEBUG: Agent execution threw error:', error)); + logger.failRun(error instanceof Error ? error.message : String(error), 'agent'); + if (!quiet) console.log(chalk.red('✗ Agent execution failed')); + if (quiet) console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: ${error instanceof Error ? error.message : String(error)}`)); + return; // Early exit - don't continue to evaluation + } + } else if (!promptContent) { + console.log(chalk.magenta('🔍 DEBUG: Skipping agent execution - no prompt content')); + } else { + console.log(chalk.magenta('🔍 DEBUG: Skipping agent execution - using echo agent')); + } + + // Stage 4: Validation + console.log(chalk.magenta('🔍 DEBUG: Stage 4 - Validation starting')); + if (progress) updateProgress(progress, 4, 'Running validation commands'); + const commandLog = workspaceDir ? runValidationCommands(workspaceDir, scenarioCfg.validation?.commands) : []; + const diffArtifacts = workspaceDir && fixtureDir ? buildDiffArtifacts(fixtureDir, workspaceDir) : { diffSummary: [], depsDelta: [] }; + + const passedCommands = commandLog.filter(cmd => cmd.exitCode === 0).length; + if (!quiet) console.log(chalk.gray(` ✓ ${passedCommands}/${commandLog.length} commands passed`)); + + // Stage 5: Evaluation + console.log(chalk.magenta('🔍 DEBUG: Stage 5 - Evaluation starting')); + if (progress) updateProgress(progress, 5, 'Computing scores'); + + try { + if (workspaceDir) { + // Load benchmark config to get suitesDir + const { loadBenchmarkConfig } = await import('../lib/config.ts'); + const config = loadBenchmarkConfig(); + + // Calculate reference path if scenario has reference_path + let referencePath: string | undefined; + if (scenarioCfg.reference_path) { + const scenarioDir = getScenarioDir(suite, scenario); + referencePath = join(scenarioDir, scenarioCfg.reference_path); + } + + // Actually run evaluators + const ctx = { + scenario: scenarioCfg, + workspaceDir, + suitesDir: config.suitesDir, + referencePath, + agentResponse: result.agent_response, + commandLog, + diffSummary: diffArtifacts.diffSummary, + depsDelta: diffArtifacts.depsDelta, + }; + const { scoreCard, results: evaluatorResults } = await runEvaluators(ctx); + result.scores = { ...result.scores, ...scoreCard }; + result.totals = computeWeightedTotals(result.scores, scenarioCfg); + (result as any).evaluator_results = evaluatorResults; + (result as any).diff_summary = diffArtifacts.diffSummary; + (result as any).deps_delta = diffArtifacts.depsDelta; + + // Log evaluation results to database + for (const evalResult of evaluatorResults) { + logger.logEvaluation( + evalResult.name, + evalResult.score, + 1.0, // max score + evalResult.details + ); + } + + // Show evaluator summary + const avgScore = Object.values(scoreCard).reduce((sum, score) => sum + (score as number), 0) / Object.keys(scoreCard).length; + if (!quiet) console.log(chalk.gray(` ✓ Average score: ${(avgScore * 100).toFixed(1)}%`)); + } + } catch (e) { + // Evaluator run failed + } + + // Complete the run in database + const totalScore = Object.values(result.scores || {}).reduce((sum, score) => sum + (typeof score === 'number' ? score : 0), 0) / Object.keys(result.scores || {}).length; + + // Calculate success based on validation commands and evaluator scores + const { isSuccessful, successMetric } = calculateSuccess(commandLog, result.scores || {}, scenarioCfg); + + // Extract package manager and test results + let packageManager: string | undefined; + if (workspaceDir) { + packageManager = detectPackageManager(workspaceDir); + if (packageManager === 'unknown') { + packageManager = extractPackageManagerFromCommand(commandLog); + } + } + + const testResults = extractTestResults(commandLog); + const testResultsJson = testResults ? JSON.stringify(testResults) : undefined; + + logger.completeRun( + totalScore, + result.totals?.weighted, + { + diffSummary: diffArtifacts.diffSummary, + depsDelta: diffArtifacts.depsDelta, + oracleQuestions: (result as any).oracle_questions + }, + isSuccessful, + successMetric, + packageManager, + testResultsJson + ); + + // Stage 6: Results + if (progress) updateProgress(progress, 6, 'Preparing results'); + + if (progress) completeProgress(progress); + + // Display results in table format (only if not in quiet mode) + const duration = (Date.now() - startTime) / 1000; + const weightedScore = result.totals?.weighted || 0; + + // In quiet mode, show compact one-line output + if (quiet) { + const status = isSuccessful ? chalk.green('✓') : chalk.red('✗'); + const modelStr = model ? ` [${model}]` : ''; + const successStr = isSuccessful ? 'SUCCESS' : 'FAILED'; + console.log(`${status} ${suite}/${scenario} (${tier}) ${agent}${modelStr} - ${weightedScore.toFixed(2)}/10 [${successStr}]`); + return; + } + + console.log(`\n${chalk.bold.underline('Benchmark Results')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Agent:')} ${chalk.cyan(agent.padEnd(15))} ${chalk.bold('Tier:')} ${chalk.cyan(tier.padEnd(8))} ${chalk.bold('Duration:')} ${chalk.blue(duration.toFixed(2) + 's')} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + console.log(`│ ${chalk.bold('Score (mean ± σ):')} ${chalk.green(weightedScore.toFixed(4))} ± ${chalk.green('0.0000')} ${chalk.gray('(out of 10.0)')} │`); + console.log(`│ ${chalk.bold('Range (min ... max):')} ${chalk.green(weightedScore.toFixed(4))} ${chalk.white('...')} ${chalk.red(weightedScore.toFixed(4))} ${chalk.gray('(1 run)')} │`); + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + // Print evaluation breakdown in table format + if (result.scores) { + console.log(`\n${chalk.bold.underline('Evaluation Breakdown')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Evaluator'.padEnd(25))} ${chalk.bold('Score'.padEnd(10))} ${chalk.bold('Status'.padEnd(15))} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + + Object.entries(result.scores).forEach(([name, score]) => { + const percent = (score as number) * 100; + const color = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; + const status = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'Excellent' : percent >= SCORE_THRESHOLDS.GOOD ? 'Good' : 'Needs Work'; + const statusColor = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; + + // Special handling for LLM Judge Evaluator + const displayName = name === 'LLMJudgeEvaluator' ? 'LLM Judge' : name; + + console.log(`│ ${chalk.cyan(displayName.padEnd(25))} ${chalk[color](score.toFixed(4).padEnd(10))} ${chalk[statusColor](status.padEnd(15))} │`); + }); + + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + // Show detailed LLM Judge scores if available + displayLLMJudgeScores(result); + } + + // Print telemetry in table format + if (result.telemetry) { + console.log(`\n${chalk.bold.underline('Telemetry')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Metric'.padEnd(20))} ${chalk.bold('Value'.padEnd(20))} ${chalk.bold('Unit'.padEnd(15))} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + console.log(`│ ${chalk.cyan('Tool Calls'.padEnd(20))} ${chalk.green((result.telemetry.toolCalls || 0).toString().padEnd(20))} ${chalk.gray('calls'.padEnd(15))} │`); + console.log(`│ ${chalk.cyan('Tokens In'.padEnd(20))} ${chalk.green((result.telemetry.tokens?.in || 0).toString().padEnd(20))} ${chalk.gray('tokens'.padEnd(15))} │`); + console.log(`│ ${chalk.cyan('Tokens Out'.padEnd(20))} ${chalk.green((result.telemetry.tokens?.out || 0).toString().padEnd(20))} ${chalk.gray('tokens'.padEnd(15))} │`); + console.log(`│ ${chalk.cyan('Cost'.padEnd(20))} ${chalk.green(`$${(result.telemetry.cost_usd || 0).toFixed(6)}`.padEnd(20))} ${chalk.gray('USD'.padEnd(15))} │`); + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + } + + // Show database summary in table format + try { + const stats = logger.getStats(); + console.log(`\n${chalk.bold.underline('Database Summary')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Metric'.padEnd(25))} ${chalk.bold('Value'.padEnd(20))} ${chalk.bold('Status'.padEnd(10))} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + console.log(`│ ${chalk.cyan('Total Runs'.padEnd(25))} ${chalk.blue(stats.totalRuns.toString().padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); + console.log(`│ ${chalk.cyan('Success Rate'.padEnd(25))} ${chalk.green(`${(stats.successRate * 100).toFixed(1)}%`.padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); + console.log(`│ ${chalk.cyan('Average Score'.padEnd(25))} ${chalk.green(stats.averageWeightedScore.toFixed(4).padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); + console.log(`│ ${chalk.cyan('Database'.padEnd(25))} ${chalk.blue('benchmark-report/public/benchmarks.db'.padEnd(20))} ${chalk.green('✓'.padEnd(10))} │`); + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + } catch (dbError) { + console.log(chalk.yellow('Database query failed:')); + console.error(chalk.dim(dbError instanceof Error ? dbError.message : String(dbError))); + } + + // Results are saved to database only (benchmarks.db is the single source of truth) + console.log(`\n${chalk.green('✓')} Results saved to database`); + + // Note: Database is now created directly in public/ directory + + // Show completion outro + console.log(`\n${chalk.green('✓')} Benchmark completed successfully`); + + } catch (error) { + // Catch-all for unexpected errors + const errorMessage = error instanceof Error ? error.message : String(error); + const errorStack = error instanceof Error ? error.stack : undefined; + + logger.failRun(errorMessage, 'unknown'); + + if (!quiet) { + console.log(chalk.red('✗ Unexpected error')); + console.error(chalk.red('\nError details:')); + console.error(chalk.red(errorMessage)); + if (errorStack) { + console.error(chalk.dim('\nStack trace:')); + console.error(chalk.dim(errorStack)); + } + } + if (quiet) { + console.log(chalk.red(`[X] ${suite}/${scenario} (${tier}) ${agent}${model ? ` [${model}]` : ''} - FAILED: ${errorMessage}`)); + } + } finally { + if (progress) completeProgress(progress); + // Clear timeout watchdog if set + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + } +} diff --git a/packages/harness/src/interactive/benchmark.ts b/packages/harness/src/interactive/benchmark.ts new file mode 100644 index 0000000..fe8ebf4 --- /dev/null +++ b/packages/harness/src/interactive/benchmark.ts @@ -0,0 +1,639 @@ +import { existsSync, readdirSync } from 'node:fs'; +import { join } from 'node:path'; +import { select, multiselect, isCancel, cancel, text } from '@clack/prompts'; +import chalk from 'chalk'; +import { BenchmarkLogger } from '@ze/database'; +import { OpenRouterAPI } from '../lib/openrouter-api.ts'; +import { log } from '@clack/prompts'; +import { executeBenchmark } from '../execution/benchmark.ts'; +import { findRepoRoot } from '../lib/workspace-utils.ts'; +import { getAvailableTiers, getTierLabel, loadScenario } from '../domain/scenario.ts'; +import { createTitle } from '../lib/display.ts'; +import { executeWarmup } from '../domain/warmup.ts'; +import { createAgentAdapter } from '../domain/agent.ts'; + +const TABLE_WIDTH = 60; + +// ============================================================================ +// PARALLEL EXECUTION HELPER +// ============================================================================ + +export async function executeWithConcurrency( + items: T[], + concurrency: number, + executor: (item: T, index: number) => Promise +): Promise { + const results: Promise[] = []; + let currentIndex = 0; + + async function runNext(): Promise { + const index = currentIndex++; + if (index >= items.length) return; + + await executor(items[index], index); + await runNext(); + } + + // Start initial batch of concurrent executions + for (let i = 0; i < Math.min(concurrency, items.length); i++) { + results.push(runNext()); + } + + await Promise.all(results); +} + +// ============================================================================ +// MULTIPLE BENCHMARKS EXECUTION +// ============================================================================ + +export async function executeMultipleBenchmarks( + suites: string[], + scenarios: string[], + tiers: string[], + agents: string[], + models: (string | undefined)[] +) { + // Initialize batch tracking + const logger = BenchmarkLogger.getInstance(); + const batchId = logger.startBatch(); + + // Calculate total combinations + const combinations: Array<{ + suite: string; + scenario: string; + tier: string; + agent: string; + model?: string; + }> = []; + + for (const suite of suites) { + for (const scenario of scenarios) { + const availableTiers = getAvailableTiers(suite, scenario); + const availableTierValues = availableTiers.map(t => t.value); + const validTiers = tiers.filter(tier => availableTierValues.includes(tier)); + + if (validTiers.length === 0) { + console.log(chalk.yellow(`⚠ Skipping ${suite}/${scenario}: no valid tiers (available: ${availableTierValues.join(', ')})`)); + continue; + } + + // Log if some tiers are being skipped + const skippedTiers = tiers.filter(tier => !availableTierValues.includes(tier)); + if (skippedTiers.length > 0) { + console.log(chalk.gray(` Skipping tiers for ${suite}/${scenario}: ${skippedTiers.join(', ')}`)); + } + + for (const tier of validTiers) { + for (const agent of agents) { + // Handle model selection per agent + const agentModels = (agent === 'anthropic' || agent === 'claude-code' || agent === 'openrouter') + ? models.filter(m => m !== undefined) + : [undefined]; + + for (const model of agentModels) { + combinations.push({ suite, scenario, tier, agent, model }); + } + } + } + } + } + + // Automatically determine parallel execution based on number of benchmarks + const useParallel = combinations.length >= 3; // Enable parallel for 3+ benchmarks + let concurrency = 3; + + if (useParallel) { + // Smart concurrency based on number of benchmarks + if (combinations.length <= 5) { + concurrency = 2; // Conservative for small batches + } else if (combinations.length <= 15) { + concurrency = 3; // Balanced for medium batches + } else if (combinations.length <= 30) { + concurrency = 5; // Aggressive for large batches + } else { + concurrency = 8; // Maximum for very large batches + } + } + + // Show summary + console.log(chalk.bold.underline(`\nRunning ${combinations.length} benchmark(s):`)); + if (useParallel) { + console.log(chalk.gray(`Parallel execution with concurrency: ${concurrency}`)); + } + + // Execute warmup once per unique suite/scenario before running benchmarks + const uniqueScenarios = new Set(combinations.map(c => `${c.suite}/${c.scenario}`)); + if (uniqueScenarios.size > 0) { + console.log(chalk.blue('\n🔥 Running warmup phase for scenarios...')); + for (const scenarioKey of uniqueScenarios) { + const [suite, scenario] = scenarioKey.split('/'); + try { + const scenarioCfg = loadScenario(suite, scenario); + const warmupResult = await executeWarmup(suite, scenario, scenarioCfg, createAgentAdapter, true); + if (!warmupResult.success) { + console.log(chalk.yellow(`⚠️ Warmup for ${scenarioKey}: ${warmupResult.error || 'failed'}`)); + } else { + console.log(chalk.green(`✓ Warmup completed for ${scenarioKey}`)); + } + } catch (error) { + console.log(chalk.yellow(`⚠️ Warmup error for ${scenarioKey}: ${error instanceof Error ? error.message : String(error)}`)); + } + } + console.log(); + } + + // Track batch statistics + let successfulRuns = 0; + let totalScore = 0; + let totalWeightedScore = 0; + const startTime = Date.now(); + + if (useParallel) { + // Parallel execution + await executeWithConcurrency( + combinations, + concurrency, + async (combo, i) => { + const { suite, scenario, tier, agent, model } = combo; + console.log(`${chalk.bold.cyan(`[${i + 1}/${combinations.length}]`)} ${suite}/${scenario} ${chalk.gray(`(${tier}) ${agent}${model ? ` [${model}]` : ''}`)}`); + await executeBenchmark(suite, scenario, tier, agent, model, batchId, true, undefined, true); // quiet mode, skip warmup + } + ); + } else { + // Sequential execution + for (let i = 0; i < combinations.length; i++) { + const { suite, scenario, tier, agent, model } = combinations[i]; + + console.log(`${chalk.bold.cyan(`[${i + 1}/${combinations.length}]`)} ${suite}/${scenario} ${chalk.gray(`(${tier}) ${agent}${model ? ` [${model}]` : ''}`)}`); + + await executeBenchmark(suite, scenario, tier, agent, model, batchId, true, undefined, true); // quiet mode, skip warmup + } + } + + // Complete batch tracking + const endTime = Date.now(); + const duration = endTime - startTime; + + // Calculate batch statistics + const batchStats = logger.getBatchDetails(batchId); + if (batchStats) { + // Calculate successful runs directly from individual runs in the batch + // This ensures we use the new is_successful field + successfulRuns = logger.getBatchSuccessfulRunsCount(batchId); + + // Calculate average scores directly from individual runs in the batch + const scoreStats = logger.getBatchScoreStats(batchId); + totalScore = scoreStats.avgScore; + totalWeightedScore = scoreStats.avgWeightedScore; + } + + logger.completeBatch(batchId, { + totalRuns: combinations.length, + successfulRuns, + avgScore: totalScore, + avgWeightedScore: totalWeightedScore, + metadata: { + suites, + scenarios, + tiers, + agents, + models: models.filter(m => m !== undefined), + duration + } + }); + + // Note: Database is now created directly in public/ directory + + // Get comprehensive batch analytics + const analytics = logger.getBatchAnalytics(batchId); + + // Show batch summary header + console.log('\n' + chalk.bold.underline('Batch Summary')); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Batch ID:')} ${chalk.dim(batchId.substring(0, 8))}...`); + + // Show model if all runs used the same model + const uniqueModels = [...new Set(combinations.map(c => c.model).filter(m => m))]; + if (uniqueModels.length === 1) { + console.log(`│ ${chalk.bold('Model:')} ${chalk.cyan(uniqueModels[0])}`); + } + + console.log(`│ ${chalk.bold('Total Runs:')} ${combinations.length}`); + console.log(`│ ${chalk.bold('Completed:')} ${successfulRuns} (${combinations.length > 0 ? ((successfulRuns / combinations.length) * 100).toFixed(1) : 0}%)`); + + // Show failed runs breakdown + const failedRuns = combinations.length - successfulRuns; + if (failedRuns > 0) { + console.log(`│ ${chalk.bold('Failed:')} ${chalk.red(failedRuns)} (${combinations.length > 0 ? ((failedRuns / combinations.length) * 100).toFixed(1) : 0}%)`); + + // Get failure breakdown + const failureBreakdown = logger.getFailureBreakdown(batchId); + if (failureBreakdown.length > 0) { + const failureReasons = failureBreakdown.map(f => `${f.errorType}: ${f.count}`).join(', '); + console.log(`│ ${chalk.bold('Failure Reasons:')} ${chalk.red(failureReasons)}`); + } + } + + console.log(`│ ${chalk.bold('Avg Score:')} ${combinations.length > 0 ? (totalWeightedScore / combinations.length).toFixed(4) : 0} / 10.0`); + console.log(`│ ${chalk.bold('Duration:')} ${(duration / 1000).toFixed(2)}s`); + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + // Show suite breakdown if analytics available + if (analytics && analytics.suiteBreakdown.length > 0) { + console.log(`\n${chalk.bold.underline('Suite Breakdown')}`); + analytics.suiteBreakdown.forEach(suite => { + const successRate = suite.runs > 0 ? ((suite.successfulRuns / suite.runs) * 100).toFixed(0) : 0; + console.log(` ${chalk.cyan(suite.suite)}/${suite.scenario}: ${suite.avgWeightedScore.toFixed(2)}/10 ${chalk.gray(`(${successRate}% success, ${suite.runs} runs)`)}`); + }); + } + + // Show agent performance + if (analytics && analytics.agentPerformance.length > 0) { + console.log(`\n${chalk.bold.underline('Agent Performance')}`); + analytics.agentPerformance.forEach((agent, index) => { + const rank = index + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + const modelStr = agent.model && agent.model !== 'default' ? ` [${agent.model}]` : ''; + const scoreColor = agent.avgWeightedScore >= 9 ? 'green' : agent.avgWeightedScore >= 7 ? 'yellow' : 'red'; + console.log(` ${rankDisplay} ${chalk.cyan(agent.agent)}${modelStr}: ${chalk[scoreColor](agent.avgWeightedScore.toFixed(2))}/10 ${chalk.gray(`(${agent.successfulRuns}/${agent.runs} runs)`)}`); + }); + } + + // Show failed runs if any + if (analytics && analytics.failedRuns.length > 0) { + console.log(`\n${chalk.bold.underline(chalk.red('Failed Runs'))}`); + analytics.failedRuns.forEach(run => { + console.log(` ${chalk.red('✗')} ${run.suite}/${run.scenario} (${run.tier}) ${run.agent} - ${run.error || 'Unknown error'}`); + }); + } + + // Show completion summary + console.log('\n' + chalk.green('✓') + chalk.bold(` Completed all ${combinations.length} benchmark(s)!`)); + + // Note: Database is now created directly in public/ directory +} + +// ============================================================================ +// INTERACTIVE BENCHMARK +// ============================================================================ + +export async function runInteractiveBenchmark() { + console.log(chalk.bold.underline('Demo: Benchmarking AI Agents:')); + + // Get available suites and scenarios + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + + if (!existsSync(suitesDir)) { + log.error(chalk.red('No suites directory found')); + return; + } + + const suites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + + if (suites.length === 0) { + log.error(chalk.red('No suites found')); + return; + } + + // Select suites (multiselect) + const selectedSuites = await multiselect({ + message: 'Choose suites:', + options: [ + { value: '__ALL__', label: 'All suites' }, + ...suites.map(suite => ({ value: suite, label: suite })) + ], + required: true + }); + + if (isCancel(selectedSuites)) { + cancel('Operation cancelled.'); + return; + } + + // Expand "All" selection + const suitesToUse = selectedSuites.includes('__ALL__') ? suites : selectedSuites; + + // Get scenarios for all selected suites + const allScenarios: Array<{ value: string; label: string; suite: string }> = []; + for (const suite of suitesToUse) { + const scenariosDir = join(suitesDir, suite, 'scenarios'); + if (existsSync(scenariosDir)) { + const scenarios = readdirSync(scenariosDir).filter(dir => + existsSync(join(scenariosDir, dir, 'scenario.yaml')) + ); + scenarios.forEach(scenario => { + allScenarios.push({ + value: scenario, + label: `${scenario} (${suite})`, + suite + }); + }); + } + } + + if (allScenarios.length === 0) { + log.error(chalk.red('No scenarios found for selected suites')); + return; + } + + // Select scenarios (multiselect) + const selectedScenarios = await multiselect({ + message: 'Choose scenarios:', + options: [ + { value: '__ALL__', label: 'All scenarios' }, + ...allScenarios + ], + required: true + }); + + if (isCancel(selectedScenarios)) { + cancel('Operation cancelled.'); + return; + } + + // Expand "All" selection and filter by suite + const scenariosToUse = selectedScenarios.includes('__ALL__') + ? allScenarios.map(s => s.value) + : selectedScenarios; + + // Collect available tiers from all selected scenarios + console.log('🔍 Scanning available tiers for selected scenarios...'); + const availableTiersSet = new Set(); + const scenarioTierMap = new Map(); + + for (const suite of suitesToUse) { + for (const scenario of scenariosToUse) { + const tiers = getAvailableTiers(suite, scenario); + const tierValues = tiers.map(t => t.value); + scenarioTierMap.set(`${suite}/${scenario}`, tierValues); + tiers.forEach(tier => availableTiersSet.add(tier.value)); + } + } + + // Show what tiers are available for each scenario + scenarioTierMap.forEach((tiers, scenario) => { + console.log(` ${scenario}: ${tiers.join(', ')}`); + }); + + const tierOptions = [ + { value: '__ALL__', label: 'All available tiers' }, + ...Array.from(availableTiersSet).sort().map(tier => ({ + value: tier, + label: getTierLabel(tier) + })) + ]; + + console.log(`✅ Found ${availableTiersSet.size} unique tiers across all scenarios`); + + // Select tiers (multiselect) + const selectedTiers = await multiselect({ + message: 'Choose difficulty tiers:', + options: tierOptions, + required: true + }); + + if (isCancel(selectedTiers)) { + cancel('Operation cancelled.'); + return; + } + + // Expand "All" selection + const tiersToUse = selectedTiers.includes('__ALL__') + ? Array.from(availableTiersSet).sort() + : selectedTiers; + + // Select agents (multiselect) - dynamically loaded + console.log('Loading available agents...'); + const agentOptions = await getAvailableAgents(); + console.log(`✅ Loaded ${agentOptions.length} agent options`); + + const selectedAgents = await multiselect({ + message: 'Choose agents:', + options: agentOptions, + required: true + }); + + if (isCancel(selectedAgents)) { + cancel('Operation cancelled.'); + return; + } + + // Expand "All" selection + const agentsToUse = selectedAgents.includes('__ALL__') + ? ['echo', 'openrouter', 'anthropic', 'claude-code'] + : selectedAgents; + + console.log(`🎯 Selected agents: ${agentsToUse.join(', ')}`); + + // Ask for models if needed + let modelsToUse: (string | undefined)[] = [undefined]; + const needsOpenRouterModels = agentsToUse.some(agent => agent === 'openrouter'); + const needsAnthropicModels = agentsToUse.some(agent => agent === 'anthropic'); + const needsClaudeCodeModels = agentsToUse.some(agent => agent === 'claude-code'); + + if (needsOpenRouterModels) { + console.log('🔍 Loading OpenRouter models with tool support...'); + + const openrouterAPI = new OpenRouterAPI(process.env.OPENROUTER_API_KEY || ''); + const toolModels = await openrouterAPI.getModelsWithToolSupport(); + + console.log(`✅ Found ${toolModels.length} models with tool support`); + + // Quick shortcuts for common models + const QUICK_MODELS = { + 'free': 'Filter by free models only', + 'gpt': 'OpenAI GPT models', + 'claude': 'Anthropic Claude models', + 'llama': 'Meta Llama models', + 'gemma': 'Google Gemma models', + 'mistral': 'Mistral AI models' + }; + + // Show shortcuts + console.log(`\n${chalk.gray('Quick searches:')} ${Object.keys(QUICK_MODELS).join(', ')}`); + + // Text-based search instead of dropdown + const modelSearch = await text({ + message: 'Search for OpenRouter model (type to filter):', + placeholder: 'e.g., gpt-4o, llama, gemma, claude', + validate: (value) => { + if (!value || value.length < 2) { + return 'Please enter at least 2 characters to search'; + } + } + }); + + if (isCancel(modelSearch)) { + cancel('Operation cancelled.'); + return; + } + + // Search and display results + const searchResults = openrouterAPI.searchModels(toolModels, modelSearch); + + if (searchResults.length === 0) { + log.warning(`No models found matching "${modelSearch}"`); + log.info('Try searching for: gpt-4o, llama, gemma, claude, mistral'); + return; + } + + // Show search results with pricing info + console.log(`\n📋 Found ${searchResults.length} matching models:\n`); + + const selectedModel = await select({ + message: 'Choose a model:', + options: searchResults.map(model => { + const promptCost = parseFloat(model.pricing.prompt); + const isFree = promptCost === 0; + const costLabel = isFree ? '(FREE)' : `($${promptCost}/1K tokens)`; + + return { + value: model.id, + label: `${model.name} ${costLabel}`, + hint: model.description?.substring(0, 80) + }; + }) + }); + + if (isCancel(selectedModel)) { + cancel('Operation cancelled.'); + return; + } + + modelsToUse = [selectedModel]; + console.log(`🎯 Selected model: ${selectedModel}`); + + // Display selected model details + const selectedModelInfo = toolModels.find(m => m.id === selectedModel); + + if (selectedModelInfo) { + console.log(`\n${chalk.bold.cyan('Model Details:')}`); + console.log(` ${chalk.gray('Name:')} ${selectedModelInfo.name}`); + console.log(` ${chalk.gray('ID:')} ${selectedModelInfo.id}`); + console.log(` ${chalk.gray('Context:')} ${selectedModelInfo.context_length.toLocaleString()} tokens`); + console.log(` ${chalk.gray('Cost:')} $${selectedModelInfo.pricing.prompt}/1K prompt, $${selectedModelInfo.pricing.completion}/1K completion`); + + const isFree = parseFloat(selectedModelInfo.pricing.prompt) === 0; + if (isFree) { + console.log(` ${chalk.green('✓ FREE MODEL')}`); + } + } + } else if (needsAnthropicModels) { + console.log('🧠 Loading available Anthropic models...'); + // Anthropic has a fixed set of models + const anthropicModels = [ + { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet (Current)' }, + { value: 'claude-3-5-haiku-20241022', label: 'Claude 3.5 Haiku (Current)' }, + { value: 'claude-3-7-sonnet-20250219', label: 'Claude 3.7 Sonnet (Active)' }, + { value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4 (Active)' }, + { value: 'claude-opus-4-20250514', label: 'Claude Opus 4 (Active)' }, + { value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1 (Active)' }, + { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5 (Active)' }, + { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5 (Active)' } + ]; + + const selectedModels = await multiselect({ + message: 'Choose Anthropic models:', + options: anthropicModels, + required: true + }); + + if (isCancel(selectedModels)) { + cancel('Operation cancelled.'); + return; + } + + modelsToUse = selectedModels; + console.log(`🎯 Selected Anthropic models: ${modelsToUse.join(', ')}`); + } else if (needsClaudeCodeModels) { + console.log('🧠 Loading available Claude Code models...'); + // Claude Code has a fixed set of models + const claudeCodeModels = [ + { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet (Current)' }, + { value: 'claude-3-5-haiku-20241022', label: 'Claude 3.5 Haiku (Current)' }, + { value: 'claude-3-7-sonnet-20250219', label: 'Claude 3.7 Sonnet (Active)' }, + { value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4 (Active)' }, + { value: 'claude-opus-4-20250514', label: 'Claude Opus 4 (Active)' }, + { value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1 (Active)' }, + { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5 (Active)' }, + { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5 (Active)' } + ]; + + const selectedModels = await multiselect({ + message: 'Choose Claude Code models:', + options: claudeCodeModels, + required: true + }); + + if (isCancel(selectedModels)) { + cancel('Operation cancelled.'); + return; + } + + modelsToUse = selectedModels; + console.log(`🎯 Selected Claude Code models: ${modelsToUse.join(', ')}`); + } + + + // Calculate total combinations for automatic parallel decision + const totalCombinations = suitesToUse.length * scenariosToUse.length * tiersToUse.length * agentsToUse.length * modelsToUse.length; + + // Automatically determine parallel execution based on number of benchmarks + const useParallel = totalCombinations >= 3; // Enable parallel for 3+ benchmarks + let concurrency = 3; + + if (useParallel) { + // Smart concurrency based on number of benchmarks + if (totalCombinations <= 5) { + concurrency = 2; // Conservative for small batches + } else if (totalCombinations <= 15) { + concurrency = 3; // Balanced for medium batches + } else if (totalCombinations <= 30) { + concurrency = 5; // Aggressive for large batches + } else { + concurrency = 8; // Maximum for very large batches + } + } + + // Show summary of what will be executed + console.log(`\n${chalk.green('►')} Will run ${chalk.bold(totalCombinations.toString())} benchmark combination(s)`); + console.log(` ${chalk.cyan('Suites:')} ${suitesToUse.join(', ')}`); + console.log(` ${chalk.cyan('Scenarios:')} ${scenariosToUse.join(', ')}`); + console.log(` ${chalk.cyan('Tiers:')} ${tiersToUse.join(', ')}`); + console.log(` ${chalk.cyan('Agents:')} ${agentsToUse.join(', ')}`); + if (needsOpenRouterModels || needsAnthropicModels || needsClaudeCodeModels) { + console.log(` ${chalk.cyan('Models:')} ${modelsToUse.join(', ')}`); + } + console.log(` ${chalk.cyan('Parallel execution:')} ${useParallel ? `Yes (concurrency: ${concurrency})` : 'No'}`); + + // Show title before execution + console.log(chalk.cyan(createTitle())); + + // Execute all benchmark combinations + await executeMultipleBenchmarks( + suitesToUse, + scenariosToUse, + tiersToUse, + agentsToUse, + modelsToUse + ); +} + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +async function getAvailableAgents(): Promise> { + const agents = [ + { value: '__ALL__', label: 'All agents' }, + { value: 'echo', label: 'Echo (Test Agent)' }, + { value: 'openrouter', label: 'OpenRouter (Any Model)' }, + { value: 'anthropic', label: 'Anthropic Claude (Direct API)' }, + { value: 'claude-code', label: 'Claude Code' } + ]; + + return agents; +} diff --git a/packages/harness/src/interactive/clear.ts b/packages/harness/src/interactive/clear.ts new file mode 100644 index 0000000..1b2c029 --- /dev/null +++ b/packages/harness/src/interactive/clear.ts @@ -0,0 +1,38 @@ +import { intro, outro, spinner, log, confirm } from '@clack/prompts'; +import chalk from 'chalk'; +import { BenchmarkLogger } from '@ze/database'; + +async function runInteractiveClear() { + const logger = BenchmarkLogger.getInstance(); + + try { + intro(chalk.bgRed(' Clear Database ')); + + // Get count before clearing + const stats = logger.getStats(); + log.warning(`Found ${chalk.bold(stats.totalRuns)} benchmark runs`); + + const shouldClear = await confirm({ + message: 'Are you sure you want to clear all data?', + initialValue: false + }); + + if (shouldClear) { + const s = spinner(); + s.start('Clearing database...'); + logger.clearDatabase(); + s.stop('Database cleared'); + outro(chalk.green('✓ All data removed')); + } else { + outro(chalk.yellow('Cancelled')); + } + + } catch (error) { + log.error(chalk.red('Failed to clear database:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +export { runInteractiveClear }; diff --git a/packages/harness/src/interactive/history.ts b/packages/harness/src/interactive/history.ts new file mode 100644 index 0000000..ef763ff --- /dev/null +++ b/packages/harness/src/interactive/history.ts @@ -0,0 +1,122 @@ +import { select, intro, outro, log } from '@clack/prompts'; +import chalk from 'chalk'; +import { BenchmarkLogger } from '@ze/database'; +import { startDevServer } from '../dev-server.ts'; +import { displayRunInfo, formatStats } from '../lib/display.ts'; + +// ============================================================================ +// INTERACTIVE RUN HISTORY +// ============================================================================ + +export async function runInteractiveHistory() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); + } catch (err) { + // Continue without web server + } + + const logger = BenchmarkLogger.getInstance(); + + try { + const limit = await select({ + message: 'How many recent runs to show?', + options: [ + { value: 5, label: '5 runs' }, + { value: 10, label: '10 runs' }, + { value: 20, label: '20 runs' }, + { value: 50, label: '50 runs' } + ] + }) as number; + + // Execute history command + const runHistory = logger.getRunHistory(limit); + + if (runHistory.length === 0) { + log.warning('No benchmark runs found'); + outro(chalk.yellow('Run a benchmark first')); + return; + } + + intro(chalk.bgCyan(' Benchmark History ')); + + // Use common display function + runHistory.forEach((run, index) => displayRunInfo(run, index)); + + outro(chalk.green(`Showing ${runHistory.length} recent runs`)); + + } catch (error) { + log.error(chalk.red('Failed to fetch history:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +// ============================================================================ +// INTERACTIVE BATCH HISTORY +// ============================================================================ + +export async function runInteractiveBatchHistory() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); + console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); + console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); + } catch (err) { + // Continue without web server + console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); + } + + const logger = BenchmarkLogger.getInstance(); + + try { + const limit = await select({ + message: 'How many recent batches to show?', + options: [ + { value: 5, label: '5 batches' }, + { value: 10, label: '10 batches' }, + { value: 20, label: '20 batches' }, + { value: 50, label: '50 batches' } + ] + }) as number; + + // Execute batch history command + const batchHistory = logger.getBatchHistory(limit); + + if (batchHistory.length === 0) { + log.warning('No batch runs found'); + outro(chalk.yellow('Run some benchmarks first')); + return; + } + + intro(chalk.bgCyan(' Batch History ')); + + batchHistory.forEach((batch, index) => { + const status = batch.completedAt + ? chalk.green('✓') + : chalk.yellow('○'); + + console.log(`\n${chalk.bold(`${index + 1}.`)} ${status} ${chalk.cyan('Batch')} ${chalk.dim(batch.batchId.substring(0, 8))}...`); + console.log(` ${formatStats('Runs', `${batch.successfulRuns}/${batch.totalRuns}`, 'green')}`); + console.log(` ${formatStats('Avg Score', batch.avgWeightedScore?.toFixed(4) || 'N/A', 'yellow')}`); + console.log(` ${chalk.gray(new Date(batch.createdAt).toLocaleString())}`); + if (batch.completedAt) { + const duration = (batch.completedAt - batch.createdAt) / 1000; + console.log(` ${formatStats('Duration', `${duration.toFixed(2)}s`, 'blue')}`); + } + }); + + outro(chalk.green(`Showing ${batchHistory.length} recent batches`)); + + } catch (error) { + log.error(chalk.red('Failed to fetch batch history:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} diff --git a/packages/harness/src/interactive/menu.ts b/packages/harness/src/interactive/menu.ts new file mode 100644 index 0000000..2444fd0 --- /dev/null +++ b/packages/harness/src/interactive/menu.ts @@ -0,0 +1,124 @@ +import { intro, outro, select, isCancel, cancel, log } from '@clack/prompts'; +import chalk from 'chalk'; + +// Import functions that will be called by the menu +// These imports will need to be adjusted based on the refactoring structure +import { runInteractiveBenchmark } from './benchmark.ts'; +import { runInteractiveHistory, runInteractiveBatchHistory } from './history.ts'; +import { runInteractiveSuiteStats, runInteractiveScenarioStats, runInteractiveRunStats, runInteractiveBatchStats, runInteractiveEvaluators } from './statistics.ts'; +import { createNewSuite, createNewScenario } from './suite-management.ts'; +import { runInteractiveClear } from './clear.ts'; +import { showHelp } from '../cli/args.ts'; +import { createTitle } from '../lib/display.ts'; +import { validateEnvironment } from '../cli/environment.ts'; + +async function showInteractiveMenu() { + // Check environment variables for interactive mode + await validateEnvironment(); + + console.log(chalk.cyan(createTitle())); + intro(chalk.bgBlue(' Interactive Mode ')); + + // Show web dashboard info + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')} ${chalk.blue('http://localhost:3000')} ${chalk.gray('- Interactive charts and analytics')}`); + console.log(` ${chalk.gray('Run:')} ${chalk.yellow('pnpm dev')} ${chalk.gray('to start the web server')}\n`); + + while (true) { + const action = await select({ + message: 'What would you like to do?', + options: [ + { value: 'benchmark', label: 'Run Benchmarks' }, + { value: 'history', label: 'History' }, + { value: 'statistics', label: 'Statistics' }, + { value: 'new-suite', label: 'Create New Suite' }, + { value: 'new-scenario', label: 'Create New Scenario' }, + { value: 'clear', label: 'Clear Database' }, + { value: 'help', label: 'Show Help' }, + { value: 'exit', label: 'Exit' } + ] + }); + + switch (action) { + case 'benchmark': + await runInteractiveBenchmark(); + break; + case 'history': + await runInteractiveHistoryMenu(); + break; + case 'statistics': + await runInteractiveStatisticsMenu(); + break; + case 'new-suite': + await createNewSuite(); + break; + case 'new-scenario': + await createNewScenario(); + break; + case 'clear': + await runInteractiveClear(); + break; + case 'help': + showHelp(); + break; + case 'exit': + outro(chalk.green('Goodbye!')); + process.exit(0); + break; + } + + // Add a small pause before showing the menu again + console.log('\n'); + } +} + +async function runInteractiveHistoryMenu() { + const historyType = await select({ + message: 'What history would you like to view?', + options: [ + { value: 'run-history', label: 'Run History' }, + { value: 'batch-history', label: 'Batch History' } + ] + }) as string; + + switch (historyType) { + case 'run-history': + await runInteractiveHistory(); + break; + case 'batch-history': + await runInteractiveBatchHistory(); + break; + } +} + +async function runInteractiveStatisticsMenu() { + const statsType = await select({ + message: 'What statistics would you like to view?', + options: [ + { value: 'suite-stats', label: 'Suite Statistics' }, + { value: 'scenario-stats', label: 'Scenario Statistics' }, + { value: 'run-stats', label: 'Run Details' }, + { value: 'batch-stats', label: 'Batch Statistics' }, + { value: 'evaluators', label: 'Evaluator Performance' } + ] + }) as string; + + switch (statsType) { + case 'suite-stats': + await runInteractiveSuiteStats(); + break; + case 'scenario-stats': + await runInteractiveScenarioStats(); + break; + case 'run-stats': + await runInteractiveRunStats(); + break; + case 'batch-stats': + await runInteractiveBatchStats(); + break; + case 'evaluators': + await runInteractiveEvaluators(); + break; + } +} + +export { showInteractiveMenu, runInteractiveHistoryMenu, runInteractiveStatisticsMenu }; diff --git a/packages/harness/src/interactive/statistics.ts b/packages/harness/src/interactive/statistics.ts new file mode 100644 index 0000000..86631d9 --- /dev/null +++ b/packages/harness/src/interactive/statistics.ts @@ -0,0 +1,379 @@ +import { existsSync, readdirSync } from 'node:fs'; +import { join } from 'node:path'; +import { select, intro, outro, log } from '@clack/prompts'; +import chalk from 'chalk'; +import { BenchmarkLogger } from '@ze/database'; +import { startDevServer } from '../dev-server.ts'; +import { findRepoRoot } from '../lib/workspace-utils.ts'; +import { formatStats, displayModelPerformance } from '../lib/display.ts'; + +// ============================================================================ +// INTERACTIVE SUITE STATISTICS +// ============================================================================ + +export async function runInteractiveSuiteStats() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); + console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); + console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); + } catch (err) { + // Continue without web server + console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); + } + + const logger = BenchmarkLogger.getInstance(); + + try { + // Get available suites + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + const suites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + + if (suites.length === 0) { + log.error(chalk.red('No suites found')); + return; + } + + const selectedSuite = await select({ + message: 'Choose a suite:', + options: suites.map(suite => ({ value: suite, label: suite })) + }) as string; + + // Execute suite stats + const stats = logger.getSuiteStats(selectedSuite); + console.log(chalk.bold.bgBlue(` Suite: ${selectedSuite} `)); + console.log('\n' + chalk.underline('Overview')); + console.log(formatStats('Total Runs', stats.totalRuns)); + console.log(formatStats('Success Rate', `${stats.totalRuns > 0 ? ((stats.successfulRuns / stats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); + console.log(formatStats('Avg Score', stats.avgScore.toFixed(4), 'yellow')); + console.log(formatStats('Avg Weighted', stats.avgWeightedScore.toFixed(4), 'yellow')); + console.log(formatStats('Avg Duration', `${(stats.avgDuration / 1000).toFixed(2)}s`, 'blue')); + + if (stats.scenarioBreakdown.length > 0) { + console.log('\n' + chalk.underline('Scenario Breakdown')); + stats.scenarioBreakdown.forEach(scenario => { + console.log(` ${chalk.cyan('•')} ${chalk.bold(scenario.scenario)}: ${chalk.yellow(scenario.avgScore.toFixed(4))} ${chalk.gray(`(${scenario.runs} runs)`)}`); + }); + } + + } catch (error) { + log.error(chalk.red('Failed to fetch suite statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +// ============================================================================ +// INTERACTIVE SCENARIO STATISTICS +// ============================================================================ + +export async function runInteractiveScenarioStats() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); + console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); + console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); + } catch (err) { + // Continue without web server + console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); + } + + const logger = BenchmarkLogger.getInstance(); + + try { + // Get available suites and scenarios + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + const suites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + + const selectedSuite = await select({ + message: 'Choose a suite:', + options: suites.map(suite => ({ value: suite, label: suite })) + }) as string; + + const scenariosDir = join(suitesDir, selectedSuite, 'scenarios'); + const scenarios = readdirSync(scenariosDir).filter(dir => + existsSync(join(scenariosDir, dir, 'scenario.yaml')) + ); + + const selectedScenario = await select({ + message: 'Choose a scenario:', + options: scenarios.map(scenario => ({ value: scenario, label: scenario })) + }) as string; + + // Execute scenario stats + const stats = logger.getScenarioStats(selectedSuite, selectedScenario); + console.log(chalk.bold.bgMagenta(` ${selectedSuite}/${selectedScenario} `)); + + // Score range with visual bar + const scorePercent = (stats.avgWeightedScore / 10) * 100; + const barLength = 20; + const filled = Math.round((scorePercent / 100) * barLength); + const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(barLength - filled)); + console.log(`\n${bar} ${chalk.bold(stats.avgWeightedScore.toFixed(2))}/10`); + + console.log('\n' + chalk.underline('Overview')); + console.log(formatStats('Total Runs', stats.totalRuns)); + console.log(formatStats('Success Rate', `${stats.totalRuns > 0 ? ((stats.successfulRuns / stats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); + console.log(formatStats('Avg Score', stats.avgScore.toFixed(4), 'yellow')); + console.log(formatStats('Score Range', `${stats.minScore.toFixed(4)} - ${stats.maxScore.toFixed(4)}`, 'blue')); + + // Agent comparison table + if (stats.agentComparison.length > 0) { + console.log('\n' + chalk.underline('Agent Performance')); + stats.agentComparison.forEach((agent, i) => { + const rank = i + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + console.log(` ${rankDisplay} ${chalk.cyan(agent.agent.padEnd(15))} ${chalk.yellow(agent.avgScore.toFixed(4))} ${chalk.gray(`(${agent.runs} runs)`)}`); + }); + } + + } catch (error) { + log.error(chalk.red('Failed to fetch scenario statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +// ============================================================================ +// INTERACTIVE RUN STATISTICS +// ============================================================================ + +export async function runInteractiveRunStats() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(`\n${chalk.cyan('🌐')} ${chalk.bold('Web Dashboard:')}`); + console.log(` ${chalk.blue.underline(serverUrl)} ${chalk.gray('- Click to open interactive dashboard')}`); + console.log(` ${chalk.gray('Features: Charts, analytics, batch comparison, and detailed run analysis')}`); + } catch (err) { + // Continue without web server + console.log(chalk.yellow('⚠ Web server not available, showing CLI statistics only')); + } + + const logger = BenchmarkLogger.getInstance(); + + try { + // Get recent runs to choose from + const runHistory = logger.getRunHistory(10); + + if (runHistory.length === 0) { + log.warning('No benchmark runs found'); + outro(chalk.yellow('Run a benchmark first')); + return; + } + + const selectedRun = await select({ + message: 'Choose a run to view details:', + options: runHistory.map((run, index) => ({ + value: run.runId, + label: `${index + 1}. ${run.suite}/${run.scenario} (${run.tier}) - ${run.agent} - ${run.weightedScore?.toFixed(4) || 'N/A'}` + })) + }) as string; + + // Execute run stats + const stats = logger.getDetailedRunStats(selectedRun); + console.log(chalk.bold.bgGreen(` Run Details `)); + console.log(`\n${chalk.gray('ID:')} ${chalk.dim(selectedRun.substring(0, 8))}...`); + console.log(formatStats('Suite', stats.run.suite)); + console.log(formatStats('Scenario', stats.run.scenario)); + console.log(formatStats('Tier', stats.run.tier)); + console.log(formatStats('Agent', stats.run.agent + (stats.run.model ? ` (${stats.run.model})` : ''))); + console.log(formatStats('Status', stats.run.status, stats.run.status === 'completed' ? 'green' : stats.run.status === 'failed' ? 'red' : 'yellow')); + console.log(formatStats('Started', new Date(stats.run.startedAt).toLocaleString())); + if (stats.run.completedAt) { + console.log(formatStats('Completed', new Date(stats.run.completedAt).toLocaleString())); + } + if (stats.run.totalScore !== null && stats.run.totalScore !== undefined) { + console.log(formatStats('Total Score', stats.run.totalScore.toFixed(4), 'green')); + } + if (stats.run.weightedScore !== null && stats.run.weightedScore !== undefined) { + console.log(formatStats('Weighted Score', stats.run.weightedScore.toFixed(4), 'green')); + } + + // Evaluation breakdown with progress bars + if (stats.evaluationBreakdown.length > 0) { + console.log('\n' + chalk.underline('Evaluations')); + stats.evaluationBreakdown.forEach(evaluation => { + const percent = evaluation.percentage; + const color = percent === 100 ? 'green' : percent >= 80 ? 'yellow' : 'red'; + const barLength = 15; + const filled = Math.round((percent / 100) * barLength); + const bar = chalk[color]('█'.repeat(filled)) + chalk.gray('░'.repeat(barLength - filled)); + + console.log(` ${evaluation.name.padEnd(30)} ${bar} ${chalk[color](percent.toFixed(1) + '%')}`); + }); + } + + if (stats.telemetrySummary) { + console.log('\n' + chalk.underline('Telemetry')); + console.log(formatStats('Tool Calls', stats.telemetrySummary.toolCalls, 'blue')); + console.log(formatStats('Tokens', stats.telemetrySummary.tokens, 'blue')); + console.log(formatStats('Cost', `$${stats.telemetrySummary.cost.toFixed(6)}`, 'blue')); + console.log(formatStats('Duration', `${(stats.telemetrySummary.duration / 1000).toFixed(2)}s`, 'blue')); + } + + } catch (error) { + log.error(chalk.red('Failed to fetch run statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +// ============================================================================ +// INTERACTIVE BATCH STATISTICS +// ============================================================================ + +export async function runInteractiveBatchStats() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); + } catch (err) { + // Continue without web server + } + + const logger = BenchmarkLogger.getInstance(); + + try { + // Get recent batches to choose from + const batchHistory = logger.getBatchHistory(10); + + if (batchHistory.length === 0) { + log.warning('No batch runs found'); + outro(chalk.yellow('Run some benchmarks first')); + return; + } + + const selectedBatch = await select({ + message: 'Choose a batch to view details:', + options: batchHistory.map((batch, index) => ({ + value: batch.batchId, + label: `${index + 1}. Batch ${batch.batchId.substring(0, 8)}... - ${batch.successfulRuns}/${batch.totalRuns} runs - ${batch.avgWeightedScore?.toFixed(4) || 'N/A'}` + })) + }) as string; + + // Execute batch stats + const batchStats = logger.getBatchDetails(selectedBatch); + if (!batchStats) { + log.error('Batch not found'); + return; + } + + console.log(chalk.bold.bgGreen(` Batch Details `)); + console.log(`\n${chalk.gray('ID:')} ${chalk.dim(selectedBatch.substring(0, 8))}...`); + console.log(formatStats('Total Runs', batchStats.totalRuns)); + console.log(formatStats('Successful', batchStats.successfulRuns, 'green')); + console.log(formatStats('Success Rate', `${batchStats.totalRuns > 0 ? ((batchStats.successfulRuns / batchStats.totalRuns) * 100).toFixed(1) : 0}%`, 'green')); + console.log(formatStats('Avg Score', batchStats.avgScore.toFixed(4), 'yellow')); + console.log(formatStats('Avg Weighted', batchStats.avgWeightedScore.toFixed(4), 'yellow')); + console.log(formatStats('Duration', `${(batchStats.duration / 1000).toFixed(2)}s`, 'blue')); + console.log(formatStats('Started', new Date(batchStats.createdAt).toLocaleString())); + if (batchStats.completedAt) { + console.log(formatStats('Completed', new Date(batchStats.completedAt).toLocaleString())); + } + + // Show runs in batch with ranking + if (batchStats.runs.length > 0) { + console.log('\n' + chalk.underline('Runs in Batch')); + const sortedRuns = batchStats.runs + .filter(run => run.weightedScore !== null && run.weightedScore !== undefined) + .sort((a, b) => (b.weightedScore || 0) - (a.weightedScore || 0)); + + sortedRuns.forEach((run, index) => { + const rank = index + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + const status = run.status === 'completed' + ? chalk.green('✓') + : run.status === 'failed' + ? chalk.red('✗') + : run.status === 'incomplete' + ? chalk.yellow('◐') + : chalk.blue('○'); + console.log(` ${rankDisplay} ${status} ${chalk.cyan(run.suite)}/${chalk.cyan(run.scenario)} ${chalk.gray(`(${run.tier})`)} ${chalk.cyan(run.agent)} ${chalk.yellow(run.weightedScore?.toFixed(4) || 'N/A')}`); + }); + } + + } catch (error) { + log.error(chalk.red('Failed to fetch batch statistics:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} + +// ============================================================================ +// INTERACTIVE EVALUATOR STATISTICS +// ============================================================================ + +export async function runInteractiveEvaluators() { + // Start dev server for web viewing + try { + const serverUrl = await startDevServer(); + // Note: Database is now created directly in public/ directory + console.log(chalk.gray(`\nView in browser: ${serverUrl}`)); + } catch (err) { + // Continue without web server + } + + const logger = BenchmarkLogger.getInstance(); + + try { + intro(chalk.bgYellow(' Evaluator Performance ')); + + const stats = logger.getStats(); + + if (Object.keys(stats.evaluatorStats).length === 0) { + log.warning('No evaluator data available'); + outro(chalk.yellow('Run some benchmarks first')); + return; + } + + // Sort by performance + const sorted = Object.entries(stats.evaluatorStats).sort((a, b) => b[1].averageScore - a[1].averageScore); + + console.log('\n' + chalk.underline('Performance Ranking')); + sorted.forEach(([name, stat], index) => { + const rank = index + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + const percent = (stat.averageScore * 100).toFixed(1); + const color = stat.averageScore >= 0.9 ? 'green' : stat.averageScore >= 0.7 ? 'yellow' : 'red'; + + console.log(` ${rankDisplay} ${chalk.bold(name.padEnd(30))} ${chalk[color](percent + '%')} ${chalk.gray(`(${stat.count} runs)`)}`); + }); + + // Show best and worst performers + const best = sorted[0]; + const worst = sorted[sorted.length - 1]; + + console.log('\n' + chalk.underline('Performance Summary')); + console.log(` ${chalk.green('Best:')} ${chalk.bold(best[0])} ${chalk.green((best[1].averageScore * 100).toFixed(1) + '%')}`); + console.log(` ${chalk.red('Needs Work:')} ${chalk.bold(worst[0])} ${chalk.red((worst[1].averageScore * 100).toFixed(1) + '%')}`); + + // Show model performance using common function + const modelStats = logger.getModelPerformanceStats(); + displayModelPerformance(modelStats); + + outro(chalk.green('Analysis complete')); + + } catch (error) { + log.error(chalk.red('Failed to fetch evaluator data:')); + console.error(chalk.dim(error instanceof Error ? error.message : String(error))); + } finally { + logger.close(); + } +} diff --git a/packages/harness/src/interactive/suite-management.ts b/packages/harness/src/interactive/suite-management.ts new file mode 100644 index 0000000..d51d610 --- /dev/null +++ b/packages/harness/src/interactive/suite-management.ts @@ -0,0 +1,309 @@ +import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from 'node:fs'; +import { join } from 'node:path'; +import { intro, outro, spinner, log, select, confirm, isCancel, cancel, text } from '@clack/prompts'; +import chalk from 'chalk'; + +// Import helper functions +import { findRepoRoot } from '../lib/workspace-utils.ts'; +import { validateName, checkSuiteExists, checkScenarioExists } from '../domain/scenario.ts'; + +async function createNewSuite(name?: string): Promise { + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + + // Get suite name from argument or prompt + let suiteName: string | undefined = name; + if (!suiteName) { + intro(chalk.bgGreen(' Create New Suite ')); + const input = await text({ + message: 'Enter suite name (kebab-case):', + placeholder: 'e.g., my-benchmark-suite', + validate: (value) => { + const validation = validateName(value, 'suite'); + if (!validation.valid) { + return validation.error; + } + if (checkSuiteExists(value)) { + return `Suite "${value}" already exists`; + } + return; + } + }); + + if (isCancel(input)) { + cancel('Operation cancelled.'); + return; + } + suiteName = input as string; + } + + // Validate name - TypeScript guard + if (!suiteName) { + log.error('Suite name is required'); + return; + } + + // TypeScript type narrowing - suiteName is guaranteed to be string here + const finalSuiteName = suiteName as string; + const validation = validateName(finalSuiteName, 'suite'); + if (!validation.valid) { + log.error(validation.error || 'Invalid suite name'); + return; + } + + // Check if suite exists + if (checkSuiteExists(finalSuiteName)) { + log.error(`Suite "${finalSuiteName}" already exists at suites/${finalSuiteName}/`); + return; + } + + // Create directory structure + const suitePath = join(suitesDir, finalSuiteName); + const promptsPath = join(suitePath, 'prompts'); + const scenariosPath = join(suitePath, 'scenarios'); + + const s = spinner(); + s.start('Creating suite directory structure...'); + + try { + mkdirSync(suitePath, { recursive: true }); + mkdirSync(promptsPath, { recursive: true }); + mkdirSync(scenariosPath, { recursive: true }); + s.stop('Suite created'); + + // Show relative path + const relativePath = join('suites', finalSuiteName); + console.log(`\n${chalk.green('✓')} Suite created at: ${chalk.cyan(relativePath)}`); + console.log(` ${chalk.gray('Structure:')} ${relativePath}/{prompts,scenarios}`); + + outro(chalk.green(`Suite "${finalSuiteName}" created successfully`)); + } catch (error) { + s.stop('Failed to create suite'); + log.error(`Failed to create suite: ${error instanceof Error ? error.message : String(error)}`); + } +} + +async function createNewScenario(suite?: string, scenarioName?: string): Promise { + const root = findRepoRoot(); + const suitesDir = join(root, 'suites'); + + // Get suite name + let suiteName = suite; + if (!suiteName) { + intro(chalk.bgGreen(' Create New Scenario ')); + + // Check if any suites exist + if (!existsSync(suitesDir)) { + log.error('No suites directory found. Create a suite first.'); + return; + } + + const existingSuites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + + if (existingSuites.length === 0) { + const shouldCreate = await confirm({ + message: 'No existing suites found. Create a new suite first?', + initialValue: true + }); + + if (isCancel(shouldCreate) || !shouldCreate) { + cancel('Operation cancelled.'); + return; + } + + await createNewSuite(); + // Refresh suites list + const refreshedSuites = readdirSync(suitesDir).filter(dir => + existsSync(join(suitesDir, dir, 'scenarios')) + ); + if (refreshedSuites.length === 0) { + log.error('Failed to create suite or no suites available'); + return; + } + existingSuites.push(...refreshedSuites); + } + + const selectedSuite = await select({ + message: 'Select suite to add scenario to:', + options: existingSuites.map(s => ({ value: s, label: s })) + }); + + if (isCancel(selectedSuite)) { + cancel('Operation cancelled.'); + return; + } + suiteName = selectedSuite as string; + } + + // TypeScript guard - suiteName must be string at this point + if (!suiteName) { + log.error('Suite name is required'); + return; + } + + // Validate suite exists + if (!checkSuiteExists(suiteName)) { + log.error(`Suite "${suiteName}" does not exist. Create it first with --new-suite.`); + return; + } + + // Get scenario name + let name: string | undefined = scenarioName; + if (!name) { + const input = await text({ + message: 'Enter scenario name (kebab-case):', + placeholder: 'e.g., my-test-scenario', + validate: (value) => { + const validation = validateName(value, 'scenario'); + if (!validation.valid) { + return validation.error; + } + if (checkScenarioExists(suiteName, value)) { + return `Scenario "${value}" already exists in suite "${suiteName}"`; + } + return; + } + }); + + if (isCancel(input)) { + cancel('Operation cancelled.'); + return; + } + name = input as string; + } + + // Validate name - TypeScript guard + if (!name) { + log.error('Scenario name is required'); + return; + } + + // TypeScript type narrowing - name is guaranteed to be string here + const finalName = name as string; + const validation = validateName(finalName, 'scenario'); + if (!validation.valid) { + log.error(validation.error || 'Invalid scenario name'); + return; + } + + // Check if scenario exists + if (checkScenarioExists(suiteName, finalName)) { + log.error(`Scenario "${finalName}" already exists in suite "${suiteName}" at suites/${suiteName}/scenarios/${finalName}/`); + return; + } + + const s = spinner(); + s.start('Creating scenario structure...'); + + try { + // Create scenario directory + const scenarioPath = join(suitesDir, suiteName, 'scenarios', finalName); + mkdirSync(scenarioPath, { recursive: true }); + + // Create prompts directory for this scenario + const promptsPath = join(suitesDir, suiteName, 'prompts', finalName); + mkdirSync(promptsPath, { recursive: true }); + + s.message('Copying scenario template...'); + + // Copy and customize scenario.yaml template + const templatePath = join(root, 'docs', 'templates', 'scenario.yaml'); + if (!existsSync(templatePath)) { + throw new Error(`Template file not found: ${templatePath}`); + } + + let templateContent = readFileSync(templatePath, 'utf8'); + // Replace placeholders + templateContent = templateContent.replace(/^id: my-scenario$/m, `id: ${finalName}`); + templateContent = templateContent.replace(/^suite: my-suite$/m, `suite: ${suiteName}`); + + const scenarioYamlPath = join(scenarioPath, 'scenario.yaml'); + writeFileSync(scenarioYamlPath, templateContent); + + s.message('Creating oracle-answers.json...'); + + // Create oracle-answers.json + const oraclePath = join(scenarioPath, 'oracle-answers.json'); + writeFileSync(oraclePath, '{}\n'); + + s.message('Creating repo-fixture directory...'); + + // Create repo-fixture directory + const repoFixturePath = join(scenarioPath, 'repo-fixture'); + mkdirSync(repoFixturePath, { recursive: true }); + + s.message('Copying repo-fixture guide to README.md...'); + + // Copy repo-fixture.md template content into README.md inside repo-fixture + const repoFixtureTemplatePath = join(root, 'docs', 'templates', 'repo-fixture.md'); + if (existsSync(repoFixtureTemplatePath)) { + const templateContent = readFileSync(repoFixtureTemplatePath, 'utf8'); + const repoFixtureReadmePath = join(repoFixturePath, 'README.md'); + writeFileSync(repoFixtureReadmePath, templateContent); + } else { + log.warning(`Template file not found: ${repoFixtureTemplatePath}`); + // Create a basic README if template is missing + const repoFixtureReadmePath = join(repoFixturePath, 'README.md'); + const basicContent = `# Repository Fixture + +This directory contains the starting codebase state for this scenario. + +## Setup Instructions + +1. Add a \`package.json\` file with your project dependencies +2. Include source files, tests, and configuration files +3. Ensure the fixture represents the starting state before the agent performs the task +4. Test that baseline commands (install, build, test) work correctly +`; + writeFileSync(repoFixtureReadmePath, basicContent); + } + + s.message('Creating default prompt tiers...'); + + // Create default prompt tier files + const promptTiers = [ + { file: 'L0-minimal.md', content: 'Complete the task with minimal guidance.\n' }, + { file: 'L1-basic.md', content: 'Complete the task with basic context and requirements.\n\nConstraints and goals:\n- Follow best practices\n- Ensure correctness\n' }, + { file: 'L2-directed.md', content: 'Complete the task with detailed guidance.\n\nConstraints and goals:\n- Follow all specified requirements\n- Maintain code quality\n- Ensure tests pass\n- Handle edge cases appropriately\n\nIf major changes are required, ask before proceeding.\n' } + ]; + + for (const tier of promptTiers) { + const tierPath = join(promptsPath, tier.file); + writeFileSync(tierPath, tier.content); + } + + s.stop('Scenario created'); + + // Show relative paths + const scenarioRelativePath = join('suites', suiteName, 'scenarios', finalName); + const promptsRelativePath = join('suites', suiteName, 'prompts', finalName); + + console.log(`\n${chalk.green('✓')} Scenario created:`); + console.log(` ${chalk.cyan('Scenario:')} ${scenarioRelativePath}/`); + console.log(` ${chalk.cyan('Prompts:')} ${promptsRelativePath}/`); + const repoFixtureRelativePath = join('suites', suiteName, 'scenarios', finalName, 'repo-fixture'); + + console.log(`\n${chalk.gray('Created files:')}`); + console.log(` - ${scenarioRelativePath}/scenario.yaml`); + console.log(` - ${scenarioRelativePath}/oracle-answers.json`); + console.log(` - ${repoFixtureRelativePath}/README.md ${chalk.dim('(setup guide)')}`); + console.log(` - ${repoFixtureRelativePath}/ ${chalk.dim('(empty - add your fixture files here)')}`); + console.log(` - ${promptsRelativePath}/L0-minimal.md`); + console.log(` - ${promptsRelativePath}/L1-basic.md`); + console.log(` - ${promptsRelativePath}/L2-directed.md`); + console.log(`\n${chalk.yellow('Next steps:')}`); + console.log(` ${chalk.cyan('1.')} Read ${chalk.bold('repo-fixture/README.md')} for setup instructions`); + console.log(` ${chalk.cyan('2.')} Add your starting codebase to ${chalk.bold('repo-fixture/')} directory`); + console.log(` ${chalk.cyan('3.')} Customize ${chalk.bold('scenario.yaml')} with your scenario configuration`); + console.log(` ${chalk.cyan('4.')} Update prompt files in ${chalk.bold('prompts/')} directory`); + + outro(chalk.green(`Scenario "${finalName}" created successfully in suite "${suiteName}"`)); + } catch (error) { + s.stop('Failed to create scenario'); + log.error(`Failed to create scenario: ${error instanceof Error ? error.message : String(error)}`); + } +} + +export { createNewSuite, createNewScenario }; diff --git a/packages/harness/src/lib/config.ts b/packages/harness/src/lib/config.ts new file mode 100644 index 0000000..98d45b9 --- /dev/null +++ b/packages/harness/src/lib/config.ts @@ -0,0 +1,79 @@ +import { readFileSync, existsSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { findZeBenchmarksRoot } from './project-root.js'; + +export interface BenchmarkConfig { + suitesDir: string; + outputDir: string; + databasePath: string; +} + +/** + * Default configuration values + */ +const DEFAULT_CONFIG: BenchmarkConfig = { + suitesDir: './suites', + outputDir: './results', + databasePath: './benchmarks.db', +}; + +/** + * Load benchmark configuration from benchmark.config.json + * Falls back to defaults if config file is not found + * + * @param projectRoot - The project root directory (if not provided, will be auto-detected) + * @returns Benchmark configuration with absolute paths + */ +export function loadBenchmarkConfig(projectRoot?: string): BenchmarkConfig { + // Auto-detect project root if not provided + const root = projectRoot || findZeBenchmarksRoot(); + + if (!root) { + throw new Error( + 'Could not find ze-benchmarks project root. ' + + 'Make sure you are running from within the ze-benchmarks directory, ' + + 'or that benchmark.config.json exists in your project root.' + ); + } + + // Try to load config file + const configPath = join(root, 'benchmark.config.json'); + let config: Partial = {}; + + if (existsSync(configPath)) { + try { + const configContent = readFileSync(configPath, 'utf-8'); + config = JSON.parse(configContent); + } catch (error) { + console.warn(`Failed to parse benchmark.config.json: ${error instanceof Error ? error.message : String(error)}`); + console.warn('Using default configuration'); + } + } + + // Merge with defaults + const mergedConfig: BenchmarkConfig = { + suitesDir: config.suitesDir || DEFAULT_CONFIG.suitesDir, + outputDir: config.outputDir || DEFAULT_CONFIG.outputDir, + databasePath: config.databasePath || DEFAULT_CONFIG.databasePath, + }; + + // Resolve all paths relative to project root + return { + suitesDir: resolve(root, mergedConfig.suitesDir), + outputDir: resolve(root, mergedConfig.outputDir), + databasePath: resolve(root, mergedConfig.databasePath), + }; +} + +/** + * Get database path from environment variable or config + * Priority: BENCHMARK_DB_PATH env var > config > default + */ +export function getDatabasePath(projectRoot?: string): string { + if (process.env.BENCHMARK_DB_PATH) { + return resolve(process.env.BENCHMARK_DB_PATH); + } + + const config = loadBenchmarkConfig(projectRoot); + return config.databasePath; +} diff --git a/packages/harness/src/lib/constants.ts b/packages/harness/src/lib/constants.ts new file mode 100644 index 0000000..3c59d20 --- /dev/null +++ b/packages/harness/src/lib/constants.ts @@ -0,0 +1,18 @@ +// ============================================================================ +// CONSTANTS +// ============================================================================ + +export const TABLE_WIDTH = 60; +export const SCORE_THRESHOLDS = { + EXCELLENT: 90, + GOOD: 70, + NEEDS_WORK: 60 +} as const; + +// Simple progress state and helpers +export const TOTAL_STAGES = 6; + +export interface ProgressState { + spinner: any; + currentStage: number; +} diff --git a/packages/harness/src/lib/display.ts b/packages/harness/src/lib/display.ts new file mode 100644 index 0000000..0a15730 --- /dev/null +++ b/packages/harness/src/lib/display.ts @@ -0,0 +1,173 @@ +import chalk from 'chalk'; +import figlet from 'figlet'; +import { spinner } from '@clack/prompts'; +import { TABLE_WIDTH, SCORE_THRESHOLDS, TOTAL_STAGES, type ProgressState } from './constants.ts'; + +// ============================================================================ +// PROGRESS TRACKING +// ============================================================================ + +export function createProgress(): ProgressState { + return { + spinner: spinner(), + currentStage: 0 + }; +} + +export function updateProgress(state: ProgressState, stage: number, description: string) { + const percent = Math.round((stage / TOTAL_STAGES) * 100); + const message = `[${stage}/${TOTAL_STAGES}] ${percent}% - ${description}`; + + if (state.currentStage === 0) { + // First time - start the spinner + state.spinner.start(message); + } else { + // Update existing spinner message + state.spinner.message(message); + } + + state.currentStage = stage; +} + +export function completeProgress(state: ProgressState) { + state.spinner.stop('Benchmark complete'); +} + +// ============================================================================ +// DISPLAY FUNCTIONS +// ============================================================================ + +export function formatStats(label: string, value: string | number, color: 'green' | 'blue' | 'yellow' | 'red' = 'blue') { + return `${chalk.gray(label)}: ${chalk[color](value)}`; +} + +export function createTitle() { + return figlet.textSync('ze-benchmarks', { + font: 'ANSI Shadow', + horizontalLayout: 'fitted', + verticalLayout: 'default' + }); +} + +// LLM Judge display function +export function displayLLMJudgeScores(result: { scores?: Record; evaluator_results?: Array<{ name: string; details?: string }> }) { + const llmJudgeScore = (result.scores as any)['LLMJudgeEvaluator']; + const evaluatorResults = (result as any).evaluator_results; + + let llmJudgeResult = null; + if (evaluatorResults && Array.isArray(evaluatorResults)) { + llmJudgeResult = evaluatorResults.find((r: any) => r.name === 'LLMJudgeEvaluator'); + } + + if (!llmJudgeResult && !llmJudgeScore) return; + + const details = llmJudgeResult?.details || llmJudgeScore?.details; + if (!details) return; + + try { + const parsedDetails = JSON.parse(details); + if (parsedDetails.scores && Array.isArray(parsedDetails.scores)) { + console.log(`\n${chalk.bold.underline('LLM Judge Detailed Scores')}`); + console.log(`┌${'─'.repeat(TABLE_WIDTH)}┐`); + console.log(`│ ${chalk.bold('Category'.padEnd(20))} ${chalk.bold('Score'.padEnd(8))} ${chalk.bold('Weight'.padEnd(8))} ${chalk.bold('Status'.padEnd(15))} │`); + console.log(`├${'─'.repeat(TABLE_WIDTH)}┤`); + + const weights: Record = { + 'dependency_quality': 25, + 'safety_stability': 20, + 'best_practices': 15, + 'monorepo_coordination': 15, + 'technical_execution': 10, + 'communication_transparency': 10, + 'long_term_maintainability': 5 + }; + + const expectedCategories = [ + 'dependency_quality', + 'safety_stability', + 'best_practices', + 'monorepo_coordination', + 'technical_execution', + 'communication_transparency', + 'long_term_maintainability', + 'overall_integrity' + ]; + + const scoreMap = new Map(); + parsedDetails.scores.forEach((score: any) => { + if (score.category && score.score !== undefined) { + scoreMap.set(score.category, score); + } + }); + + expectedCategories.forEach(category => { + const score = scoreMap.get(category); + const weight = weights[category] || 0; + + if (score) { + const percent = (score.score / 5) * 100; + const color = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; + const status = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'Excellent' : percent >= SCORE_THRESHOLDS.GOOD ? 'Good' : 'Needs Work'; + const statusColor = percent >= SCORE_THRESHOLDS.EXCELLENT ? 'green' : percent >= SCORE_THRESHOLDS.GOOD ? 'yellow' : 'red'; + const categoryName = category.replace(/_/g, ' ').replace(/\b\w/g, (l: string) => l.toUpperCase()); + + console.log(`│ ${chalk.cyan(categoryName.padEnd(20))} ${chalk[color]((score.score.toFixed(1)).padEnd(8))} ${chalk.gray((weight + '%').padEnd(8))} ${chalk[statusColor](status.padEnd(15))} │`); + } else { + const categoryName = category.replace(/_/g, ' ').replace(/\b\w/g, (l: string) => l.toUpperCase()); + console.log(`│ ${chalk.red(categoryName.padEnd(20))} ${chalk.red('N/A'.padEnd(8))} ${chalk.gray((weight + '%').padEnd(8))} ${chalk.red('Missing'.padEnd(15))} │`); + } + }); + + console.log(`└${'─'.repeat(TABLE_WIDTH)}┘`); + + if (parsedDetails.overall_assessment) { + console.log(`\n${chalk.bold('LLM Judge Assessment:')}`); + console.log(chalk.gray(parsedDetails.overall_assessment)); + } + + if (parsedDetails.input_tokens) { + console.log(`\n${chalk.bold('Token Usage:')}`); + console.log(chalk.blue(`Input tokens: ${parsedDetails.input_tokens}`)); + } + } + } catch (error) { + console.log(`\n${chalk.bold('LLM Judge Details:')}`); + console.log(chalk.gray(details)); + } +} + +// Common display functions +export function displayRunInfo(run: { status: string; suite: string; scenario: string; tier: string; agent: string; model?: string; weightedScore?: number | null; runId: string; startedAt: string | number }, index: number) { + const status = run.status === 'completed' + ? chalk.green('✓') + : run.status === 'failed' + ? chalk.red('✗') + : run.status === 'incomplete' + ? chalk.yellow('◐') + : chalk.blue('○'); + + console.log(`\n${chalk.bold(`${index + 1}.`)} ${status} ${chalk.cyan(run.suite)}/${chalk.cyan(run.scenario)} ${chalk.gray(`(${run.tier})`)}`); + console.log(` ${formatStats('Agent', run.agent + (run.model ? ` (${run.model})` : ''))}`); + console.log(` ${formatStats('Score', run.weightedScore?.toFixed(4) || 'N/A', 'green')}`); + console.log(` ${chalk.gray(new Date(run.startedAt).toLocaleString())}`); + console.log(` ${chalk.dim(`ID: ${run.runId.substring(0, 8)}...`)}`); +} + + +export function displayModelPerformance(modelStats: Array<{ model: string; avgScore: number; runs: number }>) { + if (modelStats.length === 0) return; + + console.log('\n' + chalk.underline('Model Performance')); + modelStats.forEach((model, index) => { + const rank = index + 1; + const rankDisplay = rank <= 3 ? `#${rank}` : `${rank}.`; + const percent = model.avgScore > 1 ? model.avgScore.toFixed(1) : (model.avgScore * 100).toFixed(1); + const color = model.avgScore >= 0.9 ? 'green' : model.avgScore >= 0.7 ? 'yellow' : 'red'; + + console.log(` ${rankDisplay} ${chalk.bold(model.model.padEnd(35))} ${chalk[color](percent + '%')} ${chalk.gray(`(${model.runs} runs)`)}`); + }); + + const bestModel = modelStats[0]; + const bestPercent = bestModel.avgScore > 1 ? bestModel.avgScore.toFixed(1) : (bestModel.avgScore * 100).toFixed(1); + console.log(`\n ${chalk.cyan('Top Model:')} ${chalk.bold(bestModel.model)} ${chalk.green(bestPercent + '%')} ${chalk.gray(`(${bestModel.runs} runs)`)}`); +} diff --git a/packages/harness/src/lib/project-root.ts b/packages/harness/src/lib/project-root.ts new file mode 100644 index 0000000..cc7d5b2 --- /dev/null +++ b/packages/harness/src/lib/project-root.ts @@ -0,0 +1,68 @@ +import { existsSync, readFileSync } from 'node:fs'; +import { join, resolve, dirname } from 'node:path'; + +/** + * Find the project root by walking up from startDir looking for a marker file + * + * @param markerFile - The file to look for (e.g., 'benchmark.config.json', 'package.json', 'pnpm-workspace.yaml') + * @param startDir - The directory to start searching from (defaults to process.cwd()) + * @returns The project root directory, or null if not found + */ +export function findProjectRoot(markerFile: string, startDir: string = process.cwd()): string | null { + let currentDir = resolve(startDir); + + // Walk up the directory tree + while (true) { + const markerPath = join(currentDir, markerFile); + + if (existsSync(markerPath)) { + return currentDir; + } + + // Check if we've reached the filesystem root + const parentDir = dirname(currentDir); + if (parentDir === currentDir) { + return null; // Reached root without finding marker + } + + currentDir = parentDir; + } +} + +/** + * Find the ze-benchmarks project root + * Looks for benchmark.config.json first, then falls back to package.json with name "ze-benchmarks" + */ +export function findZeBenchmarksRoot(startDir: string = process.cwd()): string | null { + // First try to find benchmark.config.json + const configRoot = findProjectRoot('benchmark.config.json', startDir); + if (configRoot) { + return configRoot; + } + + // Fallback: look for package.json and check if it's ze-benchmarks + let currentDir = resolve(startDir); + + while (true) { + const pkgPath = join(currentDir, 'package.json'); + + if (existsSync(pkgPath)) { + try { + const pkgContent = readFileSync(pkgPath, 'utf-8'); + const pkg = JSON.parse(pkgContent); + if (pkg.name === 'ze-benchmarks') { + return currentDir; + } + } catch (error) { + // Invalid package.json, continue searching + } + } + + const parentDir = dirname(currentDir); + if (parentDir === currentDir) { + return null; + } + + currentDir = parentDir; + } +} diff --git a/packages/harness/src/lib/workspace-utils.ts b/packages/harness/src/lib/workspace-utils.ts new file mode 100644 index 0000000..1977045 --- /dev/null +++ b/packages/harness/src/lib/workspace-utils.ts @@ -0,0 +1,105 @@ +import { existsSync, mkdirSync, mkdtempSync, cpSync, readdirSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { log } from '@clack/prompts'; +import { findZeBenchmarksRoot, findProjectRoot } from './project-root.js'; + +// ============================================================================ +// WORKSPACE UTILITIES +// ============================================================================ + +// Find workspace root by looking for pnpm-workspace.yaml +// In case of nested workspaces, find the topmost one +export function findWorkspaceRoot(startDir: string): string { + let currentDir = startDir; + let lastWorkspaceRoot = startDir; + + while (currentDir !== resolve(currentDir, '..')) { + if (existsSync(join(currentDir, 'pnpm-workspace.yaml'))) { + lastWorkspaceRoot = currentDir; + } + currentDir = resolve(currentDir, '..'); + } + + return lastWorkspaceRoot; +} + +export function findRepoRoot(): string { + // Use project root detection instead of hardcoded traversal + const root = findZeBenchmarksRoot(); + if (!root) { + // Fallback: Look for package.json with name "ze-benchmarks" + const pkgRoot = findProjectRoot('package.json', fileURLToPath(import.meta.url)); + if (pkgRoot) { + return pkgRoot; + } + // Last resort: old behavior + return resolve(fileURLToPath(import.meta.url), '../../../../..'); + } + return root; +} + +export function prepareWorkspaceFromFixture(suite: string, scenario: string, getScenarioDir: (suite: string, scenario: string) => string): { workspaceDir: string; fixtureDir: string } | undefined { + console.log('\x1b[90m[DEBUG] prepareWorkspaceFromFixture()\x1b[0m'); + console.log(`\x1b[90m Suite: ${suite}, Scenario: ${scenario}\x1b[0m`); + + const scenarioDir = getScenarioDir(suite, scenario); + console.log(`\x1b[90m Scenario dir: ${scenarioDir}\x1b[0m`); + + const candidates = ['repo', 'repo-fixture']; + console.log(`\x1b[90m Looking for fixture directories: ${candidates.join(', ')}\x1b[0m`); + + let fixtureDir: string | null = null; + for (const name of candidates) { + const dir = join(scenarioDir, name); + console.log(`\x1b[90m Checking: ${dir} (exists: ${existsSync(dir)})\x1b[0m`); + if (existsSync(dir)) { + fixtureDir = dir; + console.log(`\x1b[90m ✓ Found fixture: ${dir}\x1b[0m`); + break; + } + } + + if (!fixtureDir) { + console.error(`\x1b[31m[DEBUG] No fixture directory found (looked for ${candidates.join(', ')}) in ${scenarioDir}\x1b[0m`); + log.warning(`No raw fixture directory found (looked for ${candidates.join(', ')}) in ${scenarioDir}`); + return; + } + + const root = findRepoRoot(); + console.log(`\x1b[90m Repo root: ${root}\x1b[0m`); + + const workspacesDir = join(root, 'results', 'workspaces'); + console.log(`\x1b[90m Workspaces dir: ${workspacesDir}\x1b[0m`); + + mkdirSync(workspacesDir, { recursive: true }); + const workspaceDir = mkdtempSync(join(workspacesDir, `${suite}-${scenario}-`)); + console.log(`\x1b[90m Created temp workspace: ${workspaceDir}\x1b[0m`); + + try { + console.log(`\x1b[90m Copying fixture files from ${fixtureDir} to ${workspaceDir}...\x1b[0m`); + + // Copy fixture directory while excluding README.md files + cpSync(fixtureDir, workspaceDir, { + recursive: true, + filter: (src: string) => { + // Exclude README.md files from being copied (check filename and path) + const fileName = src.split(/[/\\]/).pop() || ''; + return fileName !== 'README.md'; + } + }); + + // Count files copied + const fileCount = readdirSync(workspaceDir, { recursive: true }).length; + console.log(`\x1b[90m ✓ Copied ${fileCount} files/directories successfully\x1b[0m`); + + return { workspaceDir, fixtureDir }; + } catch (err) { + console.error(`\x1b[31m[DEBUG] Failed to copy fixture directory: ${err instanceof Error ? err.message : String(err)}\x1b[0m`); + if (err instanceof Error && err.stack) { + console.error(`\x1b[90m${err.stack}\x1b[0m`); + } + console.error('Failed to copy fixture directory:', err); + return; + } +} diff --git a/packages/harness/src/runtime/workspace-tools.ts b/packages/harness/src/runtime/workspace-tools.ts index 214efb7..4720646 100644 --- a/packages/harness/src/runtime/workspace-tools.ts +++ b/packages/harness/src/runtime/workspace-tools.ts @@ -1,6 +1,7 @@ import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'node:fs'; import { join, relative } from 'node:path'; import { execSync } from 'node:child_process'; +import chalk from 'chalk'; export type ToolDefinition = { name: string; @@ -94,14 +95,34 @@ export function createListFilesTool(): ToolDefinition { }; } +// Create fetchUrl tool definition +export function createFetchUrlTool(): ToolDefinition { + return { + name: 'fetchUrl', + description: 'Fetch content from a URL and return it as text. Use this to read documentation, APIs, or any web content.', + input_schema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'The URL to fetch (e.g., "https://ui.shadcn.com/docs/installation/vite")' + } + }, + required: ['url'] + } + }; +} + // Create tool handlers for workspace operations export function createWorkspaceToolHandlers(workspaceDir: string): Map { const handlers = new Map(); // readFile handler handlers.set('readFile', async (input: { path: string }): Promise => { + console.log(chalk.blue(`[readFile] Reading: ${input.path}`)); + const fullPath = join(workspaceDir, input.path); - + // Security: ensure path is within workspace const resolvedPath = join(workspaceDir, input.path); if (!resolvedPath.startsWith(workspaceDir)) { @@ -124,17 +145,20 @@ export function createWorkspaceToolHandlers(workspaceDir: string): Map => { + console.log(chalk.blue(`[writeFile] Writing to: ${input.path} (${input.content.length} bytes)`)); + const fullPath = join(workspaceDir, input.path); - + // Security: ensure path is within workspace const resolvedPath = join(workspaceDir, input.path); if (!resolvedPath.startsWith(workspaceDir)) { @@ -148,17 +172,19 @@ export function createWorkspaceToolHandlers(workspaceDir: string): Map => { - console.log(`[runCommand] ${input.description || input.command}`); - + const desc = input.description || input.command; + console.log(chalk.blue(`[runCommand] Starting: ${desc}`)); + try { const output = execSync(input.command, { cwd: workspaceDir, @@ -167,25 +193,30 @@ export function createWorkspaceToolHandlers(workspaceDir: string): Map 200 ? '...' : ''}`)); return output || 'Command completed successfully (no output)'; } catch (error: any) { const message = error.stderr || error.stdout || error.message || String(error); - console.log(`[runCommand] Failed: ${message.slice(0, 200)}`); + console.error(chalk.red(`[runCommand] ❌ Failed: ${desc}`)); + console.error(chalk.gray(`[runCommand] Error: ${message.slice(0, 200)}`)); return `Command failed: ${message}`; } }); // listFiles handler handlers.set('listFiles', async (input: { path: string }): Promise => { + console.log(chalk.blue(`[listFiles] Listing: ${input.path}`)); + const fullPath = join(workspaceDir, input.path); - + // Security: block access to node_modules directories if (input.path.includes('node_modules') || input.path.includes('/node_modules/') || input.path.startsWith('node_modules/')) { return `Error: Access to node_modules is not allowed. Please focus on your project files.`; } - + if (!existsSync(fullPath)) { return `Error: Path '${input.path}' does not exist`; } @@ -206,13 +237,53 @@ export function createWorkspaceToolHandlers(workspaceDir: string): Map => { + console.log(chalk.blue(`[fetchUrl] Fetching: ${input.url}`)); + + try { + const response = await fetch(input.url); + + if (!response.ok) { + console.error(chalk.red(`[fetchUrl] ❌ HTTP ${response.status}: ${input.url}`)); + return `Error: HTTP ${response.status} - ${response.statusText}`; + } + + const contentType = response.headers.get('content-type') || ''; + + if (contentType.includes('text/html')) { + // For HTML, extract text content (simple approach) + const html = await response.text(); + // Remove script and style tags + const cleaned = html + .replace(/)<[^<]*)*<\/script>/gi, '') + .replace(/)<[^<]*)*<\/style>/gi, '') + .replace(/<[^>]+>/g, ' ') + .replace(/\s+/g, ' ') + .trim(); + + console.log(chalk.green(`[fetchUrl] ✓ Fetched HTML from ${input.url} (${cleaned.length} chars)`)); + return cleaned; + } else { + // For other content types, return as text + const text = await response.text(); + console.log(chalk.green(`[fetchUrl] ✓ Fetched ${input.url} (${text.length} chars)`)); + return text; + } + } catch (error) { + console.error(chalk.red(`[fetchUrl] ❌ Failed to fetch ${input.url}:`), error); + return `Error fetching URL: ${error instanceof Error ? error.message : String(error)}`; + } + }); + return handlers; } @@ -222,6 +293,7 @@ export function getAllWorkspaceTools(): ToolDefinition[] { createReadFileTool(), createWriteFileTool(), createRunCommandTool(), - createListFilesTool() + createListFilesTool(), + createFetchUrlTool() ]; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29defc0..dd1bfb5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,12 @@ importers: figlet: specifier: ^1.9.3 version: 1.9.3 + json5: + specifier: ^2.2.3 + version: 2.2.3 + yaml: + specifier: ^2.8.1 + version: 2.8.1 devDependencies: '@types/cli-progress': specifier: ^3.11.6 @@ -38,104 +44,22 @@ importers: version: 4.20.6 vitest: specifier: ^2.0.0 - version: 2.1.9(@types/node@20.19.19)(jsdom@26.1.0)(lightningcss@1.30.2) - - benchmark-report: - dependencies: - '@radix-ui/react-accordion': - specifier: ^1.2.12 - version: 1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-dialog': - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-progress': - specifier: ^1.1.7 - version: 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-separator': - specifier: ^1.1.7 - version: 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': - specifier: ^1.2.3 - version: 1.2.3(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-tooltip': - specifier: ^1.2.8 - version: 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@rsbuild/plugin-node-polyfill': - specifier: ^1.4.2 - version: 1.4.2(@rsbuild/core@1.5.17) - '@tailwindcss/postcss': - specifier: ^4.1.16 - version: 4.1.16 - '@tanstack/react-router': - specifier: ^1.133.25 - version: 1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@tanstack/router-devtools': - specifier: ^1.133.25 - version: 1.133.25(@tanstack/react-router@1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.133.25)(@types/node@20.19.19)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6)(yaml@2.8.1) - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - lucide-react: - specifier: ^0.546.0 - version: 0.546.0(react@19.2.0) - postcss: - specifier: ^8.5.6 - version: 8.5.6 - react: - specifier: ^19.2.0 - version: 19.2.0 - react-dom: - specifier: ^19.2.0 - version: 19.2.0(react@19.2.0) - recharts: - specifier: 2.15.4 - version: 2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - sql.js: - specifier: ^1.13.0 - version: 1.13.0 - tailwind-merge: - specifier: ^3.3.1 - version: 3.3.1 - tailwindcss: - specifier: ^4.1.16 - version: 4.1.16 - tw-animate-css: - specifier: ^1.4.0 - version: 1.4.0 - devDependencies: - '@rsbuild/core': - specifier: ^1.5.14 - version: 1.5.17 - '@rsbuild/plugin-react': - specifier: ^1.4.1 - version: 1.4.1(@rsbuild/core@1.5.17) - '@tanstack/router-plugin': - specifier: ^1.133.25 - version: 1.133.25(@rsbuild/core@1.5.17)(@tanstack/react-router@1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@7.1.12(@types/node@20.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1)) - '@types/react': - specifier: ^19.2.2 - version: 19.2.2 - '@types/react-dom': - specifier: ^19.2.1 - version: 19.2.2(@types/react@19.2.2) - '@types/sql.js': - specifier: ^1.4.9 - version: 1.4.9 - typescript: - specifier: ^5.9.3 - version: 5.9.3 + version: 2.1.9(@types/node@24.10.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.1(@types/node@24.10.1)(typescript@5.9.3)) packages/agent-adapters: dependencies: '@anthropic-ai/sdk': specifier: ^0.65.0 version: 0.65.0(zod@3.25.76) + agency-prompt-creator: + specifier: link:../../../agency-prompt-creator + version: link:../../../agency-prompt-creator dotenv: specifier: ^16.0.0 version: 16.6.1 + json5: + specifier: ^2.2.3 + version: 2.2.3 openai: specifier: ^4.0.0 version: 4.104.0(ws@8.18.3)(zod@3.25.76) @@ -187,6 +111,9 @@ importers: '@types/node': specifier: ^20.14.10 version: 20.19.19 + '@types/semver': + specifier: ^7.7.1 + version: 7.7.1 typescript: specifier: ^5.5.4 version: 5.9.3 @@ -219,6 +146,82 @@ importers: specifier: ^5.5.4 version: 5.9.3 + results/workspaces/shadcn-generate-vite-shadcn-generate-vite-Ylq9Pj/shadcn-project: + dependencies: + '@radix-ui/react-slot': + specifier: ^1.2.4 + version: 1.2.4(@types/react@19.2.4)(react@19.2.0) + '@tailwindcss/postcss': + specifier: ^4.1.17 + version: 4.1.17 + autoprefixer: + specifier: ^10.4.22 + version: 10.4.22(postcss@8.5.6) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.553.0 + version: 0.553.0(react@19.2.0) + postcss: + specifier: ^8.5.6 + version: 8.5.6 + react: + specifier: ^19.2.0 + version: 19.2.0 + react-dom: + specifier: ^19.2.0 + version: 19.2.0(react@19.2.0) + tailwind-merge: + specifier: ^3.4.0 + version: 3.4.0 + tailwindcss: + specifier: ^4.1.17 + version: 4.1.17 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@4.1.17) + devDependencies: + '@eslint/js': + specifier: ^9.39.1 + version: 9.39.1 + '@types/node': + specifier: ^24.10.0 + version: 24.10.1 + '@types/react': + specifier: ^19.2.2 + version: 19.2.4 + '@types/react-dom': + specifier: ^19.2.2 + version: 19.2.3(@types/react@19.2.4) + '@vitejs/plugin-react': + specifier: ^5.1.0 + version: 5.1.1(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1)) + eslint: + specifier: ^9.39.1 + version: 9.39.1(jiti@2.6.1) + eslint-plugin-react-hooks: + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-refresh: + specifier: ^0.4.24 + version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) + globals: + specifier: ^16.5.0 + version: 16.5.0 + typescript: + specifier: ~5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.46.3 + version: 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + vite: + specifier: ^7.2.2 + version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1) + packages: '@alloc/quick-lru@5.2.0': @@ -253,28 +256,14 @@ packages: resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} - engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.5': - resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.28.5': - resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} @@ -285,24 +274,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.27.1': - resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} - engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} - '@babel/helper-replace-supers@7.27.1': - resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -324,32 +299,14 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-jsx@7.27.1': - resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.27.1': - resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-commonjs@7.27.1': - resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.28.5': - resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/preset-typescript@7.28.5': - resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -404,23 +361,14 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@emnapi/core@1.6.0': - resolution: {integrity: sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==} - - '@emnapi/runtime@1.6.0': - resolution: {integrity: sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==} - - '@emnapi/wasi-threads@1.1.0': - resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.11': - resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -431,8 +379,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.11': - resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -443,8 +391,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.11': - resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -455,8 +403,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.11': - resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -467,8 +415,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.11': - resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -479,8 +427,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.11': - resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -491,8 +439,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.11': - resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -503,8 +451,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.11': - resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -515,8 +463,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.11': - resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -527,8 +475,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.11': - resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -539,8 +487,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.11': - resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -551,8 +499,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.11': - resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -563,8 +511,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.11': - resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -575,8 +523,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.11': - resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -587,8 +535,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.11': - resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -599,8 +547,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.11': - resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -611,14 +559,14 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.11': - resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.11': - resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -629,14 +577,14 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.11': - resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.11': - resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -647,14 +595,14 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.11': - resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.11': - resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -665,8 +613,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.11': - resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -677,8 +625,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.11': - resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -689,8 +637,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.11': - resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -701,26 +649,100 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.11': - resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@floating-ui/core@1.7.3': - resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@floating-ui/dom@1.7.4': - resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@floating-ui/react-dom@2.1.6': - resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + '@eslint/js@9.39.1': + resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} + engines: {node: '>=18'} + + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} + engines: {node: '>=18'} peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} + engines: {node: '>=18'} - '@floating-ui/utils@0.2.10': - resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -738,26 +760,9 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@module-federation/error-codes@0.18.0': - resolution: {integrity: sha512-Woonm8ehyVIUPXChmbu80Zj6uJkC0dD9SJUZ/wOPtO8iiz/m+dkrOugAuKgoiR6qH4F+yorWila954tBz4uKsQ==} - - '@module-federation/runtime-core@0.18.0': - resolution: {integrity: sha512-ZyYhrDyVAhUzriOsVfgL6vwd+5ebYm595Y13KeMf6TKDRoUHBMTLGQ8WM4TDj8JNsy7LigncK8C03fn97of0QQ==} - - '@module-federation/runtime-tools@0.18.0': - resolution: {integrity: sha512-fSga9o4t1UfXNV/Kh6qFvRyZpPp3EHSPRISNeyT8ZoTpzDNiYzhtw0BPUSSD8m6C6XQh2s/11rI4g80UY+d+hA==} - - '@module-federation/runtime@0.18.0': - resolution: {integrity: sha512-+C4YtoSztM7nHwNyZl6dQKGUVJdsPrUdaf3HIKReg/GQbrt9uvOlUWo2NXMZ8vDAnf/QRrpSYAwXHmWDn9Obaw==} - - '@module-federation/sdk@0.18.0': - resolution: {integrity: sha512-Lo/Feq73tO2unjmpRfyyoUkTVoejhItXOk/h5C+4cistnHbTV8XHrW/13fD5e1Iu60heVdAhhelJd6F898Ve9A==} - - '@module-federation/webpack-bundler-runtime@0.18.0': - resolution: {integrity: sha512-TEvErbF+YQ+6IFimhUYKK3a5wapD90d90sLsNpcu2kB3QGT7t4nIluE25duXuZDVUKLz86tEPrza/oaaCWTpvQ==} - - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@mswjs/interceptors@0.40.0': + resolution: {integrity: sha512-EFd6cVbHsgLa6wa4RljGj6Wk75qoHxUSyc5asLyyPSyuhIcdS2Q3Phw6ImS1q+CkALthJRShiYfKANcQMuMqsQ==} + engines: {node: '>=18'} '@next/env@14.2.33': resolution: {integrity: sha512-CgVHNZ1fRIlxkLhIX22flAZI/HmpDaZ8vwyJ/B0SDPTBuLZ1PJ+DWMjCHhqnExfmSQzA/PbZi8OAc7PAq2w9IA==} @@ -828,530 +833,150 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - '@radix-ui/primitive@1.1.3': - resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - - '@radix-ui/react-accordion@1.2.12': - resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-arrow@1.1.7': - resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-collapsible@1.1.12': - resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rolldown/pluginutils@1.0.0-beta.47': + resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} - '@radix-ui/react-collection@1.1.7': - resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rollup/rollup-android-arm-eabi@4.53.2': + resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} + cpu: [arm] + os: [android] - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@rollup/rollup-android-arm64@4.53.2': + resolution: {integrity: sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==} + cpu: [arm64] + os: [android] - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@rollup/rollup-darwin-arm64@4.53.2': + resolution: {integrity: sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==} + cpu: [arm64] + os: [darwin] - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rollup/rollup-darwin-x64@4.53.2': + resolution: {integrity: sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==} + cpu: [x64] + os: [darwin] - '@radix-ui/react-direction@1.1.1': - resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@rollup/rollup-freebsd-arm64@4.53.2': + resolution: {integrity: sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==} + cpu: [arm64] + os: [freebsd] - '@radix-ui/react-dismissable-layer@1.1.11': - resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rollup/rollup-freebsd-x64@4.53.2': + resolution: {integrity: sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==} + cpu: [x64] + os: [freebsd] - '@radix-ui/react-focus-guards@1.1.3': - resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} + cpu: [arm] + os: [linux] - '@radix-ui/react-focus-scope@1.1.7': - resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rollup/rollup-linux-arm-musleabihf@4.53.2': + resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} + cpu: [arm] + os: [linux] - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@rollup/rollup-linux-arm64-gnu@4.53.2': + resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} + cpu: [arm64] + os: [linux] - '@radix-ui/react-popper@1.2.8': - resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rollup/rollup-linux-arm64-musl@4.53.2': + resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} + cpu: [arm64] + os: [linux] - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@rollup/rollup-linux-loong64-gnu@4.53.2': + resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} + cpu: [loong64] + os: [linux] - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-progress@1.1.7': - resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-separator@1.1.7': - resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-tooltip@1.2.8': - resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.1': - resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.1': - resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.2.3': - resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/rect@1.1.1': - resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - - '@rollup/rollup-android-arm-eabi@4.52.5': - resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.52.5': - resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.52.5': - resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.52.5': - resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.52.5': - resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.52.5': - resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': - resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.52.5': - resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.52.5': - resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.52.5': - resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loong64-gnu@4.52.5': - resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.52.5': - resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + '@rollup/rollup-linux-ppc64-gnu@4.53.2': + resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.5': - resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + '@rollup/rollup-linux-riscv64-gnu@4.53.2': + resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.5': - resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + '@rollup/rollup-linux-riscv64-musl@4.53.2': + resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.5': - resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + '@rollup/rollup-linux-s390x-gnu@4.53.2': + resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.5': - resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + '@rollup/rollup-linux-x64-gnu@4.53.2': + resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.5': - resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + '@rollup/rollup-linux-x64-musl@4.53.2': + resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.5': - resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + '@rollup/rollup-openharmony-arm64@4.53.2': + resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.5': - resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + '@rollup/rollup-win32-arm64-msvc@4.53.2': + resolution: {integrity: sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.5': - resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + '@rollup/rollup-win32-ia32-msvc@4.53.2': + resolution: {integrity: sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.5': - resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + '@rollup/rollup-win32-x64-gnu@4.53.2': + resolution: {integrity: sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.5': - resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + '@rollup/rollup-win32-x64-msvc@4.53.2': + resolution: {integrity: sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==} cpu: [x64] os: [win32] - '@rsbuild/core@1.5.17': - resolution: {integrity: sha512-tHa4puv+pEooQvSewu/K5sm270nkVPcP07Ioz1c+fbFCrFpiZWV5XumgznilS80097glUrieN+9xTbIHGXjThQ==} - engines: {node: '>=18.12.0'} - hasBin: true - - '@rsbuild/plugin-node-polyfill@1.4.2': - resolution: {integrity: sha512-Vq1So9ZQa0nz9scgDfAa/t7bLPl7lYBQw6n3Qhd8hGUpPXacSjr0xLdJ2c20EDihOO4qJKN4zlomBYVjCHPy0A==} - peerDependencies: - '@rsbuild/core': 1.x - peerDependenciesMeta: - '@rsbuild/core': - optional: true - - '@rsbuild/plugin-react@1.4.1': - resolution: {integrity: sha512-kahvnfRPQTsG0tReR6h0KuVfyjKJfpv/PoU5qW5SDkON7vmbGn8Jx7l3fI+yU3lR/7qDiAO19QnNjkqxPsFT3Q==} - peerDependencies: - '@rsbuild/core': 1.x - - '@rspack/binding-darwin-arm64@1.5.8': - resolution: {integrity: sha512-spJfpOSN3f7V90ic45/ET2NKB2ujAViCNmqb0iGurMNQtFRq+7Kd+jvVKKGXKBHBbsQrFhidSWbbqy2PBPGK8g==} - cpu: [arm64] - os: [darwin] - - '@rspack/binding-darwin-x64@1.5.8': - resolution: {integrity: sha512-YFOzeL1IBknBcri8vjUp43dfUBylCeQnD+9O9p0wZmLAw7DtpN5JEOe2AkGo8kdTqJjYKI+cczJPKIw6lu1LWw==} - cpu: [x64] - os: [darwin] - - '@rspack/binding-linux-arm64-gnu@1.5.8': - resolution: {integrity: sha512-UAWCsOnpkvy8eAVRo0uipbHXDhnoDq5zmqWTMhpga0/a3yzCp2e+fnjZb/qnFNYb5MeL0O1mwMOYgn1M3oHILQ==} - cpu: [arm64] - os: [linux] - - '@rspack/binding-linux-arm64-musl@1.5.8': - resolution: {integrity: sha512-GnSvGT4GjokPSD45cTtE+g7LgghuxSP1MRmvd+Vp/I8pnxTVSTsebRod4TAqyiv+l11nuS8yqNveK9qiOkBLWw==} - cpu: [arm64] - os: [linux] - - '@rspack/binding-linux-x64-gnu@1.5.8': - resolution: {integrity: sha512-XLxh5n/pzUfxsugz/8rVBv+Tx2nqEM+9rharK69kfooDsQNKyz7PANllBQ/v4svJ+W0BRHnDL4qXSGdteZeEjA==} - cpu: [x64] - os: [linux] - - '@rspack/binding-linux-x64-musl@1.5.8': - resolution: {integrity: sha512-gE0+MZmwF+01p9/svpEESkzkLpBkVUG2o03YMpwXYC/maeRRhWvF8BJ7R3i/Ls/jFGSE87dKX5NbRLVzqksq/w==} - cpu: [x64] - os: [linux] - - '@rspack/binding-wasm32-wasi@1.5.8': - resolution: {integrity: sha512-cfg3niNHeJuxuml1Vy9VvaJrI/5TakzoaZvKX2g5S24wfzR50Eyy4JAsZ+L2voWQQp1yMJbmPYPmnTCTxdJQBQ==} - cpu: [wasm32] - - '@rspack/binding-win32-arm64-msvc@1.5.8': - resolution: {integrity: sha512-7i3ZTHFXKfU/9Jm9XhpMkrdkxO7lfeYMNVEGkuU5dyBfRMQj69dRgPL7zJwc2plXiqu9LUOl+TwDNTjap7Q36g==} - cpu: [arm64] - os: [win32] - - '@rspack/binding-win32-ia32-msvc@1.5.8': - resolution: {integrity: sha512-7ZPPWO11J+soea1+mnfaPpQt7GIodBM7A86dx6PbXgVEoZmetcWPrCF2NBfXxQWOKJ9L3RYltC4z+ZyXRgMOrw==} - cpu: [ia32] - os: [win32] - - '@rspack/binding-win32-x64-msvc@1.5.8': - resolution: {integrity: sha512-N/zXQgzIxME3YUzXT8qnyzxjqcnXudWOeDh8CAG9zqTCnCiy16SFfQ/cQgEoLlD9geQntV6jx2GbDDI5kpDGMQ==} - cpu: [x64] - os: [win32] - - '@rspack/binding@1.5.8': - resolution: {integrity: sha512-/91CzhRl9r5BIQCgGsS7jA6MDbw1I2BQpbfcUUdkdKl2P79K3Zo/Mw/TvKzS86catwLaUQEgkGRmYawOfPg7ow==} - - '@rspack/core@1.5.8': - resolution: {integrity: sha512-sUd2LfiDhqYVfvknuoz0+/c+wSpn693xotnG5g1CSWKZArbtwiYzBIVnNlcHGmuoBRsnj/TkSq8dTQ7gwfBroQ==} - engines: {node: '>=18.12.0'} - peerDependencies: - '@swc/helpers': '>=0.5.1' - peerDependenciesMeta: - '@swc/helpers': - optional: true - - '@rspack/lite-tapable@1.0.1': - resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} - engines: {node: '>=16.0.0'} - - '@rspack/plugin-react-refresh@1.5.2': - resolution: {integrity: sha512-uTbN6P01LPdQOnl5YNwHkN4hDsb9Sb5nIetQb55mPyFiJnu9MQetmBUm+tmh8JJg0QPv4Ew7tXgi4hjpHFY3Rw==} - peerDependencies: - react-refresh: '>=0.10.0 <1.0.0' - webpack-hot-middleware: 2.x - peerDependenciesMeta: - webpack-hot-middleware: - optional: true - '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} @@ -1359,71 +984,68 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.17': - resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} - '@swc/helpers@0.5.5': resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} - '@tailwindcss/node@4.1.16': - resolution: {integrity: sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==} + '@tailwindcss/node@4.1.17': + resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} - '@tailwindcss/oxide-android-arm64@4.1.16': - resolution: {integrity: sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==} + '@tailwindcss/oxide-android-arm64@4.1.17': + resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.16': - resolution: {integrity: sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==} + '@tailwindcss/oxide-darwin-arm64@4.1.17': + resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.16': - resolution: {integrity: sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==} + '@tailwindcss/oxide-darwin-x64@4.1.17': + resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.16': - resolution: {integrity: sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==} + '@tailwindcss/oxide-freebsd-x64@4.1.17': + resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16': - resolution: {integrity: sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.16': - resolution: {integrity: sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.16': - resolution: {integrity: sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.16': - resolution: {integrity: sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.16': - resolution: {integrity: sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==} + '@tailwindcss/oxide-linux-x64-musl@4.1.17': + resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.16': - resolution: {integrity: sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==} + '@tailwindcss/oxide-wasm32-wasi@4.1.17': + resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -1434,116 +1056,36 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.16': - resolution: {integrity: sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.16': - resolution: {integrity: sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.16': - resolution: {integrity: sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==} + '@tailwindcss/oxide@4.1.17': + resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.1.16': - resolution: {integrity: sha512-Qn3SFGPXYQMKR/UtqS+dqvPrzEeBZHrFA92maT4zijCVggdsXnDBMsPFJo1eArX3J+O+Gi+8pV4PkqjLCNBk3A==} - - '@tanstack/history@1.133.19': - resolution: {integrity: sha512-Y866qBVVprdQkmO0/W1AFBI8tiQy398vFeIwP+VrRWCOzs3VecxSVzAvaOM4iHfkJz81fFAZMhLLjDVoPikD+w==} - engines: {node: '>=12'} - - '@tanstack/react-router-devtools@1.133.25': - resolution: {integrity: sha512-MidzhULcDABhc7qRz4RAN2ktDsmfAzhTxZMCHDJASmqeDD6NH986I4HEYhEbZoaC+RtfkV1bHt18CFZVn/LBKw==} - engines: {node: '>=12'} - peerDependencies: - '@tanstack/react-router': ^1.133.25 - react: '>=18.0.0 || >=19.0.0' - react-dom: '>=18.0.0 || >=19.0.0' - - '@tanstack/react-router@1.133.25': - resolution: {integrity: sha512-NGm5b8KgCdlNr6H/bhtyqFOGflqrbF7c2GGha9E2Ab8Wt1iajnXK+Ba7Y7+Am+zFVCrF5+AL6NCq+8Z1qKO8KA==} - engines: {node: '>=12'} - peerDependencies: - react: '>=18.0.0 || >=19.0.0' - react-dom: '>=18.0.0 || >=19.0.0' - - '@tanstack/react-store@0.7.7': - resolution: {integrity: sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - '@tanstack/router-core@1.133.25': - resolution: {integrity: sha512-u3lBGKsGbt0i66qTBaqMCGPnE9yZrxeMi2Al3ZRaikOmSnTeuxXExbX+QAW4cI6GtFigdI3Ec8KW5M29Uft7Hg==} - engines: {node: '>=12'} - - '@tanstack/router-devtools-core@1.133.25': - resolution: {integrity: sha512-Bz1ASxeoJRzm2K7OTVU0aw5ZsVwA2Q/ugp3HheIAu3UlSG8teLiymu/l9avHFPht+bt5xH5yGkpB2SqhArtndA==} - engines: {node: '>=12'} - peerDependencies: - '@tanstack/router-core': ^1.133.25 - csstype: ^3.0.10 - solid-js: '>=1.9.5' - tiny-invariant: ^1.3.3 - peerDependenciesMeta: - csstype: - optional: true - - '@tanstack/router-devtools@1.133.25': - resolution: {integrity: sha512-J5BVup9eZYhM1RLcAZVF4A5EZGyXDKV05LXbhfwPqh7321U7o7COt0zLW8JKTeelPYoD3K9a3oTBf54J89OuVQ==} - engines: {node: '>=12'} - peerDependencies: - '@tanstack/react-router': ^1.133.25 - csstype: ^3.0.10 - react: '>=18.0.0 || >=19.0.0' - react-dom: '>=18.0.0 || >=19.0.0' - peerDependenciesMeta: - csstype: - optional: true - - '@tanstack/router-generator@1.133.25': - resolution: {integrity: sha512-tHEqokiyFhcwZCBu52PYQ+J9zuUNbKuYb/taYjXHKDZRGXhmC+H5raJTucwGmaCh/JFuBNgZ9mq6kmVqwb6RLw==} - engines: {node: '>=12'} - - '@tanstack/router-plugin@1.133.25': - resolution: {integrity: sha512-DgwHoHmL+6qgrM+Fu0fnKINqf0e7u096W/6cwMq33ADtD+LwjQbJ6D8KyL1TscNjOMpp+NrFWJYS/+lspXwnDg==} - engines: {node: '>=12'} - peerDependencies: - '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.133.25 - vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' - vite-plugin-solid: ^2.11.10 - webpack: '>=5.92.0' - peerDependenciesMeta: - '@rsbuild/core': - optional: true - '@tanstack/react-router': - optional: true - vite: - optional: true - vite-plugin-solid: - optional: true - webpack: - optional: true + '@tailwindcss/postcss@4.1.17': + resolution: {integrity: sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==} - '@tanstack/router-utils@1.133.19': - resolution: {integrity: sha512-WEp5D2gPxvlLDRXwD/fV7RXjYtqaqJNXKB/L6OyZEbT+9BG/Ib2d7oG9GSUZNNMGPGYAlhBUOi3xutySsk6rxA==} - engines: {node: '>=12'} + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@tanstack/store@0.7.7': - resolution: {integrity: sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==} + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - '@tanstack/virtual-file-routes@1.133.19': - resolution: {integrity: sha512-IKwZENsK7owmW1Lm5FhuHegY/SyQ8KqtL/7mTSnzoKJgfzhrrf9qwKB1rmkKkt+svUuy/Zw3uVEpZtUzQruWtA==} - engines: {node: '>=12'} + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} '@types/better-sqlite3@7.6.13': resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} @@ -1551,42 +1093,15 @@ packages: '@types/cli-progress@3.11.6': resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==} - '@types/d3-array@3.2.2': - resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} - - '@types/d3-color@3.1.3': - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - - '@types/d3-ease@3.0.2': - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - - '@types/d3-interpolate@3.0.4': - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - - '@types/d3-path@3.1.1': - resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} - - '@types/d3-scale@4.0.9': - resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} - - '@types/d3-shape@3.1.7': - resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} - - '@types/d3-time@3.0.4': - resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} - - '@types/d3-timer@3.0.2': - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - - '@types/emscripten@1.41.4': - resolution: {integrity: sha512-ECf0qTibhAi2Z0K6FIY96CvBTVkVIuVunOfbTUgbaAmGmbwsc33dbK9KZPROWsmzHotddy6C5pIqYqOmsBoJEw==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/figlet@1.7.0': resolution: {integrity: sha512-KwrT7p/8Eo3Op/HBSIwGXOsTZKYiM9NpWRBJ5sVjWP/SmlS+oxxRvJht/FNAtliJvja44N3ul1yATgohnVBV0Q==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node-fetch@2.6.13': resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} @@ -1596,20 +1111,91 @@ packages: '@types/node@20.19.19': resolution: {integrity: sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg==} - '@types/react-dom@19.2.2': - resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.2': - resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} + '@types/react@19.2.4': + resolution: {integrity: sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} - '@types/sql.js@1.4.9': - resolution: {integrity: sha512-ep8b36RKHlgWPqjNG9ToUrPiwkhwh0AEzy883mO5Xnd+cL6VBH1EvSjBAAuxLUFF2Vn/moE3Me6v9E1Lo+48GQ==} + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@typescript-eslint/eslint-plugin@8.46.4': + resolution: {integrity: sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.46.4 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.46.4': + resolution: {integrity: sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.46.4': + resolution: {integrity: sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.46.4': + resolution: {integrity: sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.46.4': + resolution: {integrity: sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.46.4': + resolution: {integrity: sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.46.4': + resolution: {integrity: sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.46.4': + resolution: {integrity: sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.46.4': + resolution: {integrity: sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.46.4': + resolution: {integrity: sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react@5.1.1': + resolution: {integrity: sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitest/expect@2.1.9': resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} @@ -1643,6 +1229,11 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -1656,117 +1247,72 @@ packages: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansis@4.2.0: - resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} - engines: {node: '>=14'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - aria-hidden@1.2.6: - resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} - engines: {node: '>=10'} - - asn1.js@4.10.1: - resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} - assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-types@0.16.1: - resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} - engines: {node: '>=4'} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + autoprefixer@10.4.22: + resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 - babel-dead-code-elimination@1.0.10: - resolution: {integrity: sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.8.20: - resolution: {integrity: sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==} + baseline-browser-mapping@2.8.28: + resolution: {integrity: sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ==} hasBin: true better-sqlite3@12.4.1: resolution: {integrity: sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==} engines: {node: 20.x || 22.x || 23.x || 24.x} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - bn.js@4.12.2: - resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - bn.js@5.2.2: - resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - - browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} - - browserify-cipher@1.0.1: - resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} - - browserify-des@1.0.2: - resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} - - browserify-rsa@4.1.1: - resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} - engines: {node: '>= 0.10'} - - browserify-sign@4.2.5: - resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} - engines: {node: '>= 0.10'} - - browserify-zlib@0.2.0: - resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - - browserslist@4.27.0: - resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} + browserslist@4.28.0: + resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - builtin-status-codes@3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -1779,21 +1325,24 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} caniuse-lite@1.0.30001751: resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} + caniuse-lite@1.0.30001754: + resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} + chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -1802,17 +1351,9 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - cipher-base@1.0.7: - resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} - engines: {node: '>= 0.10'} - class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -1820,13 +1361,28 @@ packages: resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==} engines: {node: '>=4'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1835,36 +1391,19 @@ packages: resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==} engines: {node: '>=20'} - console-browserify@1.2.0: - resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} - - constants-browserify@1.0.0: - resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-es@2.0.0: - resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} - - core-js@3.46.0: - resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - create-ecdh@4.0.4: - resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} - - create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} - - create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} - crypto-browserify@3.12.1: - resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} - engines: {node: '>= 0.10'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} cssstyle@4.6.0: resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} @@ -1873,50 +1412,6 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - d3-array@3.2.4: - resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} - engines: {node: '>=12'} - - d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - - d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - - d3-format@3.1.0: - resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} - engines: {node: '>=12'} - - d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} - - d3-path@3.1.0: - resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} - engines: {node: '>=12'} - - d3-scale@4.0.2: - resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} - engines: {node: '>=12'} - - d3-shape@3.2.0: - resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} - engines: {node: '>=12'} - - d3-time-format@4.1.0: - resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} - engines: {node: '>=12'} - - d3-time@3.1.0: - resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} - engines: {node: '>=12'} - - d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} - data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -1930,9 +1425,6 @@ packages: supports-color: optional: true - decimal.js-light@2.5.1: - resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} @@ -1948,42 +1440,17 @@ packages: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - des.js@1.1.0: - resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - - diff@8.0.2: - resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} - engines: {node: '>=0.3.1'} - - diffie-hellman@5.0.3: - resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} - - dom-helpers@5.2.1: - resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - - domain-browser@5.7.0: - resolution: {integrity: sha512-edTFu0M/7wO1pXY6GDxVNVW086uqwWYIHP98txhcPyV995X21JIH2DtYp33sQJOupYoXKe9RwTw2Ya2vWaquTQ==} - engines: {node: '>=4'} - dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -1992,11 +1459,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.240: - resolution: {integrity: sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==} - - elliptic@6.6.1: - resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + electron-to-chromium@1.5.250: + resolution: {integrity: sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2012,9 +1476,6 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - error-stack-parser@2.1.4: - resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -2039,8 +1500,8 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.25.11: - resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} hasBin: true @@ -2048,28 +1509,70 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.4.24: + resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==} + peerDependencies: + eslint: '>=8.40' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.1: + resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} - expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -2078,14 +1581,19 @@ packages: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} - fast-equals@5.3.2: - resolution: {integrity: sha512-6rxyATwPCkaFIL3JLqw8qXqMpIZ942pTX/tbQFkRsDGblS8tNGtlUauA/+mt6RUfqn/4MoEr+WDkYoIQbibWuQ==} - engines: {node: '>=6.0.0'} + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -2103,6 +1611,10 @@ packages: engines: {node: '>= 17.0.0'} hasBin: true + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -2110,9 +1622,16 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} form-data-encoder@1.7.2: resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} @@ -2125,6 +1644,9 @@ packages: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} engines: {node: '>= 12.20'} + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -2136,28 +1658,24 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-tsconfig@4.12.0: - resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} @@ -2166,15 +1684,22 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + globby@14.1.0: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} - goober@2.1.18: - resolution: {integrity: sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==} - peerDependencies: - csstype: ^3.0.10 - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2182,8 +1707,16 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + graphql@16.12.0: + resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} @@ -2193,38 +1726,27 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hash-base@3.0.5: - resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} - engines: {node: '>= 0.10'} - - hash-base@3.1.2: - resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} - engines: {node: '>= 0.8'} - - hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} - html-entities@2.6.0: - resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} - http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - https-browserify@1.0.0: - resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} - https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -2239,32 +1761,28 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + ignore@7.0.5: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} - engines: {node: '>=12'} - - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2273,17 +1791,12 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} - engines: {node: '>= 0.4'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} @@ -2292,23 +1805,8 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isbot@5.1.31: - resolution: {integrity: sha512-DPgQshehErHAqSCKDb3rNW03pa2wS/v5evvUqtxt6TTnHRqAG8FdzcSSJs9656pK6Y+NT7K9R4acEYXLHYfpUQ==} - engines: {node: '>=18'} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} @@ -2317,6 +1815,10 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + jsdom@26.1.0: resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} engines: {node: '>=18'} @@ -2331,15 +1833,31 @@ packages: engines: {node: '>=6'} hasBin: true + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-schema-to-ts@3.1.1: resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} engines: {node: '>=16'} + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.30.2: resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} engines: {node: '>= 12.0.0'} @@ -2410,12 +1928,12 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} @@ -2426,21 +1944,21 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@0.546.0: - resolution: {integrity: sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ==} + lucide-react@0.553.0: + resolution: {integrity: sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2449,10 +1967,6 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - miller-rabin@4.0.1: - resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} - hasBin: true - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2465,11 +1979,12 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -2480,6 +1995,20 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.12.1: + resolution: {integrity: sha512-arzsi9IZjjByiEw21gSUP82qHM8zkV69nNpWV6W4z72KiLvsDWoOp678ORV6cNfU/JGhlX0SsnD4oXo9gI6I2A==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2488,6 +2017,9 @@ packages: napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + next@14.2.33: resolution: {integrity: sha512-GiKHLsD00t4ACm1p00VgrI0rUFAC9cRDGReKyERlM57aeEZkOQGcZTpIbsGn0b562FTPJWmYfKwplfO9EaT6ng==} engines: {node: '>=18.17.0'} @@ -2506,8 +2038,8 @@ packages: sass: optional: true - node-abi@3.78.0: - resolution: {integrity: sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==} + node-abi@3.80.0: + resolution: {integrity: sha512-LyPuZJcI9HVwzXK1GPxWNzrr+vr8Hp/3UqlmWxxh8p54U1ZbclOqbSog9lWHaCX+dBaiGi6n/hIX+mKu74GmPA==} engines: {node: '>=10'} node-domexception@1.0.0: @@ -2524,36 +2056,16 @@ packages: encoding: optional: true - node-releases@2.0.26: - resolution: {integrity: sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} nwsapi@2.2.22: resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -2569,21 +2081,38 @@ packages: zod: optional: true - os-browserify@0.3.0: - resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} - parse-asn1@5.1.9: - resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} - engines: {node: '>= 0.10'} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} path-type@6.0.0: resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} @@ -2592,17 +2121,10 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.1: resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} - pbkdf2@3.1.5: - resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==} - engines: {node: '>= 0.10'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2614,9 +2136,8 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} @@ -2631,51 +2152,20 @@ packages: engines: {node: '>=10'} hasBin: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} - engines: {node: '>=14'} - hasBin: true - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - - public-encrypt@4.0.3: - resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - - querystring-es3@0.2.1: - resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} - engines: {node: '>=0.4.x'} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - - randomfill@1.0.4: - resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} - rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -2685,104 +2175,38 @@ packages: peerDependencies: react: ^19.2.0 - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - react-refresh@0.17.0: - resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} engines: {node: '>=0.10.0'} - react-remove-scroll-bar@2.3.8: - resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - - react-remove-scroll@2.7.1: - resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - react-smooth@4.0.4: - resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - react-style-singleton@2.2.3: - resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - react-transition-group@4.4.5: - resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} - peerDependencies: - react: '>=16.6.0' - react-dom: '>=16.6.0' - react@19.2.0: resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - recast@0.23.11: - resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} - engines: {node: '>= 4'} - - recharts-scale@0.4.5: - resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} - recharts@2.15.4: - resolution: {integrity: sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==} - engines: {node: '>=14'} - peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + rettime@0.7.0: + resolution: {integrity: sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - ripemd160@2.0.3: - resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} - engines: {node: '>= 0.8'} - - rollup@4.52.5: - resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + rollup@4.53.2: + resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2792,16 +2216,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -2821,47 +2238,21 @@ packages: engines: {node: '>=10'} hasBin: true - seroval-plugins@1.3.3: - resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} - engines: {node: '>=10'} - peerDependencies: - seroval: ^1.0 - - seroval@1.3.2: - resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} - engines: {node: '>=10'} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - - sha.js@2.4.12: - resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} - engines: {node: '>= 0.10'} - hasBin: true - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} @@ -2875,50 +2266,31 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} - solid-js@1.9.9: - resolution: {integrity: sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} - - sql.js@1.13.0: - resolution: {integrity: sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA==} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - stream-browserify@3.0.0: - resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} - - stream-http@3.2.0: - resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} - streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -2930,6 +2302,10 @@ packages: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + styled-jsx@5.1.1: resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -2943,14 +2319,23 @@ packages: babel-plugin-macros: optional: true + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tailwind-merge@3.3.1: - resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + tailwind-merge@3.4.0: + resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} - tailwindcss@4.1.16: - resolution: {integrity: sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==} + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + + tailwindcss@4.1.17: + resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} @@ -2963,16 +2348,6 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} - timers-browserify@2.0.12: - resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} - engines: {node: '>=0.6.0'} - - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - - tiny-warning@1.0.3: - resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2998,13 +2373,16 @@ packages: tldts-core@6.1.86: resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + tldts-core@7.0.17: + resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==} + tldts@6.1.86: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true - to-buffer@1.2.2: - resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} - engines: {node: '>= 0.4'} + tldts@7.0.17: + resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==} + hasBin: true to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -3014,6 +2392,10 @@ packages: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -3024,6 +2406,12 @@ packages: ts-algebra@2.0.0: resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3032,18 +2420,23 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - tty-browserify@0.0.1: - resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} - tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - tw-animate-css@1.4.0: - resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript-eslint@8.46.4: + resolution: {integrity: sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} @@ -3056,13 +2449,15 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - unplugin@2.3.10: - resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} - engines: {node: '>=18.12.0'} + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} update-browserslist-db@1.1.4: resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} @@ -3070,48 +2465,16 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - url@0.11.4: - resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} - engines: {node: '>= 0.4'} - - use-callback-ref@1.3.3: - resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - use-sidecar@1.1.3: - resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - use-sync-external-store@1.6.0: - resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - victory-vendor@36.9.2: - resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} - vite-node@2.1.9: resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3148,8 +2511,8 @@ packages: terser: optional: true - vite@7.1.12: - resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==} + vite@7.2.2: + resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3213,9 +2576,6 @@ packages: jsdom: optional: true - vm-browserify@1.1.2: - resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -3231,9 +2591,6 @@ packages: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} - webpack-virtual-modules@0.6.2: - resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -3249,15 +2606,28 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -3280,9 +2650,9 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -3292,6 +2662,28 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -3350,40 +2742,16 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.3': - dependencies: - '@babel/types': 7.28.5 - '@babel/helper-compilation-targets@7.27.2': dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.27.0 + browserslist: 4.28.0 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/helper-globals@7.28.0': {} - '@babel/helper-member-expression-to-functions@7.28.5': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.28.5 @@ -3400,28 +2768,8 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.27.1': - dependencies: - '@babel/types': 7.28.5 - '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} @@ -3437,45 +2785,15 @@ snapshots: dependencies: '@babel/types': 7.28.5 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color '@babel/runtime@7.28.4': {} @@ -3538,980 +2856,507 @@ snapshots: '@csstools/css-tokenizer@3.0.4': optional: true - '@emnapi/core@1.6.0': - dependencies: - '@emnapi/wasi-threads': 1.1.0 - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.6.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/wasi-threads@1.1.0': - dependencies: - tslib: 2.8.1 - optional: true - '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.25.11': + '@esbuild/aix-ppc64@0.25.12': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.25.11': + '@esbuild/android-arm64@0.25.12': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.25.11': + '@esbuild/android-arm@0.25.12': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.25.11': + '@esbuild/android-x64@0.25.12': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.25.11': + '@esbuild/darwin-arm64@0.25.12': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.25.11': + '@esbuild/darwin-x64@0.25.12': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.25.11': + '@esbuild/freebsd-arm64@0.25.12': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.25.11': + '@esbuild/freebsd-x64@0.25.12': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.25.11': + '@esbuild/linux-arm64@0.25.12': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.25.11': + '@esbuild/linux-arm@0.25.12': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.25.11': + '@esbuild/linux-ia32@0.25.12': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.25.11': + '@esbuild/linux-loong64@0.25.12': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.25.11': + '@esbuild/linux-mips64el@0.25.12': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.25.11': + '@esbuild/linux-ppc64@0.25.12': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.25.11': + '@esbuild/linux-riscv64@0.25.12': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.25.11': + '@esbuild/linux-s390x@0.25.12': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.25.11': + '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.25.11': + '@esbuild/netbsd-arm64@0.25.12': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.25.11': + '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.25.11': + '@esbuild/openbsd-arm64@0.25.12': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.25.11': + '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.25.11': + '@esbuild/openharmony-arm64@0.25.12': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.25.11': + '@esbuild/sunos-x64@0.25.12': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.25.11': + '@esbuild/win32-arm64@0.25.12': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.25.11': + '@esbuild/win32-ia32@0.25.12': optional: true '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.25.11': - optional: true - - '@floating-ui/core@1.7.3': - dependencies: - '@floating-ui/utils': 0.2.10 - - '@floating-ui/dom@1.7.4': - dependencies: - '@floating-ui/core': 1.7.3 - '@floating-ui/utils': 0.2.10 - - '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@floating-ui/dom': 1.7.4 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - - '@floating-ui/utils@0.2.10': {} - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@module-federation/error-codes@0.18.0': {} - - '@module-federation/runtime-core@0.18.0': - dependencies: - '@module-federation/error-codes': 0.18.0 - '@module-federation/sdk': 0.18.0 - - '@module-federation/runtime-tools@0.18.0': - dependencies: - '@module-federation/runtime': 0.18.0 - '@module-federation/webpack-bundler-runtime': 0.18.0 - - '@module-federation/runtime@0.18.0': - dependencies: - '@module-federation/error-codes': 0.18.0 - '@module-federation/runtime-core': 0.18.0 - '@module-federation/sdk': 0.18.0 - - '@module-federation/sdk@0.18.0': {} - - '@module-federation/webpack-bundler-runtime@0.18.0': - dependencies: - '@module-federation/runtime': 0.18.0 - '@module-federation/sdk': 0.18.0 - - '@napi-rs/wasm-runtime@1.0.7': - dependencies: - '@emnapi/core': 1.6.0 - '@emnapi/runtime': 1.6.0 - '@tybys/wasm-util': 0.10.1 - optional: true - - '@next/env@14.2.33': {} - - '@next/swc-darwin-arm64@14.2.33': - optional: true - - '@next/swc-darwin-x64@14.2.33': - optional: true - - '@next/swc-linux-arm64-gnu@14.2.33': - optional: true - - '@next/swc-linux-arm64-musl@14.2.33': - optional: true - - '@next/swc-linux-x64-gnu@14.2.33': - optional: true - - '@next/swc-linux-x64-musl@14.2.33': - optional: true - - '@next/swc-win32-arm64-msvc@14.2.33': - optional: true - - '@next/swc-win32-ia32-msvc@14.2.33': - optional: true - - '@next/swc-win32-x64-msvc@14.2.33': - optional: true - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@opentelemetry/api@1.9.0': + '@esbuild/win32-x64@0.25.12': optional: true - '@radix-ui/primitive@1.1.3': {} - - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) - - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) - - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) - - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) - - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 - - '@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 - - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) - - '@radix-ui/react-direction@1.1.1(@types/react@19.2.2)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 - - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) - - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.2)(react@19.2.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 + eslint: 9.39.1(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@eslint-community/regexpp@4.12.2': {} - '@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.2.0)': + '@eslint/config-array@0.21.1': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 - - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/rect': 1.1.1 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@eslint/config-helpers@0.4.2': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@eslint/core': 0.17.0 - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@eslint/core@0.17.0': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@types/json-schema': 7.0.15 - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@eslint/eslintrc@3.3.1': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@eslint/js@9.39.1': {} - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@eslint/object-schema@2.1.7': {} - '@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.2.0)': + '@eslint/plugin-kit@0.4.1': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 - - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.2.0)': + '@humanfs/node@0.16.7': dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.2.0)': + '@inquirer/ansi@1.0.2': + optional: true + + '@inquirer/confirm@5.1.21(@types/node@24.10.1)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) optionalDependencies: - '@types/react': 19.2.2 + '@types/node': 24.10.1 + optional: true - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.2.0)': + '@inquirer/core@10.3.2(@types/node@24.10.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.10.1) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/react': 19.2.2 + '@types/node': 24.10.1 + optional: true - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.2)(react@19.2.0)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 + '@inquirer/figures@1.0.15': + optional: true + + '@inquirer/type@3.0.10(@types/node@24.10.1)': optionalDependencies: - '@types/react': 19.2.2 + '@types/node': 24.10.1 + optional: true - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.2.0)': + '@jridgewell/gen-mapping@0.3.13': dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.2)(react@19.2.0)': + '@jridgewell/remapping@2.3.5': dependencies: - '@radix-ui/rect': 1.1.1 - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.2)(react@19.2.0)': + '@jridgewell/trace-mapping@0.3.31': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.2 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@mswjs/interceptors@0.40.0': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - '@types/react-dom': 19.2.2(@types/react@19.2.2) + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + optional: true - '@radix-ui/rect@1.1.1': {} + '@next/env@14.2.33': {} - '@rollup/rollup-android-arm-eabi@4.52.5': + '@next/swc-darwin-arm64@14.2.33': optional: true - '@rollup/rollup-android-arm64@4.52.5': + '@next/swc-darwin-x64@14.2.33': optional: true - '@rollup/rollup-darwin-arm64@4.52.5': + '@next/swc-linux-arm64-gnu@14.2.33': optional: true - '@rollup/rollup-darwin-x64@4.52.5': + '@next/swc-linux-arm64-musl@14.2.33': optional: true - '@rollup/rollup-freebsd-arm64@4.52.5': + '@next/swc-linux-x64-gnu@14.2.33': optional: true - '@rollup/rollup-freebsd-x64@4.52.5': + '@next/swc-linux-x64-musl@14.2.33': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + '@next/swc-win32-arm64-msvc@14.2.33': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.5': + '@next/swc-win32-ia32-msvc@14.2.33': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.5': + '@next/swc-win32-x64-msvc@14.2.33': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.5': - optional: true + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 - '@rollup/rollup-linux-loong64-gnu@4.52.5': - optional: true + '@nodelib/fs.stat@2.0.5': {} - '@rollup/rollup-linux-ppc64-gnu@4.52.5': - optional: true + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 - '@rollup/rollup-linux-riscv64-gnu@4.52.5': + '@open-draft/deferred-promise@2.2.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.5': + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.5': + '@open-draft/until@2.1.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.5': + '@opentelemetry/api@1.9.0': optional: true - '@rollup/rollup-linux-x64-musl@4.52.5': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.4)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-slot@1.2.4(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@rolldown/pluginutils@1.0.0-beta.47': {} + + '@rollup/rollup-android-arm-eabi@4.53.2': optional: true - '@rollup/rollup-openharmony-arm64@4.52.5': + '@rollup/rollup-android-arm64@4.53.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.5': + '@rollup/rollup-darwin-arm64@4.53.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.5': + '@rollup/rollup-darwin-x64@4.53.2': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.5': + '@rollup/rollup-freebsd-arm64@4.53.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.5': + '@rollup/rollup-freebsd-x64@4.53.2': optional: true - '@rsbuild/core@1.5.17': - dependencies: - '@rspack/core': 1.5.8(@swc/helpers@0.5.17) - '@rspack/lite-tapable': 1.0.1 - '@swc/helpers': 0.5.17 - core-js: 3.46.0 - jiti: 2.6.1 + '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + optional: true - '@rsbuild/plugin-node-polyfill@1.4.2(@rsbuild/core@1.5.17)': - dependencies: - assert: 2.1.0 - browserify-zlib: 0.2.0 - buffer: 5.7.1 - console-browserify: 1.2.0 - constants-browserify: 1.0.0 - crypto-browserify: 3.12.1 - domain-browser: 5.7.0 - events: 3.3.0 - https-browserify: 1.0.0 - os-browserify: 0.3.0 - path-browserify: 1.0.1 - process: 0.11.10 - punycode: 2.3.1 - querystring-es3: 0.2.1 - readable-stream: 4.7.0 - stream-browserify: 3.0.0 - stream-http: 3.2.0 - string_decoder: 1.3.0 - timers-browserify: 2.0.12 - tty-browserify: 0.0.1 - url: 0.11.4 - util: 0.12.5 - vm-browserify: 1.1.2 - optionalDependencies: - '@rsbuild/core': 1.5.17 + '@rollup/rollup-linux-arm-musleabihf@4.53.2': + optional: true - '@rsbuild/plugin-react@1.4.1(@rsbuild/core@1.5.17)': - dependencies: - '@rsbuild/core': 1.5.17 - '@rspack/plugin-react-refresh': 1.5.2(react-refresh@0.17.0) - react-refresh: 0.17.0 - transitivePeerDependencies: - - webpack-hot-middleware + '@rollup/rollup-linux-arm64-gnu@4.53.2': + optional: true - '@rspack/binding-darwin-arm64@1.5.8': + '@rollup/rollup-linux-arm64-musl@4.53.2': optional: true - '@rspack/binding-darwin-x64@1.5.8': + '@rollup/rollup-linux-loong64-gnu@4.53.2': optional: true - '@rspack/binding-linux-arm64-gnu@1.5.8': + '@rollup/rollup-linux-ppc64-gnu@4.53.2': optional: true - '@rspack/binding-linux-arm64-musl@1.5.8': + '@rollup/rollup-linux-riscv64-gnu@4.53.2': optional: true - '@rspack/binding-linux-x64-gnu@1.5.8': + '@rollup/rollup-linux-riscv64-musl@4.53.2': optional: true - '@rspack/binding-linux-x64-musl@1.5.8': + '@rollup/rollup-linux-s390x-gnu@4.53.2': optional: true - '@rspack/binding-wasm32-wasi@1.5.8': - dependencies: - '@napi-rs/wasm-runtime': 1.0.7 + '@rollup/rollup-linux-x64-gnu@4.53.2': optional: true - '@rspack/binding-win32-arm64-msvc@1.5.8': + '@rollup/rollup-linux-x64-musl@4.53.2': optional: true - '@rspack/binding-win32-ia32-msvc@1.5.8': + '@rollup/rollup-openharmony-arm64@4.53.2': optional: true - '@rspack/binding-win32-x64-msvc@1.5.8': + '@rollup/rollup-win32-arm64-msvc@4.53.2': optional: true - '@rspack/binding@1.5.8': - optionalDependencies: - '@rspack/binding-darwin-arm64': 1.5.8 - '@rspack/binding-darwin-x64': 1.5.8 - '@rspack/binding-linux-arm64-gnu': 1.5.8 - '@rspack/binding-linux-arm64-musl': 1.5.8 - '@rspack/binding-linux-x64-gnu': 1.5.8 - '@rspack/binding-linux-x64-musl': 1.5.8 - '@rspack/binding-wasm32-wasi': 1.5.8 - '@rspack/binding-win32-arm64-msvc': 1.5.8 - '@rspack/binding-win32-ia32-msvc': 1.5.8 - '@rspack/binding-win32-x64-msvc': 1.5.8 - - '@rspack/core@1.5.8(@swc/helpers@0.5.17)': - dependencies: - '@module-federation/runtime-tools': 0.18.0 - '@rspack/binding': 1.5.8 - '@rspack/lite-tapable': 1.0.1 - optionalDependencies: - '@swc/helpers': 0.5.17 + '@rollup/rollup-win32-ia32-msvc@4.53.2': + optional: true - '@rspack/lite-tapable@1.0.1': {} + '@rollup/rollup-win32-x64-gnu@4.53.2': + optional: true - '@rspack/plugin-react-refresh@1.5.2(react-refresh@0.17.0)': - dependencies: - error-stack-parser: 2.1.4 - html-entities: 2.6.0 - react-refresh: 0.17.0 + '@rollup/rollup-win32-x64-msvc@4.53.2': + optional: true '@sindresorhus/merge-streams@2.3.0': {} '@swc/counter@0.1.3': {} - '@swc/helpers@0.5.17': - dependencies: - tslib: 2.8.1 - '@swc/helpers@0.5.5': dependencies: '@swc/counter': 0.1.3 tslib: 2.8.1 - '@tailwindcss/node@4.1.16': + '@tailwindcss/node@4.1.17': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.18.3 jiti: 2.6.1 lightningcss: 1.30.2 - magic-string: 0.30.19 + magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.1.16 + tailwindcss: 4.1.17 - '@tailwindcss/oxide-android-arm64@4.1.16': + '@tailwindcss/oxide-android-arm64@4.1.17': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.16': + '@tailwindcss/oxide-darwin-arm64@4.1.17': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.16': + '@tailwindcss/oxide-darwin-x64@4.1.17': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.16': + '@tailwindcss/oxide-freebsd-x64@4.1.17': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.16': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.16': + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.16': + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.16': + '@tailwindcss/oxide-linux-x64-musl@4.1.17': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.16': + '@tailwindcss/oxide-wasm32-wasi@4.1.17': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.16': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.16': + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': optional: true - '@tailwindcss/oxide@4.1.16': + '@tailwindcss/oxide@4.1.17': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.16 - '@tailwindcss/oxide-darwin-arm64': 4.1.16 - '@tailwindcss/oxide-darwin-x64': 4.1.16 - '@tailwindcss/oxide-freebsd-x64': 4.1.16 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.16 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.16 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.16 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.16 - '@tailwindcss/oxide-linux-x64-musl': 4.1.16 - '@tailwindcss/oxide-wasm32-wasi': 4.1.16 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.16 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.16 - - '@tailwindcss/postcss@4.1.16': + '@tailwindcss/oxide-android-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-x64': 4.1.17 + '@tailwindcss/oxide-freebsd-x64': 4.1.17 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-x64-musl': 4.1.17 + '@tailwindcss/oxide-wasm32-wasi': 4.1.17 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 + + '@tailwindcss/postcss@4.1.17': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.1.16 - '@tailwindcss/oxide': 4.1.16 + '@tailwindcss/node': 4.1.17 + '@tailwindcss/oxide': 4.1.17 postcss: 8.5.6 - tailwindcss: 4.1.16 - - '@tanstack/history@1.133.19': {} - - '@tanstack/react-router-devtools@1.133.25(@tanstack/react-router@1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.133.25)(@types/node@20.19.19)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6)(yaml@2.8.1)': - dependencies: - '@tanstack/react-router': 1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@tanstack/router-devtools-core': 1.133.25(@tanstack/router-core@1.133.25)(@types/node@20.19.19)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6)(yaml@2.8.1) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - vite: 7.1.12(@types/node@20.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1) - transitivePeerDependencies: - - '@tanstack/router-core' - - '@types/node' - - csstype - - jiti - - less - - lightningcss - - sass - - sass-embedded - - solid-js - - stylus - - sugarss - - terser - - tiny-invariant - - tsx - - yaml - - '@tanstack/react-router@1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@tanstack/history': 1.133.19 - '@tanstack/react-store': 0.7.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@tanstack/router-core': 1.133.25 - isbot: 5.1.31 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - - '@tanstack/react-store@0.7.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@tanstack/store': 0.7.7 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - use-sync-external-store: 1.6.0(react@19.2.0) - - '@tanstack/router-core@1.133.25': - dependencies: - '@tanstack/history': 1.133.19 - '@tanstack/store': 0.7.7 - cookie-es: 2.0.0 - seroval: 1.3.2 - seroval-plugins: 1.3.3(seroval@1.3.2) - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - - '@tanstack/router-devtools-core@1.133.25(@tanstack/router-core@1.133.25)(@types/node@20.19.19)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6)(yaml@2.8.1)': - dependencies: - '@tanstack/router-core': 1.133.25 - clsx: 2.1.1 - goober: 2.1.18(csstype@3.1.3) - solid-js: 1.9.9 - tiny-invariant: 1.3.3 - vite: 7.1.12(@types/node@20.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1) - optionalDependencies: - csstype: 3.1.3 - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml + tailwindcss: 4.1.17 - '@tanstack/router-devtools@1.133.25(@tanstack/react-router@1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.133.25)(@types/node@20.19.19)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6)(yaml@2.8.1)': + '@types/babel__core@7.20.5': dependencies: - '@tanstack/react-router': 1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@tanstack/react-router-devtools': 1.133.25(@tanstack/react-router@1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.133.25)(@types/node@20.19.19)(csstype@3.1.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6)(yaml@2.8.1) - clsx: 2.1.1 - goober: 2.1.18(csstype@3.1.3) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - vite: 7.1.12(@types/node@20.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1) - optionalDependencies: - csstype: 3.1.3 - transitivePeerDependencies: - - '@tanstack/router-core' - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - solid-js - - stylus - - sugarss - - terser - - tiny-invariant - - tsx - - yaml - - '@tanstack/router-generator@1.133.25': - dependencies: - '@tanstack/router-core': 1.133.25 - '@tanstack/router-utils': 1.133.19 - '@tanstack/virtual-file-routes': 1.133.19 - prettier: 3.6.2 - recast: 0.23.11 - source-map: 0.7.6 - tsx: 4.20.6 - zod: 3.25.76 - transitivePeerDependencies: - - supports-color + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 - '@tanstack/router-plugin@1.133.25(@rsbuild/core@1.5.17)(@tanstack/react-router@1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@7.1.12(@types/node@20.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1))': + '@types/babel__generator@7.27.0': dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 '@babel/types': 7.28.5 - '@tanstack/router-core': 1.133.25 - '@tanstack/router-generator': 1.133.25 - '@tanstack/router-utils': 1.133.19 - '@tanstack/virtual-file-routes': 1.133.19 - babel-dead-code-elimination: 1.0.10 - chokidar: 3.6.0 - unplugin: 2.3.10 - zod: 3.25.76 - optionalDependencies: - '@rsbuild/core': 1.5.17 - '@tanstack/react-router': 1.133.25(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - vite: 7.1.12(@types/node@20.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1) - transitivePeerDependencies: - - supports-color - '@tanstack/router-utils@1.133.19': + '@types/babel__template@7.4.4': dependencies: - '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 '@babel/parser': 7.28.5 - '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) - ansis: 4.2.0 - diff: 8.0.2 - pathe: 2.0.3 - tinyglobby: 0.2.15 - transitivePeerDependencies: - - supports-color - - '@tanstack/store@0.7.7': {} - - '@tanstack/virtual-file-routes@1.133.19': {} + '@babel/types': 7.28.5 - '@tybys/wasm-util@0.10.1': + '@types/babel__traverse@7.28.0': dependencies: - tslib: 2.8.1 - optional: true + '@babel/types': 7.28.5 '@types/better-sqlite3@7.6.13': dependencies: @@ -4521,63 +3366,148 @@ snapshots: dependencies: '@types/node': 20.19.19 - '@types/d3-array@3.2.2': {} + '@types/estree@1.0.8': {} - '@types/d3-color@3.1.3': {} + '@types/figlet@1.7.0': {} - '@types/d3-ease@3.0.2': {} + '@types/json-schema@7.0.15': {} - '@types/d3-interpolate@3.0.4': + '@types/node-fetch@2.6.13': dependencies: - '@types/d3-color': 3.1.3 + '@types/node': 20.19.19 + form-data: 4.0.4 - '@types/d3-path@3.1.1': {} + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 - '@types/d3-scale@4.0.9': + '@types/node@20.19.19': dependencies: - '@types/d3-time': 3.0.4 + undici-types: 6.21.0 - '@types/d3-shape@3.1.7': + '@types/node@24.10.1': dependencies: - '@types/d3-path': 3.1.1 + undici-types: 7.16.0 - '@types/d3-time@3.0.4': {} + '@types/react-dom@19.2.3(@types/react@19.2.4)': + dependencies: + '@types/react': 19.2.4 - '@types/d3-timer@3.0.2': {} + '@types/react@19.2.4': + dependencies: + csstype: 3.1.3 - '@types/emscripten@1.41.4': {} + '@types/semver@7.7.1': {} - '@types/estree@1.0.8': {} + '@types/statuses@2.0.6': + optional: true - '@types/figlet@1.7.0': {} + '@types/uuid@9.0.8': {} - '@types/node-fetch@2.6.13': + '@typescript-eslint/eslint-plugin@8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@types/node': 20.19.19 - form-data: 4.0.4 + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.4 + '@typescript-eslint/type-utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.4 + eslint: 9.39.1(jiti@2.6.1) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@types/node@18.19.130': + '@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: - undici-types: 5.26.5 + '@typescript-eslint/scope-manager': 8.46.4 + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.4 + debug: 4.4.3 + eslint: 9.39.1(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@types/node@20.19.19': + '@typescript-eslint/project-service@8.46.4(typescript@5.9.3)': dependencies: - undici-types: 6.21.0 + '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) + '@typescript-eslint/types': 8.46.4 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.46.4': + dependencies: + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/visitor-keys': 8.46.4 + + '@typescript-eslint/tsconfig-utils@8.46.4(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.1(jiti@2.6.1) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.46.4': {} + + '@typescript-eslint/typescript-estree@8.46.4(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.46.4(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/visitor-keys': 8.46.4 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@types/react-dom@19.2.2(@types/react@19.2.2)': + '@typescript-eslint/utils@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@types/react': 19.2.2 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.46.4 + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) + eslint: 9.39.1(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@types/react@19.2.2': + '@typescript-eslint/visitor-keys@8.46.4': dependencies: - csstype: 3.1.3 + '@typescript-eslint/types': 8.46.4 + eslint-visitor-keys: 4.2.1 - '@types/sql.js@1.4.9': + '@vitejs/plugin-react@5.1.1(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1))': dependencies: - '@types/emscripten': 1.41.4 - '@types/node': 20.19.19 - - '@types/uuid@9.0.8': {} + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.47 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color '@vitest/expect@2.1.9': dependencies: @@ -4586,13 +3516,14 @@ snapshots: chai: 5.3.3 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@20.19.19)(lightningcss@1.30.2))': + '@vitest/mocker@2.1.9(msw@2.12.1(@types/node@24.10.1)(typescript@5.9.3))(vite@5.4.21(@types/node@24.10.1)(lightningcss@1.30.2))': dependencies: '@vitest/spy': 2.1.9 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 5.4.21(@types/node@20.19.19)(lightningcss@1.30.2) + msw: 2.12.1(@types/node@24.10.1)(typescript@5.9.3) + vite: 5.4.21(@types/node@24.10.1)(lightningcss@1.30.2) '@vitest/pretty-format@2.1.9': dependencies: @@ -4623,6 +3554,10 @@ snapshots: dependencies: event-target-shim: 5.0.1 + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + acorn@8.15.0: {} agent-base@7.1.4: @@ -4632,65 +3567,46 @@ snapshots: dependencies: humanize-ms: 1.2.1 - ansi-regex@5.0.1: {} - - ansis@4.2.0: {} - - anymatch@3.1.3: + ajv@6.12.6: dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 - aria-hidden@1.2.6: - dependencies: - tslib: 2.8.1 + ansi-regex@5.0.1: {} - asn1.js@4.10.1: + ansi-styles@4.3.0: dependencies: - bn.js: 4.12.2 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 + color-convert: 2.0.1 - assert@2.1.0: - dependencies: - call-bind: 1.0.8 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.7 - util: 0.12.5 + argparse@2.0.1: {} assertion-error@2.0.1: {} - ast-types@0.16.1: - dependencies: - tslib: 2.8.1 - asynckit@0.4.0: {} - available-typed-arrays@1.0.7: + autoprefixer@10.4.22(postcss@8.5.6): dependencies: - possible-typed-array-names: 1.1.0 + browserslist: 4.28.0 + caniuse-lite: 1.0.30001754 + fraction.js: 5.3.4 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 - babel-dead-code-elimination@1.0.10: - dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color + balanced-match@1.0.2: {} base64-js@1.5.1: {} - baseline-browser-mapping@2.8.20: {} + baseline-browser-mapping@2.8.28: {} better-sqlite3@12.4.1: dependencies: bindings: 1.5.0 prebuild-install: 7.1.3 - binary-extensions@2.3.0: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 @@ -4701,82 +3617,32 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - bn.js@4.12.2: {} - - bn.js@5.2.2: {} - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - brorand@1.1.0: {} - - browserify-aes@1.2.0: - dependencies: - buffer-xor: 1.0.3 - cipher-base: 1.0.7 - create-hash: 1.2.0 - evp_bytestokey: 1.0.3 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - browserify-cipher@1.0.1: - dependencies: - browserify-aes: 1.2.0 - browserify-des: 1.0.2 - evp_bytestokey: 1.0.3 - - browserify-des@1.0.2: - dependencies: - cipher-base: 1.0.7 - des.js: 1.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - browserify-rsa@4.1.1: + brace-expansion@1.1.12: dependencies: - bn.js: 5.2.2 - randombytes: 2.1.0 - safe-buffer: 5.2.1 + balanced-match: 1.0.2 + concat-map: 0.0.1 - browserify-sign@4.2.5: + brace-expansion@2.0.2: dependencies: - bn.js: 5.2.2 - browserify-rsa: 4.1.1 - create-hash: 1.2.0 - create-hmac: 1.1.7 - elliptic: 6.6.1 - inherits: 2.0.4 - parse-asn1: 5.1.9 - readable-stream: 2.3.8 - safe-buffer: 5.2.1 + balanced-match: 1.0.2 - browserify-zlib@0.2.0: + braces@3.0.3: dependencies: - pako: 1.0.11 + fill-range: 7.1.1 - browserslist@4.27.0: + browserslist@4.28.0: dependencies: - baseline-browser-mapping: 2.8.20 - caniuse-lite: 1.0.30001751 - electron-to-chromium: 1.5.240 - node-releases: 2.0.26 - update-browserslist-db: 1.1.4(browserslist@4.27.0) - - buffer-xor@1.0.3: {} + baseline-browser-mapping: 2.8.28 + caniuse-lite: 1.0.30001754 + electron-to-chromium: 1.5.250 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.28.0) buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - builtin-status-codes@3.0.0: {} - busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -4788,20 +3654,12 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 + callsites@3.1.0: {} caniuse-lite@1.0.30001751: {} + caniuse-lite@1.0.30001754: {} + chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -4810,30 +3668,17 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@5.6.2: {} check-error@2.1.1: {} - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - chownr@1.1.4: {} - cipher-base@1.0.7: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 @@ -4842,64 +3687,44 @@ snapshots: dependencies: string-width: 4.2.3 + cli-width@4.1.0: + optional: true + client-only@0.0.1: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + optional: true + clsx@2.1.1: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 commander@14.0.1: {} - console-browserify@1.2.0: {} - - constants-browserify@1.0.0: {} + concat-map@0.0.1: {} convert-source-map@2.0.0: {} - cookie-es@2.0.0: {} - - core-js@3.46.0: {} - - core-util-is@1.0.3: {} - - create-ecdh@4.0.4: - dependencies: - bn.js: 4.12.2 - elliptic: 6.6.1 - - create-hash@1.2.0: - dependencies: - cipher-base: 1.0.7 - inherits: 2.0.4 - md5.js: 1.3.5 - ripemd160: 2.0.3 - sha.js: 2.4.12 - - create-hmac@1.1.7: - dependencies: - cipher-base: 1.0.7 - create-hash: 1.2.0 - inherits: 2.0.4 - ripemd160: 2.0.3 - safe-buffer: 5.2.1 - sha.js: 2.4.12 + cookie@1.0.2: + optional: true - crypto-browserify@3.12.1: + cross-spawn@7.0.6: dependencies: - browserify-cipher: 1.0.1 - browserify-sign: 4.2.5 - create-ecdh: 4.0.4 - create-hash: 1.2.0 - create-hmac: 1.1.7 - diffie-hellman: 5.0.3 - hash-base: 3.0.5 - inherits: 2.0.4 - pbkdf2: 3.1.5 - public-encrypt: 4.0.3 - randombytes: 2.1.0 - randomfill: 1.0.4 + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 cssstyle@4.6.0: dependencies: @@ -4909,44 +3734,6 @@ snapshots: csstype@3.1.3: {} - d3-array@3.2.4: - dependencies: - internmap: 2.0.3 - - d3-color@3.1.0: {} - - d3-ease@3.0.1: {} - - d3-format@3.1.0: {} - - d3-interpolate@3.0.1: - dependencies: - d3-color: 3.1.0 - - d3-path@3.1.0: {} - - d3-scale@4.0.2: - dependencies: - d3-array: 3.2.4 - d3-format: 3.1.0 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - - d3-shape@3.2.0: - dependencies: - d3-path: 3.1.0 - - d3-time-format@4.1.0: - dependencies: - d3-time: 3.1.0 - - d3-time@3.1.0: - dependencies: - d3-array: 3.2.4 - - d3-timer@3.0.1: {} - data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 @@ -4957,8 +3744,6 @@ snapshots: dependencies: ms: 2.1.3 - decimal.js-light@2.5.1: {} - decimal.js@10.6.0: optional: true @@ -4970,44 +3755,12 @@ snapshots: deep-extend@0.6.0: {} - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 + deep-is@0.1.4: {} delayed-stream@1.0.0: {} - des.js@1.1.0: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - detect-libc@2.1.2: {} - detect-node-es@1.1.0: {} - - diff@8.0.2: {} - - diffie-hellman@5.0.3: - dependencies: - bn.js: 4.12.2 - miller-rabin: 4.0.1 - randombytes: 2.1.0 - - dom-helpers@5.2.1: - dependencies: - '@babel/runtime': 7.28.4 - csstype: 3.1.3 - - domain-browser@5.7.0: {} - dotenv@16.6.1: {} dunder-proto@1.0.1: @@ -5016,17 +3769,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.240: {} - - elliptic@6.6.1: - dependencies: - bn.js: 4.12.2 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 + electron-to-chromium@1.5.250: {} emoji-regex@8.0.0: {} @@ -5042,10 +3785,6 @@ snapshots: entities@6.0.1: optional: true - error-stack-parser@2.1.4: - dependencies: - stackframe: 1.3.4 - es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -5089,59 +3828,133 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.25.11: + esbuild@0.25.12: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.11 - '@esbuild/android-arm': 0.25.11 - '@esbuild/android-arm64': 0.25.11 - '@esbuild/android-x64': 0.25.11 - '@esbuild/darwin-arm64': 0.25.11 - '@esbuild/darwin-x64': 0.25.11 - '@esbuild/freebsd-arm64': 0.25.11 - '@esbuild/freebsd-x64': 0.25.11 - '@esbuild/linux-arm': 0.25.11 - '@esbuild/linux-arm64': 0.25.11 - '@esbuild/linux-ia32': 0.25.11 - '@esbuild/linux-loong64': 0.25.11 - '@esbuild/linux-mips64el': 0.25.11 - '@esbuild/linux-ppc64': 0.25.11 - '@esbuild/linux-riscv64': 0.25.11 - '@esbuild/linux-s390x': 0.25.11 - '@esbuild/linux-x64': 0.25.11 - '@esbuild/netbsd-arm64': 0.25.11 - '@esbuild/netbsd-x64': 0.25.11 - '@esbuild/openbsd-arm64': 0.25.11 - '@esbuild/openbsd-x64': 0.25.11 - '@esbuild/openharmony-arm64': 0.25.11 - '@esbuild/sunos-x64': 0.25.11 - '@esbuild/win32-arm64': 0.25.11 - '@esbuild/win32-ia32': 0.25.11 - '@esbuild/win32-x64': 0.25.11 + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 escalade@3.2.0: {} - esprima@4.0.1: {} + escape-string-regexp@4.0.0: {} - estree-walker@3.0.3: + eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + eslint: 9.39.1(jiti@2.6.1) + hermes-parser: 0.25.1 + zod: 3.25.76 + zod-validation-error: 4.0.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-refresh@0.4.24(eslint@9.39.1(jiti@2.6.1)): + dependencies: + eslint: 9.39.1(jiti@2.6.1) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.1(jiti@2.6.1): dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.39.1 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color - event-target-shim@5.0.1: {} + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 - eventemitter3@4.0.7: {} + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 - events@3.3.0: {} + estraverse@5.3.0: {} - evp_bytestokey@1.0.3: + estree-walker@3.0.3: dependencies: - md5.js: 1.3.5 - safe-buffer: 5.2.1 + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + event-target-shim@5.0.1: {} expand-template@2.0.3: {} expect-type@1.2.2: {} - fast-equals@5.3.2: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: dependencies: @@ -5151,6 +3964,10 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -5163,15 +3980,27 @@ snapshots: dependencies: commander: 14.0.1 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + file-uri-to-path@1.0.0: {} fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - for-each@0.3.5: + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: dependencies: - is-callable: 1.2.7 + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} form-data-encoder@1.7.2: {} @@ -5188,6 +4017,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 + fraction.js@5.3.4: {} + fs-constants@1.0.0: {} fsevents@2.3.3: @@ -5195,10 +4026,11 @@ snapshots: function-bind@1.1.2: {} - generator-function@2.0.1: {} - gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: + optional: true + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5212,14 +4044,12 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-nonce@1.0.1: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-tsconfig@4.12.0: + get-tsconfig@4.13.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -5229,6 +4059,14 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.5.0: {} + globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -5238,17 +4076,16 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.3.0 - goober@2.1.18(csstype@3.1.3): - dependencies: - csstype: 3.1.3 - gopd@1.2.0: {} graceful-fs@4.2.11: {} - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 + graphemer@1.4.0: {} + + graphql@16.12.0: + optional: true + + has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -5256,40 +4093,24 @@ snapshots: dependencies: has-symbols: 1.1.0 - hash-base@3.0.5: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - - hash-base@3.1.2: - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.8 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - - hash.js@1.1.7: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - hasown@2.0.2: dependencies: function-bind: 1.1.2 - hmac-drbg@1.0.1: + headers-polyfill@4.0.3: + optional: true + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: dependencies: - hash.js: 1.1.7 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 + hermes-estree: 0.25.1 html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 optional: true - html-entities@2.6.0: {} - http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -5298,8 +4119,6 @@ snapshots: - supports-color optional: true - https-browserify@1.0.0: {} - https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -5319,72 +4138,47 @@ snapshots: ieee754@1.2.1: {} - ignore@7.0.5: {} - - inherits@2.0.4: {} - - ini@1.3.8: {} + ignore@5.3.2: {} - internmap@2.0.3: {} + ignore@7.0.5: {} - is-arguments@1.2.0: + import-fresh@3.3.1: dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 + parent-module: 1.0.1 + resolve-from: 4.0.0 - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 + imurmurhash@0.1.4: {} - is-callable@1.2.7: {} + inherits@2.0.4: {} + + ini@1.3.8: {} is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.2: - dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-nan@1.3.2: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - - is-number@7.0.0: {} - - is-potential-custom-element-name@1.0.1: + is-node-process@1.2.0: optional: true - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - - isarray@1.0.0: {} + is-number@7.0.0: {} - isarray@2.0.5: {} + is-potential-custom-element-name@1.0.1: + optional: true - isbot@5.1.31: {} + isexe@2.0.0: {} jiti@2.6.1: {} js-tokens@4.0.0: {} + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + jsdom@26.1.0: dependencies: cssstyle: 4.6.0 @@ -5415,13 +4209,28 @@ snapshots: jsesc@3.1.0: {} + json-buffer@3.0.1: {} + json-schema-to-ts@3.1.1: dependencies: '@babel/runtime': 7.28.4 ts-algebra: 2.0.0 + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + lightningcss-android-arm64@1.30.2: optional: true @@ -5471,11 +4280,11 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 - lodash@4.17.21: {} - - loose-envify@1.4.0: + locate-path@6.0.0: dependencies: - js-tokens: 4.0.0 + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} loupe@3.2.1: {} @@ -5486,7 +4295,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.546.0(react@19.2.0): + lucide-react@0.553.0(react@19.2.0): dependencies: react: 19.2.0 @@ -5494,13 +4303,11 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - math-intrinsics@1.1.0: {} - - md5.js@1.3.5: + magic-string@0.30.21: dependencies: - hash-base: 3.0.5 - inherits: 2.0.4 - safe-buffer: 5.2.1 + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} merge2@1.4.1: {} @@ -5509,11 +4316,6 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 - miller-rabin@4.0.1: - dependencies: - bn.js: 4.12.2 - brorand: 1.1.0 - mime-db@1.52.0: {} mime-types@2.1.35: @@ -5522,9 +4324,13 @@ snapshots: mimic-response@3.1.0: {} - minimalistic-assert@1.0.1: {} + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 - minimalistic-crypto-utils@1.0.1: {} + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 minimist@1.2.8: {} @@ -5532,10 +4338,41 @@ snapshots: ms@2.1.3: {} + msw@2.12.1(@types/node@24.10.1)(typescript@5.9.3): + dependencies: + '@inquirer/confirm': 5.1.21(@types/node@24.10.1) + '@mswjs/interceptors': 0.40.0 + '@open-draft/deferred-promise': 2.2.0 + '@types/statuses': 2.0.6 + cookie: 1.0.2 + graphql: 16.12.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.7.0 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.0 + type-fest: 4.41.0 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + optional: true + + mute-stream@2.0.0: + optional: true + nanoid@3.3.11: {} napi-build-utils@2.0.0: {} + natural-compare@1.4.0: {} + next@14.2.33(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@next/env': 14.2.33 @@ -5562,7 +4399,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - node-abi@3.78.0: + node-abi@3.80.0: dependencies: semver: 7.7.3 @@ -5572,33 +4409,13 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-releases@2.0.26: {} + node-releases@2.0.27: {} - normalize-path@3.0.0: {} + normalize-range@0.1.2: {} nwsapi@2.2.22: optional: true - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - object-is@1.1.6: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - once@1.4.0: dependencies: wrappy: 1.0.2 @@ -5618,49 +4435,55 @@ snapshots: transitivePeerDependencies: - encoding - os-browserify@0.3.0: {} + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + outvariant@1.4.3: + optional: true - pako@1.0.11: {} + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 - parse-asn1@5.1.9: + p-locate@5.0.0: dependencies: - asn1.js: 4.10.1 - browserify-aes: 1.2.0 - evp_bytestokey: 1.0.3 - pbkdf2: 3.1.5 - safe-buffer: 5.2.1 + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 parse5@7.3.0: dependencies: entities: 6.0.1 optional: true - path-browserify@1.0.1: {} + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-to-regexp@6.3.0: + optional: true path-type@6.0.0: {} pathe@1.1.2: {} - pathe@2.0.3: {} - pathval@2.0.1: {} - pbkdf2@3.1.5: - dependencies: - create-hash: 1.2.0 - create-hmac: 1.1.7 - ripemd160: 2.0.3 - safe-buffer: 5.2.1 - sha.js: 2.4.12 - to-buffer: 1.2.2 - picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.3: {} - possible-typed-array-names@1.1.0: {} + postcss-value-parser@4.2.0: {} postcss@8.4.31: dependencies: @@ -5682,60 +4505,24 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.78.0 + node-abi: 3.80.0 pump: 3.0.3 rc: 1.2.8 simple-get: 4.0.1 tar-fs: 2.1.4 tunnel-agent: 0.6.0 - prettier@3.6.2: {} - - process-nextick-args@2.0.1: {} - - process@0.11.10: {} - - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - public-encrypt@4.0.3: - dependencies: - bn.js: 4.12.2 - browserify-rsa: 4.1.1 - create-hash: 1.2.0 - parse-asn1: 5.1.9 - randombytes: 2.1.0 - safe-buffer: 5.2.1 + prelude-ls@1.2.1: {} pump@3.0.3: dependencies: end-of-stream: 1.4.5 once: 1.4.0 - punycode@1.4.1: {} - punycode@2.3.1: {} - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - - querystring-es3@0.2.1: {} - queue-microtask@1.2.3: {} - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - - randomfill@1.0.4: - dependencies: - randombytes: 2.1.0 - safe-buffer: 5.2.1 - rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -5748,146 +4535,54 @@ snapshots: react: 19.2.0 scheduler: 0.27.0 - react-is@16.13.1: {} - - react-is@18.3.1: {} - - react-refresh@0.17.0: {} - - react-remove-scroll-bar@2.3.8(@types/react@19.2.2)(react@19.2.0): - dependencies: - react: 19.2.0 - react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0) - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.2 - - react-remove-scroll@2.7.1(@types/react@19.2.2)(react@19.2.0): - dependencies: - react: 19.2.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.2)(react@19.2.0) - react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.2)(react@19.2.0) - use-sidecar: 1.1.3(@types/react@19.2.2)(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.2 - - react-smooth@4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - fast-equals: 5.3.2 - prop-types: 15.8.1 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - - react-style-singleton@2.2.3(@types/react@19.2.2)(react@19.2.0): - dependencies: - get-nonce: 1.0.1 - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.2 - - react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - '@babel/runtime': 7.28.4 - dom-helpers: 5.2.1 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react-refresh@0.18.0: {} react@19.2.0: {} - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - recast@0.23.11: - dependencies: - ast-types: 0.16.1 - esprima: 4.0.1 - source-map: 0.6.1 - tiny-invariant: 1.3.3 - tslib: 2.8.1 - - recharts-scale@0.4.5: - dependencies: - decimal.js-light: 2.5.1 + require-directory@2.1.1: + optional: true - recharts@2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - clsx: 2.1.1 - eventemitter3: 4.0.7 - lodash: 4.17.21 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-is: 18.3.1 - react-smooth: 4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - recharts-scale: 0.4.5 - tiny-invariant: 1.3.3 - victory-vendor: 36.9.2 + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} - reusify@1.1.0: {} + rettime@0.7.0: + optional: true - ripemd160@2.0.3: - dependencies: - hash-base: 3.1.2 - inherits: 2.0.4 + reusify@1.1.0: {} - rollup@4.52.5: + rollup@4.53.2: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.5 - '@rollup/rollup-android-arm64': 4.52.5 - '@rollup/rollup-darwin-arm64': 4.52.5 - '@rollup/rollup-darwin-x64': 4.52.5 - '@rollup/rollup-freebsd-arm64': 4.52.5 - '@rollup/rollup-freebsd-x64': 4.52.5 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 - '@rollup/rollup-linux-arm-musleabihf': 4.52.5 - '@rollup/rollup-linux-arm64-gnu': 4.52.5 - '@rollup/rollup-linux-arm64-musl': 4.52.5 - '@rollup/rollup-linux-loong64-gnu': 4.52.5 - '@rollup/rollup-linux-ppc64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-musl': 4.52.5 - '@rollup/rollup-linux-s390x-gnu': 4.52.5 - '@rollup/rollup-linux-x64-gnu': 4.52.5 - '@rollup/rollup-linux-x64-musl': 4.52.5 - '@rollup/rollup-openharmony-arm64': 4.52.5 - '@rollup/rollup-win32-arm64-msvc': 4.52.5 - '@rollup/rollup-win32-ia32-msvc': 4.52.5 - '@rollup/rollup-win32-x64-gnu': 4.52.5 - '@rollup/rollup-win32-x64-msvc': 4.52.5 + '@rollup/rollup-android-arm-eabi': 4.53.2 + '@rollup/rollup-android-arm64': 4.53.2 + '@rollup/rollup-darwin-arm64': 4.53.2 + '@rollup/rollup-darwin-x64': 4.53.2 + '@rollup/rollup-freebsd-arm64': 4.53.2 + '@rollup/rollup-freebsd-x64': 4.53.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.2 + '@rollup/rollup-linux-arm-musleabihf': 4.53.2 + '@rollup/rollup-linux-arm64-gnu': 4.53.2 + '@rollup/rollup-linux-arm64-musl': 4.53.2 + '@rollup/rollup-linux-loong64-gnu': 4.53.2 + '@rollup/rollup-linux-ppc64-gnu': 4.53.2 + '@rollup/rollup-linux-riscv64-gnu': 4.53.2 + '@rollup/rollup-linux-riscv64-musl': 4.53.2 + '@rollup/rollup-linux-s390x-gnu': 4.53.2 + '@rollup/rollup-linux-x64-gnu': 4.53.2 + '@rollup/rollup-linux-x64-musl': 4.53.2 + '@rollup/rollup-openharmony-arm64': 4.53.2 + '@rollup/rollup-win32-arm64-msvc': 4.53.2 + '@rollup/rollup-win32-ia32-msvc': 4.53.2 + '@rollup/rollup-win32-x64-gnu': 4.53.2 + '@rollup/rollup-win32-x64-msvc': 4.53.2 fsevents: 2.3.3 rrweb-cssom@0.8.0: @@ -5897,16 +4592,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 - safe-buffer@5.1.2: {} - safe-buffer@5.2.1: {} - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - safer-buffer@2.1.2: optional: true @@ -5921,59 +4608,17 @@ snapshots: semver@7.7.3: {} - seroval-plugins@1.3.3(seroval@1.3.2): - dependencies: - seroval: 1.3.2 - - seroval@1.3.2: {} - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - setimmediate@1.0.5: {} - - sha.js@2.4.12: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: + shebang-command@2.0.0: dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 + shebang-regex: 3.0.0 - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 + shebang-regex@3.0.0: {} siginfo@2.0.0: {} + signal-exit@4.1.0: + optional: true + simple-concat@1.0.1: {} simple-get@4.0.1: @@ -5986,50 +4631,26 @@ snapshots: slash@5.1.0: {} - solid-js@1.9.9: - dependencies: - csstype: 3.1.3 - seroval: 1.3.2 - seroval-plugins: 1.3.3(seroval@1.3.2) - source-map-js@1.2.1: {} - source-map@0.6.1: {} - - source-map@0.7.6: {} - - sql.js@1.13.0: {} - stackback@0.0.2: {} - stackframe@1.3.4: {} + statuses@2.0.2: + optional: true std-env@3.10.0: {} - stream-browserify@3.0.0: - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - - stream-http@3.2.0: - dependencies: - builtin-status-codes: 3.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - xtend: 4.0.2 - streamsearch@1.1.0: {} + strict-event-emitter@0.5.1: + optional: true + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -6040,17 +4661,27 @@ snapshots: strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} + styled-jsx@5.1.1(react@19.2.0): dependencies: client-only: 0.0.1 react: 19.2.0 + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + symbol-tree@3.2.4: optional: true - tailwind-merge@3.3.1: {} + tailwind-merge@3.4.0: {} + + tailwindcss-animate@1.0.7(tailwindcss@4.1.17): + dependencies: + tailwindcss: 4.1.17 - tailwindcss@4.1.16: {} + tailwindcss@4.1.17: {} tapable@2.3.0: {} @@ -6069,14 +4700,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - timers-browserify@2.0.12: - dependencies: - setimmediate: 1.0.5 - - tiny-invariant@1.3.3: {} - - tiny-warning@1.0.3: {} - tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -6095,16 +4718,18 @@ snapshots: tldts-core@6.1.86: optional: true + tldts-core@7.0.17: + optional: true + tldts@6.1.86: dependencies: tldts-core: 6.1.86 optional: true - to-buffer@1.2.2: + tldts@7.0.17: dependencies: - isarray: 2.0.5 - safe-buffer: 5.2.1 - typed-array-buffer: 1.0.3 + tldts-core: 7.0.17 + optional: true to-regex-range@5.0.1: dependencies: @@ -6115,6 +4740,11 @@ snapshots: tldts: 6.1.86 optional: true + tough-cookie@6.0.0: + dependencies: + tldts: 7.0.17 + optional: true + tr46@0.0.3: {} tr46@5.1.1: @@ -6124,28 +4754,40 @@ snapshots: ts-algebra@2.0.0: {} + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + tslib@2.8.1: {} tsx@4.20.6: dependencies: - esbuild: 0.25.11 - get-tsconfig: 4.12.0 + esbuild: 0.25.12 + get-tsconfig: 4.13.0 optionalDependencies: fsevents: 2.3.3 - tty-browserify@0.0.1: {} - tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 - tw-animate-css@1.4.0: {} + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@4.41.0: + optional: true - typed-array-buffer@1.0.3: + typescript-eslint@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 + '@typescript-eslint/eslint-plugin': 8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.1(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color typescript@5.9.3: {} @@ -6153,81 +4795,34 @@ snapshots: undici-types@6.21.0: {} + undici-types@7.16.0: {} + unicorn-magic@0.3.0: {} - unplugin@2.3.10: - dependencies: - '@jridgewell/remapping': 2.3.5 - acorn: 8.15.0 - picomatch: 4.0.3 - webpack-virtual-modules: 0.6.2 + until-async@3.0.2: + optional: true - update-browserslist-db@1.1.4(browserslist@4.27.0): + update-browserslist-db@1.1.4(browserslist@4.28.0): dependencies: - browserslist: 4.27.0 + browserslist: 4.28.0 escalade: 3.2.0 picocolors: 1.1.1 - url@0.11.4: - dependencies: - punycode: 1.4.1 - qs: 6.14.0 - - use-callback-ref@1.3.3(@types/react@19.2.2)(react@19.2.0): + uri-js@4.4.1: dependencies: - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.2 - - use-sidecar@1.1.3(@types/react@19.2.2)(react@19.2.0): - dependencies: - detect-node-es: 1.1.0 - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.2 - - use-sync-external-store@1.6.0(react@19.2.0): - dependencies: - react: 19.2.0 + punycode: 2.3.1 util-deprecate@1.0.2: {} - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.2 - is-typed-array: 1.1.15 - which-typed-array: 1.1.19 - uuid@9.0.1: {} - victory-vendor@36.9.2: - dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-ease': 3.0.2 - '@types/d3-interpolate': 3.0.4 - '@types/d3-scale': 4.0.9 - '@types/d3-shape': 3.1.7 - '@types/d3-time': 3.0.4 - '@types/d3-timer': 3.0.2 - d3-array: 3.2.4 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-scale: 4.0.2 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-timer: 3.0.1 - - vite-node@2.1.9(@types/node@20.19.19)(lightningcss@1.30.2): + vite-node@2.1.9(@types/node@24.10.1)(lightningcss@1.30.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 1.1.2 - vite: 5.4.21(@types/node@20.19.19)(lightningcss@1.30.2) + vite: 5.4.21(@types/node@24.10.1)(lightningcss@1.30.2) transitivePeerDependencies: - '@types/node' - less @@ -6239,36 +4834,36 @@ snapshots: - supports-color - terser - vite@5.4.21(@types/node@20.19.19)(lightningcss@1.30.2): + vite@5.4.21(@types/node@24.10.1)(lightningcss@1.30.2): dependencies: esbuild: 0.21.5 postcss: 8.5.6 - rollup: 4.52.5 + rollup: 4.53.2 optionalDependencies: - '@types/node': 20.19.19 + '@types/node': 24.10.1 fsevents: 2.3.3 lightningcss: 1.30.2 - vite@7.1.12(@types/node@20.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1): + vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.1): dependencies: - esbuild: 0.25.11 + esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.5 + rollup: 4.53.2 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 20.19.19 + '@types/node': 24.10.1 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 tsx: 4.20.6 yaml: 2.8.1 - vitest@2.1.9(@types/node@20.19.19)(jsdom@26.1.0)(lightningcss@1.30.2): + vitest@2.1.9(@types/node@24.10.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.1(@types/node@24.10.1)(typescript@5.9.3)): dependencies: '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@20.19.19)(lightningcss@1.30.2)) + '@vitest/mocker': 2.1.9(msw@2.12.1(@types/node@24.10.1)(typescript@5.9.3))(vite@5.4.21(@types/node@24.10.1)(lightningcss@1.30.2)) '@vitest/pretty-format': 2.1.9 '@vitest/runner': 2.1.9 '@vitest/snapshot': 2.1.9 @@ -6284,11 +4879,11 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.1.1 tinyrainbow: 1.2.0 - vite: 5.4.21(@types/node@20.19.19)(lightningcss@1.30.2) - vite-node: 2.1.9(@types/node@20.19.19)(lightningcss@1.30.2) + vite: 5.4.21(@types/node@24.10.1)(lightningcss@1.30.2) + vite-node: 2.1.9(@types/node@24.10.1)(lightningcss@1.30.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 20.19.19 + '@types/node': 24.10.1 jsdom: 26.1.0 transitivePeerDependencies: - less @@ -6301,8 +4896,6 @@ snapshots: - supports-color - terser - vm-browserify@1.1.2: {} - w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -6315,8 +4908,6 @@ snapshots: webidl-conversions@7.0.0: optional: true - webpack-virtual-modules@0.6.2: {} - whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -6336,21 +4927,31 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-typed-array@1.1.19: + which@2.0.2: dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 + isexe: 2.0.0 why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + word-wrap@1.2.5: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + optional: true + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + optional: true + wrappy@1.0.2: {} ws@8.18.3: @@ -6362,10 +4963,34 @@ snapshots: xmlchars@2.2.0: optional: true - xtend@4.0.2: {} + y18n@5.0.8: + optional: true yallist@3.1.1: {} yaml@2.8.1: {} + yargs-parser@21.1.1: + optional: true + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + optional: true + + yocto-queue@0.1.0: {} + + yoctocolors-cjs@2.1.3: + optional: true + + zod-validation-error@4.0.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 026a556..d495ba6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,3 @@ packages: - - 'packages/*' - - 'benchmark-report' + - 'packages/**' + - 'benchmark-report/*' diff --git a/prd.md b/prd.md index bfc57b6..5ba0b58 100644 --- a/prd.md +++ b/prd.md @@ -56,8 +56,7 @@ ze-benchmarks/ │ └─ (future-suite)/ │ └─ scenarios/... ├─ results/ -│ ├─ summary.json -│ └─ logs//*.jsonl +│ └─ workspaces// # generated workspace per run ├─ .github/workflows/bench.yml └─ docker/ ├─ node-latest.Dockerfile diff --git a/scripts/README-validate-prompts.md b/scripts/README-validate-prompts.md new file mode 100644 index 0000000..cc7f3a3 --- /dev/null +++ b/scripts/README-validate-prompts.md @@ -0,0 +1,142 @@ +# Prompt Validation Tool + +## Overview + +The prompt validation tool (`validate-prompts.ts`) verifies that specialist prompts contain the expected template content. This is useful for: + +- Ensuring specialist templates are being properly applied +- Debugging prompt generation issues +- Validating that specialist-specific content is present in agent prompts + +## Usage + +### Validate from batch ID + +```bash +pnpm validate:prompts +``` + +Example: +```bash +pnpm validate:prompts abc123 starting_from_outcome/shadcn-specialist.json5 +``` + +### Validate from exported prompts file + +```bash +pnpm validate:prompts --prompts-file +``` + +Example: +```bash +# First export prompts +pnpm export:prompts abc123 batch-prompts.json + +# Then validate +pnpm validate:prompts --prompts-file batch-prompts.json starting_from_outcome/shadcn-specialist.json5 +``` + +## What it validates + +The tool checks that specialist prompts contain: + +1. **Purpose** - Content from `template.persona.purpose` +2. **Values** - Content from `template.persona.values` (expects >50% match) +3. **Attributes** - Content from `template.persona.attributes` (tracked but not required) +4. **Spawner Prompt** - Content from `template.prompts.model_specific[].spawnerPrompt` or `template.prompts.default.spawnerPrompt` + +## Exit codes + +- `0` - All specialist prompts passed validation +- `1` - One or more specialist prompts failed validation, or an error occurred + +## Output + +The tool provides: + +- Overall pass/fail statistics +- Findings summary (how many runs found each type of content) +- Detailed failure information (which content is missing) +- Success summary (what content was found in passing runs) + +### Example output + +``` +🔍 Validating specialist prompts + +Database: /path/to/benchmarks.db +Batch ID: abc123 + +✓ Found batch: 4 runs +✓ Loaded 4 runs from database + +Template: /path/to/shadcn-specialist.json5 +✓ Loaded template: shadcn/ui Expert v0.0.1 + +Validating 2 specialist runs... + +Validation Results: + +Overall: + Total specialist runs: 2 + Passed: 2 + Failed: 0 + +Findings Summary: + Purpose found: 2/2 runs + Values found: 7.0/8 avg per run + Attributes found: 5.5/7 avg per run + Spawner prompt found: 2/2 runs + +Passed Runs: + +✓ Run run_abc123_001 (claude-sonnet-4.5) + Specialist: @zephyr-cloud/shadcn-specialist + Found: Purpose=true, Values=7/8, Spawner=true +✓ Run run_abc123_002 (claude-sonnet-4.5) + Specialist: @zephyr-cloud/shadcn-specialist + Found: Purpose=true, Values=7/8, Spawner=true + +✨ All specialist prompts validated successfully! +``` + +## Template format + +The tool expects specialist templates to follow this structure: + +```json5 +{ + "name": "specialist-name", + "displayName": "Specialist Display Name", + "version": "0.0.1", + "persona": { + "purpose": "Expert specialist for...", + "values": [ + "Value 1", + "Value 2" + ], + "attributes": [ + "Attribute 1", + "Attribute 2" + ] + }, + "prompts": { + "default": { + "spawnerPrompt": "Default spawner prompt..." + }, + "model_specific": { + "claude-sonnet-4.5": { + "spawnerPrompt": "Model-specific spawner prompt..." + } + } + } +} +``` + +## Notes + +- Only validates runs that have a specialist configured +- Skips validation if no specialist runs are found in the batch +- Supports both JSON and JSON5 template files +- Can read prompts directly from the database or from exported JSON files +- Uses fuzzy matching for spawner prompts (checks for substantial portions of the prompt) diff --git a/scripts/clear-database.ts b/scripts/clear-database.ts new file mode 100644 index 0000000..810a312 --- /dev/null +++ b/scripts/clear-database.ts @@ -0,0 +1,114 @@ +#!/usr/bin/env tsx + +import { BenchmarkLogger } from '../packages/database/src/logger'; +import { copyFileSync, existsSync, mkdirSync } from 'fs'; +import { join, dirname } from 'path'; +import chalk from 'chalk'; + +interface ClearOptions { + target?: 'benchmarks' | 'dashboard' | 'all'; +} + +function clearBenchmarksDatabase() { + console.log(chalk.blue('Clearing ze-benchmarks database...')); + + const dbPath = join(process.cwd(), 'benchmark-report', 'public', 'benchmarks.db'); + + if (!existsSync(dbPath)) { + console.log(chalk.yellow('⚠ Database does not exist, creating empty one...')); + const logger = new BenchmarkLogger(dbPath); + logger.close(); + console.log(chalk.green('✓ Created empty database')); + } else { + const logger = new BenchmarkLogger(dbPath); + logger.clearDatabase(); + logger.close(); + console.log(chalk.green('✓ Cleared ze-benchmarks database')); + } + + return dbPath; +} + +function clearDashboardDatabase() { + console.log(chalk.blue('Clearing specialist-dashboard database...')); + + // Get the path to the dashboard database + const dashboardDbPath = join(process.cwd(), '..', 'specialist-dashboard', 'public', 'benchmarks.db'); + const dashboardDbDir = dirname(dashboardDbPath); + + // Ensure directory exists + if (!existsSync(dashboardDbDir)) { + mkdirSync(dashboardDbDir, { recursive: true }); + } + + if (!existsSync(dashboardDbPath)) { + console.log(chalk.yellow('⚠ Dashboard database does not exist, creating empty one...')); + const logger = new BenchmarkLogger(dashboardDbPath); + logger.close(); + console.log(chalk.green('✓ Created empty dashboard database')); + } else { + const logger = new BenchmarkLogger(dashboardDbPath); + logger.clearDatabase(); + logger.close(); + console.log(chalk.green('✓ Cleared specialist-dashboard database')); + } +} + +function syncDatabases() { + console.log(chalk.blue('Syncing databases...')); + + const benchmarksDbPath = join(process.cwd(), 'benchmark-report', 'public', 'benchmarks.db'); + const dashboardDbPath = join(process.cwd(), '..', 'specialist-dashboard', 'public', 'benchmarks.db'); + const dashboardDbDir = dirname(dashboardDbPath); + + // Ensure benchmarks database exists + if (!existsSync(benchmarksDbPath)) { + console.log(chalk.yellow('⚠ Benchmarks database does not exist, creating it...')); + const logger = new BenchmarkLogger(benchmarksDbPath); + logger.close(); + } + + // Ensure dashboard directory exists + if (!existsSync(dashboardDbDir)) { + mkdirSync(dashboardDbDir, { recursive: true }); + } + + // Copy benchmarks database to dashboard + copyFileSync(benchmarksDbPath, dashboardDbPath); + console.log(chalk.green('✓ Synced databases')); +} + +async function main() { + const args = process.argv.slice(2); + const target = (args[0] as ClearOptions['target']) || 'all'; + + console.log(chalk.bold('\n🗑️ Database Clear Tool\n')); + + try { + if (target === 'benchmarks') { + const dbPath = clearBenchmarksDatabase(); + console.log(chalk.gray(`\nDatabase: ${dbPath}`)); + } else if (target === 'dashboard') { + clearDashboardDatabase(); + } else if (target === 'all') { + // Clear benchmarks database first + const dbPath = clearBenchmarksDatabase(); + + // Then sync it to dashboard (this will clear the dashboard too) + syncDatabases(); + + console.log(chalk.gray(`\nCleared both databases`)); + } else { + console.error(chalk.red(`Invalid target: ${target}`)); + console.error(chalk.gray('Valid targets: benchmarks, dashboard, all')); + process.exit(1); + } + + console.log(chalk.green('\n✓ All done!\n')); + } catch (error) { + console.error(chalk.red('\n✖ Error:'), error); + process.exit(1); + } +} + +main(); diff --git a/scripts/compare-batches.ts b/scripts/compare-batches.ts new file mode 100755 index 0000000..36c2469 --- /dev/null +++ b/scripts/compare-batches.ts @@ -0,0 +1,76 @@ +#!/usr/bin/env tsx +/** + * Compare benchmark results across multiple batches + * + * This script wraps the ze-benchmarks --compare-batches command + * and provides additional output formatting for dashboard consumption. + * + * Usage: + * pnpm bench:compare [batch-id3...] + * + * Examples: + * pnpm bench:compare abc123 def456 + * pnpm bench:compare abc123 def456 ghi789 + */ + +import { execSync } from 'node:child_process'; +import { resolve } from 'node:path'; +import chalk from 'chalk'; + +function parseArgs(): string[] { + const args = process.argv.slice(2); + + if (args.length < 2) { + console.error(chalk.red('❌ Error: At least two batch IDs required')); + console.log('\n' + chalk.bold('Usage:')); + console.log(` ${chalk.cyan('pnpm bench:compare')} [batch-id3...]`); + console.log('\n' + chalk.bold('Examples:')); + console.log(` ${chalk.gray('pnpm bench:compare abc123 def456')}`); + console.log(` ${chalk.gray('pnpm bench:compare abc123 def456 ghi789')}`); + console.log('\n' + chalk.bold('Tips:')); + console.log(` • View recent batches: ${chalk.cyan('pnpm --filter ze-benchmarks bench --batches')}`); + console.log(` • View batch details: ${chalk.cyan('pnpm --filter ze-benchmarks bench --batch-details ')}`); + console.log(` • View in dashboard: ${chalk.cyan('pnpm dashboard:dev')}`); + process.exit(1); + } + + return args; +} + +async function main() { + console.log(chalk.cyan.bold('\n📊 Comparing benchmark batches\n')); + + try { + // Parse arguments + const batchIds = parseArgs(); + + console.log(chalk.blue(`Comparing ${batchIds.length} batches:`)); + batchIds.forEach((id, index) => { + console.log(chalk.gray(` ${index + 1}. ${id}`)); + }); + console.log(); + + // Build and execute the command + const cliPath = resolve(__dirname, '../packages/harness/src/cli.ts'); + const cmd = `tsx ${cliPath} --compare-batches ${batchIds.join(' ')}`; + + execSync(cmd, { + cwd: resolve(__dirname, '..'), + stdio: 'inherit' + }); + + console.log(chalk.bold.green('\n✨ Comparison complete!\n')); + console.log(chalk.bold('Next steps:')); + console.log(` • View in dashboard: ${chalk.cyan('pnpm dashboard:dev')}`); + console.log(` • View batch details: ${chalk.cyan('pnpm --filter ze-benchmarks bench --batch-details ')}`); + console.log(); + } catch (error) { + const exitCode = (error as any).status || 1; + if (exitCode !== 0) { + console.error(chalk.red('\n❌ Comparison failed')); + process.exit(exitCode); + } + } +} + +main(); diff --git a/scripts/export-batch-prompts.ts b/scripts/export-batch-prompts.ts new file mode 100644 index 0000000..42e2211 --- /dev/null +++ b/scripts/export-batch-prompts.ts @@ -0,0 +1,299 @@ +#!/usr/bin/env tsx +/** + * Export prompts from a batch run + * + * This script extracts all prompts sent during a batch run and outputs them as JSON. + * Useful for reviewing what prompts were actually executed, debugging, or creating snapshots. + * + * Usage: + * pnpm export:prompts [output-file] + * + * Examples: + * pnpm export:prompts abc123 + * pnpm export:prompts abc123 batch-prompts.json + */ + +import Database from 'better-sqlite3'; +import { resolve } from 'node:path'; +import { writeFileSync } from 'node:fs'; +import chalk from 'chalk'; + +interface EvaluationDetail { + evaluatorName: string; + score: number; + maxScore: number; + details: any; +} + +interface PromptData { + runId: string; + suite: string; + scenario: string; + agent: string; + model: string; + tier: string; + specialist?: string; + messages: any[]; + score: number | null; + weightedScore: number | null; + success: boolean; + timestamp: string; + workspaceDir?: string; + durationMs?: number; + toolCalls?: number; + tokensIn?: number; + tokensOut?: number; + costUsd?: number; + evaluations: EvaluationDetail[]; +} + +function findDatabasePath(): string { + // Check environment variable first + if (process.env.BENCHMARK_DB_PATH) { + return resolve(process.env.BENCHMARK_DB_PATH); + } + + // Default location + return resolve(__dirname, '../benchmark-report/public/benchmarks.db'); +} + +function parseArgs(): { batchId: string; outputFile?: string } { + // Filter out '--' separator that pnpm adds + const args = process.argv.slice(2).filter(arg => arg !== '--'); + + if (args.length === 0) { + console.error(chalk.red('❌ Error: Batch ID required')); + console.log('\n' + chalk.bold('Usage:')); + console.log(` ${chalk.cyan('pnpm export:prompts')} [output-file]`); + console.log('\n' + chalk.bold('Examples:')); + console.log(` ${chalk.gray('pnpm export:prompts abc123')}`); + console.log(` ${chalk.gray('pnpm export:prompts abc123 batch-prompts.json')}`); + console.log('\n' + chalk.bold('Tips:')); + console.log(` • View recent batches: ${chalk.cyan('pnpm --filter ze-benchmarks bench --batches')}`); + process.exit(1); + } + + return { + batchId: args[0], + outputFile: args[1] + }; +} + +async function main() { + console.log(chalk.cyan.bold('\n📝 Exporting batch prompts\n')); + + try { + const { batchId, outputFile } = parseArgs(); + const dbPath = findDatabasePath(); + + console.log(chalk.blue(`Database: ${chalk.gray(dbPath)}`)); + console.log(chalk.blue(`Batch ID: ${chalk.gray(batchId)}\n`)); + + // Open database + const db = new Database(dbPath, { readonly: true }); + + // Verify batch exists + const batch = db.prepare(` + SELECT * FROM batch_runs WHERE batchId = ? + `).get(batchId); + + if (!batch) { + console.error(chalk.red(`❌ Batch not found: ${batchId}`)); + process.exit(1); + } + + console.log(chalk.green(`✓ Found batch: ${(batch as any).totalRuns} runs`)); + + // Fetch all runs with prompts + const runs = db.prepare(` + SELECT + br.run_id, + br.suite, + br.scenario, + br.agent, + br.model, + br.tier, + br.specialist_enabled, + br.metadata, + br.total_score, + br.weighted_score, + br.is_successful, + br.started_at, + rt.prompt_sent, + rt.workspace_dir, + rt.duration_ms, + rt.tool_calls, + rt.tokens_in, + rt.tokens_out, + rt.cost_usd + FROM benchmark_runs br + LEFT JOIN run_telemetry rt ON br.run_id = rt.run_id + WHERE br.batchId = ? + ORDER BY br.started_at ASC + `).all(batchId) as any[]; + + console.log(chalk.green(`✓ Retrieved ${runs.length} runs\n`)); + + // Prepare statement to fetch evaluations for each run + const getEvaluations = db.prepare(` + SELECT + evaluator_name, + score, + max_score, + details + FROM evaluation_results + WHERE run_id = ? + ORDER BY evaluator_name ASC + `); + + // Parse prompts + const promptData: PromptData[] = runs.map(run => { + let messages: any[] = []; + let metadata: any = {}; + let specialist: string | undefined = undefined; + + if (run.prompt_sent) { + try { + messages = JSON.parse(run.prompt_sent); + } catch (error) { + console.warn(chalk.yellow(`⚠ Failed to parse prompt for run ${run.run_id}`)); + } + } + + // Parse metadata to extract specialist info + if (run.metadata) { + try { + metadata = JSON.parse(run.metadata); + specialist = metadata.specialist; + } catch (error) { + // Ignore metadata parse errors + } + } + + // Fetch evaluations for this run + const evaluationRows = getEvaluations.all(run.run_id) as any[]; + const evaluations: EvaluationDetail[] = evaluationRows.map(evalRow => { + let details: any = null; + if (evalRow.details) { + // Try to parse as JSON, but accept plain strings too + try { + details = JSON.parse(evalRow.details); + } catch (error) { + // Not JSON - use the plain string value + details = evalRow.details; + } + } + + return { + evaluatorName: evalRow.evaluator_name, + score: evalRow.score, + maxScore: evalRow.max_score, + details + }; + }); + + return { + runId: run.run_id, + suite: run.suite, + scenario: run.scenario, + agent: run.agent, + model: run.model, + tier: run.tier, + specialist, + messages, + score: run.total_score, + weightedScore: run.weighted_score, + success: run.is_successful === 1, + timestamp: run.started_at, + workspaceDir: run.workspace_dir, + durationMs: run.duration_ms, + toolCalls: run.tool_calls, + tokensIn: run.tokens_in, + tokensOut: run.tokens_out, + costUsd: run.cost_usd, + evaluations + }; + }); + + db.close(); + + // Calculate totals for batch summary + const batchTotalTokensIn = promptData.reduce((sum, run) => sum + (run.tokensIn || 0), 0); + const batchTotalTokensOut = promptData.reduce((sum, run) => sum + (run.tokensOut || 0), 0); + const batchTotalCost = promptData.reduce((sum, run) => sum + (run.costUsd || 0), 0); + const batchTotalDuration = promptData.reduce((sum, run) => sum + (run.durationMs || 0), 0); + const batchTotalToolCalls = promptData.reduce((sum, run) => sum + (run.toolCalls || 0), 0); + + // Output + const output = { + batchId, + exportedAt: new Date().toISOString(), + totalRuns: promptData.length, + summary: { + durationMs: batchTotalDuration, + toolCalls: batchTotalToolCalls, + tokensIn: batchTotalTokensIn, + tokensOut: batchTotalTokensOut, + totalCost: batchTotalCost + }, + runs: promptData + }; + + const jsonOutput = JSON.stringify(output, null, 2); + + if (outputFile) { + writeFileSync(outputFile, jsonOutput); + console.log(chalk.green(`✓ Exported to: ${chalk.cyan(resolve(outputFile))}`)); + } else { + console.log(chalk.bold('\n📋 Prompt Data:\n')); + console.log(jsonOutput); + } + + console.log(chalk.bold.green('\n✨ Export complete!\n')); + + // Print summary + console.log(chalk.bold('Summary:')); + console.log(` • Total runs: ${chalk.cyan(promptData.length)}`); + console.log(` • Runs with prompts: ${chalk.cyan(promptData.filter(p => p.messages.length > 0).length)}`); + console.log(` • Successful runs: ${chalk.cyan(promptData.filter(p => p.success).length)}`); + console.log(` • Runs with evaluations: ${chalk.cyan(promptData.filter(p => p.evaluations.length > 0).length)}`); + + // Calculate totals + const totalTokensIn = promptData.reduce((sum, run) => sum + (run.tokensIn || 0), 0); + const totalTokensOut = promptData.reduce((sum, run) => sum + (run.tokensOut || 0), 0); + const totalCost = promptData.reduce((sum, run) => sum + (run.costUsd || 0), 0); + const totalDuration = promptData.reduce((sum, run) => sum + (run.durationMs || 0), 0); + const totalToolCalls = promptData.reduce((sum, run) => sum + (run.toolCalls || 0), 0); + + console.log(chalk.bold('\nPerformance:')); + console.log(` • Total duration: ${chalk.cyan((totalDuration / 1000 / 60).toFixed(2) + ' minutes')}`); + console.log(` • Average duration: ${chalk.cyan((totalDuration / promptData.length / 1000).toFixed(2) + ' seconds')}`); + console.log(` • Total tool calls: ${chalk.cyan(totalToolCalls.toLocaleString())}`); + + console.log(chalk.bold('\nToken usage:')); + console.log(` • Input tokens: ${chalk.cyan(totalTokensIn.toLocaleString())}`); + console.log(` • Output tokens: ${chalk.cyan(totalTokensOut.toLocaleString())}`); + console.log(` • Total cost: ${chalk.cyan('$' + totalCost.toFixed(4))}`); + + // Group by model + const modelGroups = promptData.reduce((acc, run) => { + const key = run.specialist ? `${run.model} (${run.specialist})` : run.model; + acc[key] = (acc[key] || 0) + 1; + return acc; + }, {} as Record); + + console.log(chalk.bold('\nRuns by model:')); + Object.entries(modelGroups).forEach(([model, count]) => { + console.log(` • ${chalk.gray(model)}: ${chalk.cyan(count)}`); + }); + + console.log(); + + } catch (error) { + console.error(chalk.red('\n❌ Export failed:')); + console.error(error); + process.exit(1); + } +} + +main(); diff --git a/scripts/run-all-models.ts b/scripts/run-all-models.ts new file mode 100755 index 0000000..3e9824b --- /dev/null +++ b/scripts/run-all-models.ts @@ -0,0 +1,466 @@ +#!/usr/bin/env tsx +/** + * Run benchmarks for all models defined in models.json5 + * + * This script: + * 1. Reads models.json5 from the workspace root + * 2. Runs benchmarks for each vanilla and specialist model + * 3. Organizes results into batches for easy comparison + * 4. Stores results in benchmarks.db for visualization + * + * Usage: + * pnpm bench:run [--tier L0] + * + * Examples: + * pnpm bench:run shadcn-generate-vite shadcn-generate-vite + * pnpm bench:run shadcn-generate-vite shadcn-generate-vite --tier L1 + */ + +import { readFileSync, existsSync } from 'node:fs'; +import { resolve, join, dirname } from 'node:path'; +import { spawn } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import JSON5 from 'json5'; +import chalk from 'chalk'; +import { v4 as uuidv4 } from 'uuid'; +import { BenchmarkLogger } from '../packages/database/src/logger'; +import { spinner } from '@clack/prompts'; +import { executeWarmup } from '../packages/harness/src/domain/warmup'; +import { loadScenario } from '../packages/harness/src/domain/scenario'; +import { createAgentAdapter } from '../packages/harness/src/domain/agent'; + +// ESM __dirname polyfill +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +interface ModelConfig { + provider: string; + model: string; + specialist?: string; +} + +interface ModelsConfig { + vanilla_models: ModelConfig[]; + specialist_models: ModelConfig[]; +} + +interface RunOptions { + suite: string; + scenario: string; + tier: string; + modelsConfigPath?: string; +} + +function parseArgs(): RunOptions { + let args = process.argv.slice(2); + + // Skip '--' if it's the first argument (pnpm passes it as separator) + if (args[0] === '--') { + args = args.slice(1); + } + + if (args.length < 2) { + console.error(chalk.red('❌ Error: Missing required arguments')); + console.log('\n' + chalk.bold('Usage:')); + console.log(` ${chalk.cyan('pnpm bench:run')} [options]`); + console.log('\n' + chalk.bold('Options:')); + console.log(` ${chalk.cyan('--tier')} Difficulty tier (default: L0)`); + console.log(` ${chalk.cyan('--models-config')} Path to models.json5 (default: ../../models.json5)`); + console.log('\n' + chalk.bold('Examples:')); + console.log(` ${chalk.gray('pnpm bench:run shadcn-generate-vite shadcn-generate-vite')}`); + console.log(` ${chalk.gray('pnpm bench:run shadcn-generate-vite shadcn-generate-vite --tier L1')}`); + process.exit(1); + } + + const suite = args[0]; + const scenario = args[1]; + + let tier = 'L0'; + let modelsConfigPath: string | undefined; + + for (let i = 2; i < args.length; i++) { + if (args[i] === '--tier' && args[i + 1]) { + tier = args[i + 1]; + i++; + } else if (args[i] === '--models-config' && args[i + 1]) { + modelsConfigPath = args[i + 1]; + i++; + } + } + + return { suite, scenario, tier, modelsConfigPath }; +} + +function loadModelsConfig(configPath?: string): ModelsConfig { + const defaultPath = resolve(__dirname, '../../models.json5'); + const path = configPath ? resolve(configPath) : defaultPath; + + if (!existsSync(path)) { + throw new Error(`Models config not found at: ${path}`); + } + + try { + const content = readFileSync(path, 'utf-8'); + const config = JSON5.parse(content) as ModelsConfig; + + if (!config.vanilla_models || !Array.isArray(config.vanilla_models)) { + throw new Error('Invalid models config: missing or invalid vanilla_models'); + } + + if (!config.specialist_models || !Array.isArray(config.specialist_models)) { + throw new Error('Invalid models config: missing or invalid specialist_models'); + } + + return config; + } catch (error) { + throw new Error(`Failed to load models config: ${error instanceof Error ? error.message : String(error)}`); + } +} + +function getAgentFromProvider(provider: string): string { + const agentMap: Record = { + 'anthropic': 'anthropic', + 'openai': 'openrouter', + 'openrouter': 'openrouter' + }; + + return agentMap[provider.toLowerCase()] || 'openrouter'; +} + +async function runBenchmark( + suite: string, + scenario: string, + tier: string, + model: ModelConfig, + isSpecialist: boolean, + batchId: string +): Promise<{ success: boolean; model: ModelConfig; isSpecialist: boolean; error?: string; runId?: string }> { + const agent = getAgentFromProvider(model.provider); + const modelLabel = isSpecialist && model.specialist + ? `${model.specialist}/${model.model}` + : model.model; + + const startTime = Date.now(); + const s = spinner(); + s.start(chalk.blue(`Running ${modelLabel}`)); + + return new Promise((promiseResolve) => { + try { + // Build the command + const cliPath = resolve(__dirname, '../packages/harness/src/cli.ts'); + const tsxPath = resolve(__dirname, '../../node_modules/.bin/tsx'); + + const args = [ + cliPath, + suite, + scenario, + '--tier', tier, + '--agent', agent, + '--model', model.model, + '--batch-id', batchId, + '--skip-warmup', // Skip warmup in child processes (already done once in parent) + '--quiet' // Quiet mode for cleaner parallel output + ]; + + // Add specialist flag when provided + if (isSpecialist && model.specialist) { + args.push('--specialist'); + args.push(model.specialist); + } + + // Log child process spawn details + console.log(chalk.gray(`\n[DEBUG] Spawning child process for ${modelLabel}`)); + console.log(chalk.gray(` Command: ${tsxPath} ${args.join(' ')}`)); + console.log(chalk.gray(` CWD: ${resolve(__dirname, '..')}`)); + + // Execute the benchmark with streaming output (use absolute path to tsx) + const child = spawn(tsxPath, args, { + cwd: resolve(__dirname, '..'), + stdio: ['ignore', 'pipe', 'pipe'] as const + }); + + // Log PID immediately after spawn + console.log(chalk.gray(` PID: ${child.pid}`)); + + let lastOutput = ''; + let errorOutput = ''; + let stdoutBuffer = ''; + + // Stream stdout with real-time updates + if (child.stdout) { + child.stdout.on('data', (data) => { + const output = data.toString(); + stdoutBuffer += output; + + // Log all stdout in real-time (not just for spinner) + console.log(chalk.gray(` [${modelLabel} stdout]: ${output.trim()}`)); + + lastOutput = output.trim().split('\n').pop() || lastOutput; + + // Update spinner with last meaningful line + if (lastOutput && !lastOutput.startsWith(' ') && lastOutput.length < 100) { + s.message(chalk.blue(`${modelLabel}: ${chalk.dim(lastOutput)}`)); + } + }); + } + + // Capture stderr + if (child.stderr) { + child.stderr.on('data', (data) => { + const output = data.toString(); + errorOutput += output; + // Log stderr separately in real-time + console.error(chalk.red(` [${modelLabel} stderr]: ${output.trim()}`)); + }); + } + + // Handle completion + child.on('close', (code, signal) => { + const duration = ((Date.now() - startTime) / 1000 / 60).toFixed(1); + const durationSeconds = ((Date.now() - startTime) / 1000).toFixed(2); + + // Log exit details + console.log(chalk.gray(`\n[DEBUG] Child process ${modelLabel} (PID: ${child.pid}) closed`)); + console.log(chalk.gray(` Exit code: ${code}`)); + console.log(chalk.gray(` Signal: ${signal || 'none'}`)); + console.log(chalk.gray(` Duration: ${durationSeconds}s`)); + console.log(chalk.gray(` stdout length: ${stdoutBuffer.length} bytes`)); + console.log(chalk.gray(` stderr length: ${errorOutput.length} bytes`)); + + if (code === 0) { + s.stop(chalk.green(`✓ ${modelLabel} completed (${duration}m)`)); + promiseResolve({ success: true, model, isSpecialist }); + } else { + const errorMsg = errorOutput || `Process exited with code ${code}`; + s.stop(chalk.red(`✗ ${modelLabel} failed (${duration}m)`)); + if (errorOutput) { + console.error(chalk.red(errorOutput)); + } + promiseResolve({ success: false, model, isSpecialist, error: errorMsg }); + } + }); + + // Handle errors + child.on('error', (error) => { + const duration = ((Date.now() - startTime) / 1000 / 60).toFixed(1); + console.error(chalk.red(`\n[DEBUG] Child process ${modelLabel} (PID: ${child.pid}) error: ${error.message}`)); + if (error.stack) { + console.error(chalk.gray(error.stack)); + } + s.stop(chalk.red(`✗ ${modelLabel} failed (${duration}m)`)); + console.error(chalk.red(error.message)); + promiseResolve({ success: false, model, isSpecialist, error: error.message }); + }); + } catch (error) { + const duration = ((Date.now() - startTime) / 1000 / 60).toFixed(1); + const errorMsg = error instanceof Error ? error.message : String(error); + console.error(chalk.red(`\n[DEBUG] Benchmark spawn exception for ${modelLabel}: ${errorMsg}`)); + if (error instanceof Error && error.stack) { + console.error(chalk.gray(error.stack)); + } + s.stop(chalk.red(`✗ ${modelLabel} failed (${duration}m)`)); + console.error(chalk.red(errorMsg)); + promiseResolve({ success: false, model, isSpecialist, error: errorMsg }); + } + }); +} + +/** + * Run tasks with limited concurrency to avoid resource exhaustion + * @param items Array of items to process + * @param concurrency Maximum number of concurrent executions + * @param executor Function to execute for each item + * @returns Array of settled results + */ +async function runWithConcurrency( + items: T[], + concurrency: number, + executor: (item: T) => Promise +): Promise[]> { + const results: PromiseSettledResult[] = []; + const totalBatches = Math.ceil(items.length / concurrency); + let batchNumber = 0; + + for (let i = 0; i < items.length; i += concurrency) { + batchNumber++; + const batch = items.slice(i, i + concurrency); + const batchSize = batch.length; + + console.log(chalk.cyan(`\n📦 Batch ${batchNumber}/${totalBatches}: Running ${batchSize} benchmark${batchSize > 1 ? 's' : ''} concurrently\n`)); + + const batchResults = await Promise.allSettled(batch.map(executor)); + results.push(...batchResults); + + const completed = results.length; + const successful = results.filter(r => r.status === 'fulfilled' && (r.value as any).success).length; + const failed = results.filter(r => r.status === 'rejected' || (r.status === 'fulfilled' && !(r.value as any).success)).length; + + console.log(chalk.gray(`\n Progress: ${completed}/${items.length} completed (${successful} ✓, ${failed} ✗)`)); + } + + return results; +} + +async function main() { + console.log(chalk.cyan.bold('\n🚀 Running benchmarks for all models\n')); + + try { + // Parse arguments + const options = parseArgs(); + + // Load models configuration + console.log(chalk.blue('📋 Loading models configuration...')); + const config = loadModelsConfig(options.modelsConfigPath); + + console.log(chalk.green(`✓ Found ${config.vanilla_models.length} vanilla models`)); + console.log(chalk.green(`✓ Found ${config.specialist_models.length} specialist models`)); + + // Generate a single batch ID for all benchmarks in this run + const batchId = uuidv4(); + console.log(chalk.cyan(`\n📦 Batch ID: ${chalk.bold(batchId)}`)); + console.log(chalk.gray(` Use this ID with: pnpm mint:snapshot