diff --git a/.DS_Store b/.DS_Store index ca2b787..813178d 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f155d73..550047f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - '*' + - "*" pull_request: branches: - main @@ -33,10 +33,10 @@ jobs: run: pnpm install --frozen-lockfile - name: Build - run: pnpm build:ci + run: pnpm build - name: Install browsers - run: pnpm exec playwright install --with-deps chromium + run: cd packages/agent && pnpm exec playwright install --with-deps chromium - name: Test run: pnpm test diff --git a/.gitignore b/.gitignore index c4f3fe3..9981de0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ dist .env runs data -internal_docs \ No newline at end of file +internal_docs +.DS_Store diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d7a30f7..bb96fde 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,112 +1,149 @@ -# Contributing +# Contributing to MyCoder -Key points: +First off, thank you for considering contributing to MyCoder! It's people like you that make MyCoder such a great tool. -- Run build, test, and lint before submitting changes -- Use TypeScript types over interfaces -- Maintain test coverage -- Keep documentation updated -- Use the logger system for output +## Code of Conduct + +This project and everyone participating in it is governed by our Code of Conduct. By participating, you are expected to uphold this code. ## Development Setup -```bash -# Clone the repository -git clone https://github.com/bhouston/mycoder.git +### Prerequisites + +- Node.js >= 20.0.0 +- pnpm >= 10.2.1 +- PostgreSQL (for Dashboard) +- Google Cloud SDK (for cloud features) +- Git + +### Getting Started + +1. Fork the repository +2. Clone your fork: + ```bash + git clone https://github.com/your-username/mycoder-monorepo.git + cd mycoder-monorepo + ``` +3. Install dependencies: + ```bash + pnpm install + ``` +4. Build all packages: + ```bash + pnpm build + ``` +5. Start development servers: + ```bash + pnpm dev + ``` + +## Project Architecture + +### Monorepo Structure + +- `/packages/*` - All project packages + - `agent` - Core AI agent system + - `cli` - Command-line interface -# Change directory -cd mycoder +## Development Workflow -# Install dependencies -pnpm install +1. Create a new branch for your feature/fix: -# Create a .env with your API key -echo "ANTHROPIC_API_KEY=[your-api-key]" > .env -``` + ```bash + git checkout -b feature/your-feature-name + ``` -### Development Commands +2. Make your changes, following our coding standards: -- `pnpm run build` - Build the TypeScript code -- `pnpm start` - Run the application -- `pnpm test` - Run tests -- `pnpm run lint` - Lint the code -- `pnpm run format` - Format the code -- `pnpm run clean` - Clean build artifacts + - Use TypeScript for all new code + - Follow the existing code style + - Add tests for new functionality + - Update documentation as needed -## Architecture +3. Run tests and checks: -### Core Components + ```bash + pnpm test # Run tests + pnpm typecheck # Type checking + pnpm lint # Linting + pnpm format # Code formatting + ``` -1. **Tool System** +4. Commit your changes: - - Modular tools for specific functionalities - - Categories: Interaction, I/O, System, Data Management - - Parallel execution capability - - Type-safe definitions + ```bash + git commit -m "feat: add your feature description" + ``` -2. **Agent System** + Follow [Conventional Commits](https://www.conventionalcommits.org/) specification. - - Main agent for orchestration - - Sub-agents for parallel task execution - - Anthropic Claude API integration - - Hierarchical logging +5. Push to your fork and create a Pull Request -3. **Logger System** - - Color-coded component output - - Hierarchical indentation - - Multiple log levels (info, verbose, warn, error) - - Structured data logging +## Package-Specific Guidelines -## Project Structure +### Agent Development -``` -src/ -├── core/ # Core agent and executor logic -├── interfaces/ # Type definitions and interfaces -├── tools/ # Tool implementations -│ ├── interaction/ -│ ├── io/ -│ ├── system/ -│ └── record/ -└── utils/ # Utilities including logger -``` +- Test all new tools thoroughly +- Document tool interfaces completely +- Consider parallel execution opportunities +- Add comprehensive error handling -## Coding Style +### CLI Development -### Terse and Simple +- Follow command naming conventions +- Include help text for all commands +- Add examples to documentation +- Test interactive features thoroughly -Favor a terse coding style that focuses on simplicity and readability. +### Dashboard & Website -## Prefer Types over Interfaces +- Follow component architecture +- Use existing UI components +- Add Storybook stories for new components +- Ensure responsive design -When writing types please use type rather than interfaces as they are more robust. +### GitHub Integration -### Use Logger in Tools/Agents for Output +- Test webhook handlers thoroughly +- Document API interactions +- Follow security best practices +- Add integration tests -The project uses a hierarchical logging system (Logger) that helps distinguish between different agents and tools in the output. The logging system has the following features: +## Testing Guidelines -- `verbose`: Detailed debug information (dimmed version of agent/tool color) -- `info`: Normal operational messages (colored according to agent/tool color) -- `warn`: Warning messages (yellow) -- `error`: Error messages (red) +1. Write tests for new features +2. Maintain existing test coverage +3. Use appropriate testing tools: + - Vitest for unit tests + - Playwright for E2E tests + - Component testing where appropriate -## Check Build Works after Changes +## Documentation -Ensure that `pnpm run build` works after making changes to the code, otherwise you need to make fixes. +- Update README.md files as needed +- Document new features and changes +- Include code examples +- Update API documentation -## Keep Tests & Lint & Format Up-to-Date With Changes +## Getting Help -Please add tests when making changes to the code. Try to sensible tests that a senior dev would write, try to avoid useless tests that don't add value. +- Check existing issues and discussions +- Join our community chat +- Ask questions in pull requests +- Reach out to maintainers -Ensure that the `pnpm test` passes after making changes to the code as well as `pnpm run lint` passes with no warnings or errors. Also run `pnpm run format` to ensure the code is formatted correctly. +## Review Process -If a test fails, but it is not clear why, you can add more tests around that test as well as add more verbose messages to the failed test to help you identify the cause. This will both help you and help others going forward. +1. All changes require review +2. Address review feedback promptly +3. Maintain civil and professional discourse +4. Be patient with the process -## Keep Documentation Up-to-Date with Changes +## Release Process -When making changes to the code, please ensure that the documentation in these files says up to date: +1. Maintainers will handle releases +2. Follow semantic versioning +3. Update changelog entries +4. Tag releases appropriately -- `README.md` -- `ARCHITECTURE.md` -- `CONTRIBUTING.md` -- `TOOLS.md` +Thank you for contributing to MyCoder! 👍 diff --git a/README.md b/README.md index 9465d78..400ae03 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,10 @@ -# MyCoder +# MyCoder Mono-repository -[![NPM Package][npm]][npm-url] -[![NPM Downloads][npm-downloads]][npmtrends-url] -[![CI Status][ci]][ci-url] -[![Discord][discord]][discord-url] +An open-source mono-repository containing the MyCoder agent and cli. -## Overview +!NOTE: To get started with the mycoder agent, [please see the CLI package](packages/cli) -MyCoder is a simple to install, powerful command-line based AI agent system that can perform arbitrary tasks with a particular focus on coding tasks. It uses a modular tool-based architecture that allows it to interact with files, execute commands, make network requests, and spawn sub-agents for parallel task execution. - -Please join the MyCoder.ai discord for support: https://discord.gg/5K6TYrHGHt - -![MyCoder Screenshot](./docs/Screenshot1.gif) - -## WARNING and LIABILITY WAIVER - -This tool can do anything on your command line that you ask it to. It can delete files, install software, and even send data to remote servers. It is a powerful tool that should be used with caution. By using this tool, you agree that the authors and contributors are not responsible for any damage that may occur as a result of using this tool. - -## API Key Required - -Before using MyCoder, you must have an ANTHROPIC_API_KEY specified either: - -- As an environment variable, "export ANTHROPIC_API_KEY=[your-api-key]" or -- In a .env file in the folder you run `mycoder` from - -Get an API key from https://www.anthropic.com/api - -## Quick Start - -```bash -# Install globally -npm install -g mycoder - -# Start MyCoder with a prompt -mycoder "fix all build errors and ensure the tests pass" - -# Start in interactive mode -mycoder -i - -# Read prompt from a file -mycoder --promptFile=your-prompt.txt -``` - -## Key Features +## Features - 🤖 **AI-Powered**: Leverages Anthropic's Claude API for intelligent decision making - 🛠️ **Extensible Tool System**: Modular architecture with various tool categories @@ -51,118 +13,127 @@ mycoder --promptFile=your-prompt.txt - 🔍 **Smart Logging**: Hierarchical, color-coded logging system for clear output - 👤 **Human Compatible**: Uses README.md, project files and shell commands to build its own context -### CLI Options +Please join the MyCoder.ai discord for support: https://discord.gg/5K6TYrHGHt + +## 🚀 Quick Start -- `[prompt]`: Main prompt text (positional argument) -- `-i, --interactive`: Run in interactive mode, asking for prompts -- `-f, --file`: Read prompt from a specified file -- `--log`: Set log level (info, verbose, warn, error) -- `-h, --help`: Show help -- `-V, --version`: Show version +### Prerequisites -## Available Tools +- Node.js >= 20.0.0 +- pnpm >= 10.2.1 +- ANTHROPIC_API_KEY (for AI features) -To list the current tools that the agent makes use of run: +### Installation ```bash -mycoder tools +# Install dependencies +pnpm install + +# Build all packages +pnpm build + +# Run locally built cli in interactive mode +pnpm -f mycoder -i ``` -## Example Use Cases & Prompts +## 📦 Packages -MyCoder excels at various software development tasks. Here are some example prompts and use cases: +### [`cli`](packages/cli) -### Code Migration & Updates +Command-line interface for AI-powered coding tasks: -```bash -# Converting test framework -mycoder "Convert all Jest tests in the src/ directory to Vitest, updating any necessary configuration files and dependencies" +- Interactive mode +- File-based prompt support +- Code migration and refactoring capabilities -# Dependency updates -mycoder "Update all dependencies to their latest versions, handle any breaking changes, and ensure all tests pass" -``` +### [`agent`](packages/agent) -### Code Refactoring +Core AI agent system powering MyCoder's intelligent features: -```bash -# Class refactoring -mycoder "Refactor the UserService class in src/services/UserService.ts to use the repository pattern, update all files that use this class, and ensure tests pass" +- Extensible Tool System +- Parallel Execution with sub-agents +- AI-Powered using Anthropic's Claude API -# API modernization -mycoder "Convert all callback-based functions in the project to use async/await, update tests accordingly" -``` +## 🛠 Development -### Feature Implementation +### Common Commands ```bash -# CLI enhancement -mycoder "Add a new global --debug command line option that enables verbose logging throughout the application" +# Development mode +pnpm dev -# New functionality -mycoder "Create a new caching system for API responses using Redis, including configuration options and unit tests" -``` +# Build all packages +pnpm build -### Maintenance & Fixes +# Run tests +pnpm test -```bash -# Build fixes -mycoder "Fix all TypeScript build errors and ensure all tests pass" +# Type checking +pnpm typecheck -# Test coverage -mycoder "Add unit tests for all untested functions in the src/utils directory, aiming for 80% coverage" -``` +# Linting +pnpm lint -### Documentation +# Formatting +pnpm format -```bash -# Documentation generation -mycoder "Generate comprehensive JSDoc documentation for all exported functions and update the API documentation in the docs/ directory" +# Clean build artifacts +pnpm clean -# Architecture documentation -mycoder "Analyze the current codebase and create detailed architecture documentation including component diagrams and data flow" +# Clean everything including node_modules +pnpm clean:all ``` -These examples showcase MyCoder's ability to handle complex software development tasks. The tool uses its understanding of software development best practices, project structure, and coding standards to execute these tasks while maintaining code quality and test coverage. +## 📚 Documentation + +Each package contains detailed documentation in its respective README.md file. See individual package directories for: -## Technical Requirements +- Detailed setup instructions +- API documentation +- Development guidelines +- Package-specific commands -- Node.js >=18.0.0 -- npm or pnpm +## 📦 Publishing -## Contributing +This monorepo uses [Changesets](https://github.com/changesets/changesets) to manage versions and publish packages. The following packages are published to npm: -We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for our development workflow, coding guidelines, and testing procedures. +- `mycoder` - CLI package +- `mycoder-agent` - Core agent functionality -## Development Workflow +To publish changes: -### Commit Messages +1. Make your code changes +2. Create a changeset (documents your changes): -This project uses [Conventional Commits](https://www.conventionalcommits.org/) for commit messages. See [COMMIT_CONVENTION.md](COMMIT_CONVENTION.md) for detailed guidelines. + ```bash + pnpm changeset + ``` -### CI/CD Pipeline +3. Select the packages that have changes +4. Write a clear description of the changes +5. Commit the generated changeset file -The project uses GitHub Actions for continuous integration: +When ready to publish: -- A single CI pipeline automatically builds and tests the code on all branches and pull requests to main +1. Update versions based on changesets: -The release process is managed manually using [changesets](https://github.com/changesets/changesets) which: + ```bash + pnpm changeset version + ``` -1. Determines version bumps based on commit messages -2. Generates changelogs +2. Review the changes +3. Publish packages: -Releases to GitHub and publishing to npm are performed manually after review. + ```bash + pnpm publish -r + ``` -## License +Note: Both packages are versioned together to ensure compatibility. -MIT License +## 🤝 Contributing -[npm]: https://img.shields.io/npm/v/mycoder -[npm-downloads]: https://img.shields.io/npm/dw/mycoder -[npm]: https://img.shields.io/npm/v/mycoder -[npm-url]: https://www.npmjs.com/package/mycoder -[npm-downloads]: https://img.shields.io/npm/dw/mycoder -[npmtrends-url]: https://www.npmtrends.com/mycoder -[ci]: https://img.shields.io/github/checks-status/bhouston/mycoder/main -[ci-url]: https://github.com/bhouston/mycoder/actions -[discord]: https://img.shields.io/discord/1339025847331328000 -[discord-url]: https://discord.gg/5K6TYrHGHt +1. Fork the repository +2. Create your feature branch +3. Commit your changes +4. Push to the branch +5. Create a Pull Request diff --git a/bin/cli.js b/bin/cli.js deleted file mode 100755 index 924fff1..0000000 --- a/bin/cli.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -import '../dist/index.js'; diff --git a/docs/Screenshot1.gif b/docs/Screenshot1.gif deleted file mode 100644 index 0e76218..0000000 Binary files a/docs/Screenshot1.gif and /dev/null differ diff --git a/eslint.config.js b/eslint.config.js index cf9bdf3..142221d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,78 +1,80 @@ // eslint.config.js -import js from '@eslint/js'; -import ts from 'typescript-eslint'; -import prettierRecommended from 'eslint-plugin-prettier/recommended'; -import importPlugin from 'eslint-plugin-import'; -import unusedImports from 'eslint-plugin-unused-imports'; -import pluginPromise from 'eslint-plugin-promise'; +import js from "@eslint/js"; +import pluginImport from "eslint-plugin-import"; +import prettierRecommended from "eslint-plugin-prettier/recommended"; +import pluginPromise from "eslint-plugin-promise"; +import pluginUnusedImports from "eslint-plugin-unused-imports"; +import ts from "typescript-eslint"; export default ts.config( js.configs.recommended, - ts.configs.recommendedTypeChecked, + ts.configs.recommended, prettierRecommended, - importPlugin.flatConfigs.recommended, - pluginPromise.configs['flat/recommended'], + pluginPromise.configs["flat/recommended"], { - languageOptions: { - ecmaVersion: 2022, - sourceType: 'module', - parserOptions: { - project: ['./tsconfig.eslint.json'], - tsconfigRootDir: import.meta.dirname, - }, - }, plugins: { - 'unused-imports': unusedImports, + import: pluginImport, + "unused-imports": pluginUnusedImports, }, - files: ['{src,test}/**/*.{js,ts}'], rules: { - // Basic code quality rules - 'no-console': 'off', - 'prefer-const': 'warn', - 'no-var': 'warn', - eqeqeq: ['warn', 'always'], - - // Light complexity rules - complexity: ['warn', { max: 20 }], - 'max-depth': ['warn', { max: 4 }], - 'max-lines-per-function': ['warn', { max: 150 }], + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-unused-vars": "off", // turned off in favor of unused-imports/no-unused-vars + "@typescript-eslint/no-require-imports": "warn", - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unused-vars': [ - 'error', - { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, + // Remove unused imports + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "error", + { + vars: "all", + varsIgnorePattern: "^_", + args: "after-used", + argsIgnorePattern: "^_", + }, ], - 'import/no-unresolved': 'off', - 'import/named': 'off', - 'import/extensions': [ - 'error', - 'ignorePackages', - { js: 'always', ts: 'never' }, + // Import organization + "import/order": [ + "error", + { + groups: [ + "builtin", + "external", + "internal", + "parent", + "sibling", + "index", + "object", + "type", + ], + "newlines-between": "always", + alphabetize: { order: "asc", caseInsensitive: true }, + warnOnUnassignedImports: true, + }, ], - - 'no-unused-vars': 'off', // or "@typescript-eslint/no-unused-vars": "off", - 'unused-imports/no-unused-imports': 'error', - - 'promise/always-return': 'error', - 'promise/no-return-wrap': 'error', - 'promise/param-names': 'error', - 'promise/catch-or-return': 'error', - 'promise/no-native': 'off', - 'promise/no-nesting': 'warn', - 'promise/no-promise-in-callback': 'warn', - 'promise/no-callback-in-promise': 'warn', - 'promise/avoid-new': 'off', - 'promise/no-new-statics': 'error', - 'promise/no-return-in-finally': 'warn', - 'promise/valid-params': 'warn', - 'promise/no-multiple-resolved': 'error', + "import/no-duplicates": "error", }, + settings: { + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".tsx"], + }, + "import/resolver": { + typescript: { + alwaysTryTypes: true, + project: ["./packages/*/tsconfig.json"], + }, + }, + }, + }, + { + ignores: [ + "**/dist", + "**/_doNotUse", + "**/node_modules", + "**/.vinxi", + "**/.output", + "**/pnpm-lock.yaml", + "**/routeTree.gen.ts", + ], }, ); diff --git a/package.json b/package.json index 330b07b..bf95132 100644 --- a/package.json +++ b/package.json @@ -1,87 +1,54 @@ { - "name": "mycoder", - "description": "A command line tool using agent that can do arbitrary tasks, including coding tasks", - "version": "0.1.2", + "name": "mycoder-monorepo", + "version": "0.0.1", "type": "module", - "bin": "./bin/cli.js", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", "packageManager": "pnpm@10.2.1", "engines": { "node": ">=18.0.0" }, - "repository": { - "type": "git", - "url": "git+https://github.com/bhouston/mycoder.git" - }, - "homepage": "https://mycoder.ai", - "bugs": { - "url": "https://github.com/bhouston/mycoder/issues" - }, "scripts": { - "start": "node --no-deprecation dist/index.js", - "build": "tsc --noEmit && tsc", - "build:ci": "tsc", - "clean": "rimraf dist", - "clean:all": "rimraf dist node_modules", - "lint": "eslint \"{src,test}/**/*.{js,ts}\" --fix", - "format": "prettier --write .", - "test": "vitest run", - "test:watch": "vitest", - "test:ci": "vitest --run --coverage", + "dev": "pnpm -r dev", + "build": "pnpm -r build", + "start": "pnpm -r start", + "test": "pnpm -r test", + "typecheck": "pnpm -r typecheck", + "lint": "eslint . --fix", + "format": "prettier . --write", + "clean": "pnpm -r clean", + "clean:all": "pnpm -r clean:all && rimraf node_modules", + "cloc": "npx cloc * --exclude-dir=node_modules,dist,.vinxi,.output", + "gcloud-setup": "gcloud auth application-default login && gcloud config set account \"ben@drivecore.ai\" && gcloud config set project drivecore-primary && gcloud config set run/region us-central1", "changeset": "changeset", "version": "changeset version", - "prepublishOnly": "pnpm run clean && pnpm run build && pnpm run test" + "release": "changeset publish" }, - "keywords": [ - "ai", - "agent", - "mycoder", - "swe", - "swe-agent", - "claude", - "auto-coder", - "auto-gpt", - "typescript", - "openai", - "automation" - ], - "author": "Ben Houston", - "license": "MIT", "dependencies": { - "@anthropic-ai/sdk": "^0.36", - "@playwright/test": "^1.50.1", - "@vitest/browser": "^3.0.5", - "chalk": "^5", - "dotenv": "^16", - "playwright": "^1.50.1", - "semver": "^7.7.1", - "source-map-support": "^0.5", - "uuid": "^11", - "yargs": "^17", - "yargs-file-commands": "^0.0.19", - "zod": "^3", - "zod-to-json-schema": "^3" + "rimraf": "^6.0.1" }, "devDependencies": { - "@changesets/cli": "^2", + "@changesets/cli": "^2.28.1", "@eslint/js": "^9", - "@types/node": "^18", - "@types/uuid": "^10", - "@types/yargs": "^17", - "@typescript-eslint/eslint-plugin": "^8", - "@typescript-eslint/parser": "^8", - "eslint": "^9", + "@typescript-eslint/eslint-plugin": "^8.23.0", + "@typescript-eslint/parser": "^8.23.0", + "eslint": "^9.0.0", "eslint-config-prettier": "^9", + "eslint-import-resolver-typescript": "^3.8.3", "eslint-plugin-import": "^2", "eslint-plugin-prettier": "^5", "eslint-plugin-promise": "^7.2.1", - "eslint-plugin-unused-imports": "^4", - "prettier": "^3", - "rimraf": "^5", - "type-fest": "^4", - "typescript": "^5", - "typescript-eslint": "^8", - "vitest": "^3" + "eslint-plugin-unused-imports": "^4.1.4", + "prettier": "^3.5.1", + "typescript-eslint": "^8.23.0" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "@parcel/watcher", + "@prisma/client", + "@prisma/engines", + "bcrypt", + "esbuild", + "msw", + "prisma" + ] } } diff --git a/packages/agent/.gitignore b/packages/agent/.gitignore new file mode 100644 index 0000000..4bd2941 --- /dev/null +++ b/packages/agent/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +node_modules +serviceAccountKey.json +.env +dist +bhouston-general-hosting-a254cfa6b15a.json diff --git a/packages/agent/.npmignore b/packages/agent/.npmignore new file mode 100644 index 0000000..0be8273 --- /dev/null +++ b/packages/agent/.npmignore @@ -0,0 +1,14 @@ +.changeset +.vscode +dist/*.test.* +internal_docs +node_modules +src +.env +.gitignore +.npmignore +.prettierignore +eslint.config.js +pnpm-lock.yaml +tsconfig.json +vitest.config.js diff --git a/packages/agent/CHANGELOG.md b/packages/agent/CHANGELOG.md new file mode 100644 index 0000000..52c71c2 --- /dev/null +++ b/packages/agent/CHANGELOG.md @@ -0,0 +1,7 @@ +# mycoder-agent + +## 0.1.3 + +### Patch Changes + +- Improved sub-agent directions, do not assume a lack of a response is an agent being done, rather look for explicit confirmation, allow for sub-agents to have optional custom working directorires, break agent framework into the mycoder-agent package diff --git a/packages/agent/LICENSE b/packages/agent/LICENSE new file mode 100644 index 0000000..87c6b31 --- /dev/null +++ b/packages/agent/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright © 2025-2025 mycoder authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/agent/README.md b/packages/agent/README.md new file mode 100644 index 0000000..25d1eed --- /dev/null +++ b/packages/agent/README.md @@ -0,0 +1,84 @@ +# MyCoder Agent + +Core AI agent system that powers the MyCoder CLI tool. This package provides a modular tool-based architecture that allows AI agents to interact with files, execute commands, make network requests, and spawn sub-agents for parallel task execution. + +## Overview + +The MyCoder Agent system is built around a few key concepts: + +- 🛠️ **Extensible Tool System**: Modular architecture with various tool categories +- 🔄 **Parallel Execution**: Ability to spawn sub-agents for concurrent task processing +- 🤖 **AI-Powered**: Leverages Anthropic's Claude API for intelligent decision making +- 🔍 **Smart Logging**: Hierarchical, color-coded logging system for clear output + +Please join the MyCoder.ai discord for support: https://discord.gg/5K6TYrHGHt + +## Installation + +```bash +npm install mycoder-agent +``` + +## API Key Required + +Before using MyCoder Agent, you must have an ANTHROPIC_API_KEY specified either: + +- As an environment variable, "export ANTHROPIC_API_KEY=[your-api-key]" or +- In a .env file in your project root + +Get an API key from https://www.anthropic.com/api + +## Core Components + +### Tool System + +- Modular tools for specific functionalities +- Categories: Interaction, I/O, System, Data Management +- Parallel execution capability +- Type-safe definitions + +### Agent System + +- Main agent for orchestration +- Sub-agents for parallel task execution +- Anthropic Claude API integration +- Hierarchical logging + +### Logger System + +- Color-coded component output +- Hierarchical indentation +- Multiple log levels (info, verbose, warn, error) +- Structured data logging + +## Project Structure + +``` +src/ +├── core/ # Core agent and executor logic +├── interfaces/ # Type definitions and interfaces +├── tools/ # Tool implementations +│ ├── interaction/ +│ ├── io/ +│ ├── system/ +│ └── record/ +└── utils/ # Utilities including logger +``` + +## Available Tools + +The agent system provides various tools in different categories: + +- **Interaction Tools**: User prompts, sub-agent creation +- **I/O Tools**: File reading/writing, HTTP requests +- **System Tools**: Shell command execution, process management +- **Browser Tools**: Web automation and scraping capabilities + +## Technical Requirements + +- Node.js >= 20.0.0 +- pnpm >= 10.2.1 + +## Contributing + +We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow and guidelines. diff --git a/packages/agent/package.json b/packages/agent/package.json new file mode 100644 index 0000000..0ce6f87 --- /dev/null +++ b/packages/agent/package.json @@ -0,0 +1,65 @@ +{ + "name": "mycoder-agent", + "version": "0.1.3", + "description": "Agent module for mycoder - an AI-powered software development assistant", + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "packageManager": "pnpm@10.2.1", + "engines": { + "node": ">=18.0.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/drivecore/mycoder-monorepo.git" + }, + "homepage": "https://mycoder.ai", + "bugs": { + "url": "https://github.com/drivecore/mycoder-monorepo/issues" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "dev": "tsc --watch", + "build": "tsc", + "test": "vitest run", + "test:coverage": "vitest run --coverage", + "typecheck": "tsc --noEmit", + "clean": "rimraf dist", + "clean:all": "rimraf node_modules dist", + "prepublishOnly": "pnpm run clean && pnpm run build && pnpm run test" + }, + "keywords": [ + "ai", + "agent", + "mycoder", + "swe", + "swe-agent", + "claude", + "auto-coder", + "typescript" + ], + "author": "Ben Houston", + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "^0.37", + "@playwright/test": "^1.50.1", + "@vitest/browser": "^3.0.5", + "chalk": "^5", + "dotenv": "^16", + "playwright": "^1.50.1", + "uuid": "^11", + "zod": "^3", + "zod-to-json-schema": "^3" + }, + "devDependencies": { + "@types/node": "^18", + "@types/uuid": "^10", + "rimraf": "^5", + "type-fest": "^4", + "typescript": "^5", + "vitest": "^3" + } +} diff --git a/src/core/executeToolCall.ts b/packages/agent/src/core/executeToolCall.ts similarity index 74% rename from src/core/executeToolCall.ts rename to packages/agent/src/core/executeToolCall.ts index 9836f7e..7ea6ad8 100644 --- a/src/core/executeToolCall.ts +++ b/packages/agent/src/core/executeToolCall.ts @@ -1,5 +1,6 @@ -import { Tool, ToolCall } from './types.js'; -import { Logger } from '../utils/logger.js'; +import { Logger } from "../utils/logger.js"; + +import { Tool, ToolCall } from "./types.js"; const OUTPUT_LIMIT = 12 * 1024; // 10KB limit @@ -10,6 +11,7 @@ export const executeToolCall = async ( toolCall: ToolCall, tools: Tool[], parentLogger: Logger, + options?: { workingDirectory?: string }, ): Promise => { const logger = new Logger({ name: `Tool:${toolCall.name}`, @@ -27,23 +29,26 @@ export const executeToolCall = async ( if (tool.logParameters) { tool.logParameters(toolCall.input, toolContext); } else { - logger.info('Parameters:'); + logger.info("Parameters:"); Object.entries(toolCall.input).forEach(([name, value]) => { logger.info(` - ${name}: ${JSON.stringify(value).substring(0, 60)}`); }); } // TODO: validate JSON schema for input - const output = await tool.execute(toolCall.input, { logger }); + const output = await tool.execute(toolCall.input, { + logger, + workingDirectory: options?.workingDirectory, + }); // for each result log it and its name if (tool.logReturns) { tool.logReturns(output, toolContext); } else { - logger.info('Results:'); - if (typeof output === 'string') { + logger.info("Results:"); + if (typeof output === "string") { logger.info(` - ${output}`); - } else if (typeof output === 'object') { + } else if (typeof output === "object") { Object.entries(output).forEach(([name, value]) => { logger.info(` - ${name}: ${JSON.stringify(value).substring(0, 60)}`); }); @@ -51,7 +56,7 @@ export const executeToolCall = async ( } const toolOutput = - typeof output === 'string' ? output : JSON.stringify(output, null, 2); + typeof output === "string" ? output : JSON.stringify(output, null, 2); return toolOutput.length > OUTPUT_LIMIT ? `${toolOutput.slice(0, OUTPUT_LIMIT)}...(truncated)` : toolOutput; diff --git a/packages/agent/src/core/toolAgent.respawn.test.ts b/packages/agent/src/core/toolAgent.respawn.test.ts new file mode 100644 index 0000000..a8a8334 --- /dev/null +++ b/packages/agent/src/core/toolAgent.respawn.test.ts @@ -0,0 +1,56 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +import { toolAgent } from "../../src/core/toolAgent.js"; +import { getTools } from "../../src/tools/getTools.js"; +import { Logger } from "../../src/utils/logger.js"; + +// Mock Anthropic SDK +vi.mock("@anthropic-ai/sdk", () => { + return { + default: vi.fn().mockImplementation(() => ({ + messages: { + create: vi + .fn() + .mockResolvedValueOnce({ + content: [ + { + type: "tool_use", + name: "respawn", + id: "test-id", + input: { respawnContext: "new context" }, + }, + ], + usage: { input_tokens: 10, output_tokens: 10 }, + }) + .mockResolvedValueOnce({ + content: [], + usage: { input_tokens: 5, output_tokens: 5 }, + }), + }, + })), + }; +}); + +describe("toolAgent respawn functionality", () => { + const mockLogger = new Logger({ name: "test" }); + const tools = getTools(); + + beforeEach(() => { + process.env.ANTHROPIC_API_KEY = "test-key"; + vi.clearAllMocks(); + }); + + it("should handle respawn tool calls", async () => { + const result = await toolAgent("initial prompt", tools, mockLogger, { + maxIterations: 2, // Need at least 2 iterations for respawn + empty response + model: "test-model", + maxTokens: 100, + temperature: 0, + getSystemPrompt: () => "test system prompt", + }); + + expect(result.result).toBe( + "Maximum sub-agent iterations reach without successful completion", + ); + }); +}); diff --git a/packages/agent/src/core/toolAgent.test.ts b/packages/agent/src/core/toolAgent.test.ts new file mode 100644 index 0000000..6f62394 --- /dev/null +++ b/packages/agent/src/core/toolAgent.test.ts @@ -0,0 +1,195 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; + +import { MockLogger } from "../utils/mockLogger.js"; + +import { executeToolCall } from "./executeToolCall.js"; +import { toolAgent } from "./toolAgent.js"; +import { Tool } from "./types.js"; + +const logger = new MockLogger(); + +// Mock configuration for testing +const testConfig = { + maxIterations: 50, + model: "claude-3-7-sonnet-latest", + maxTokens: 4096, + temperature: 0.7, + getSystemPrompt: () => "Test system prompt", +}; + +// Mock Anthropic client response +const mockResponse = { + content: [ + { + type: "tool_use", + name: "sequenceComplete", + id: "1", + input: { result: "Test complete" }, + }, + ], + usage: { input_tokens: 10, output_tokens: 10 }, + model: "claude-3-7-sonnet-latest", + role: "assistant", + id: "msg_123", +}; + +// Mock Anthropic SDK +const mockCreate = vi.fn().mockImplementation(() => mockResponse); +vi.mock("@anthropic-ai/sdk", () => ({ + default: class { + messages = { + create: mockCreate, + }; + }, +})); + +describe("toolAgent", () => { + beforeEach(() => { + process.env.ANTHROPIC_API_KEY = "test-key"; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + // Mock tool for testing + const mockTool: Tool = { + name: "mockTool", + description: "A mock tool for testing", + parameters: { + type: "object", + properties: { + input: { + type: "string", + description: "Test input", + }, + }, + required: ["input"], + }, + returns: { + type: "string", + description: "The processed result", + }, + execute: ({ input }) => Promise.resolve(`Processed: ${input}`), + }; + + const sequenceCompleteTool: Tool = { + name: "sequenceComplete", + description: "Completes the sequence", + parameters: { + type: "object", + properties: { + result: { + type: "string", + description: "The final result", + }, + }, + required: ["result"], + }, + returns: { + type: "string", + description: "The final result", + }, + execute: ({ result }) => Promise.resolve(result), + }; + + it("should execute tool calls", async () => { + const result = await executeToolCall( + { + id: "1", + name: "mockTool", + input: { input: "test" }, + }, + [mockTool], + logger, + ); + + expect(result.includes("Processed: test")).toBeTruthy(); + }); + + it("should handle unknown tools", async () => { + await expect( + executeToolCall( + { + id: "1", + name: "nonexistentTool", + input: {}, + }, + [mockTool], + logger, + ), + ).rejects.toThrow("No tool with the name 'nonexistentTool' exists."); + }); + + it("should handle tool execution errors", async () => { + const errorTool: Tool = { + name: "errorTool", + description: "A tool that always fails", + parameters: { + type: "object", + properties: {}, + required: [], + }, + returns: { + type: "string", + description: "Error message", + }, + execute: () => { + throw new Error("Deliberate failure"); + }, + }; + + await expect( + executeToolCall( + { + id: "1", + name: "errorTool", + input: {}, + }, + [errorTool], + logger, + ), + ).rejects.toThrow("Deliberate failure"); + }); + + // Test empty response handling + it("should handle empty responses by sending a reminder", async () => { + // Reset the mock and set up the sequence of responses + mockCreate.mockReset(); + mockCreate + .mockResolvedValueOnce({ + content: [], + usage: { input_tokens: 5, output_tokens: 5 }, + }) + .mockResolvedValueOnce(mockResponse); + + const result = await toolAgent( + "Test prompt", + [sequenceCompleteTool], + logger, + testConfig, + ); + + // Verify that create was called twice (once for empty response, once for completion) + expect(mockCreate).toHaveBeenCalledTimes(2); + expect(result.result).toBe("Test complete"); + }); + + // New tests for async system prompt + it("should handle async system prompt", async () => { + // Reset mock and set expected response + mockCreate.mockReset(); + mockCreate.mockResolvedValue(mockResponse); + + const result = await toolAgent( + "Test prompt", + [sequenceCompleteTool], + logger, + testConfig, + ); + + expect(result.result).toBe("Test complete"); + expect(result.tokens.input).toBe(10); + expect(result.tokens.output).toBe(10); + }); +}); diff --git a/src/core/toolAgent.ts b/packages/agent/src/core/toolAgent.ts similarity index 57% rename from src/core/toolAgent.ts rename to packages/agent/src/core/toolAgent.ts index fb751e6..41570a8 100644 --- a/src/core/toolAgent.ts +++ b/packages/agent/src/core/toolAgent.ts @@ -1,16 +1,18 @@ -import Anthropic from '@anthropic-ai/sdk'; -import { executeToolCall } from './executeToolCall.js'; -import { Logger } from '../utils/logger.js'; +import { execSync } from "child_process"; + +import Anthropic from "@anthropic-ai/sdk"; + +import { getAnthropicApiKeyError } from "../utils/errors.js"; +import { Logger } from "../utils/logger.js"; + +import { executeToolCall } from "./executeToolCall.js"; import { Tool, TextContent, ToolUseContent, ToolResultContent, Message, -} from './types.js'; -import { execSync } from 'child_process'; - -import { getAnthropicApiKeyError } from '../utils/errors.js'; +} from "./types.js"; export interface ToolAgentResult { result: string; @@ -23,7 +25,7 @@ export interface ToolAgentResult { const CONFIG = { maxIterations: 50, - model: 'claude-3-5-sonnet-20241022', + model: "claude-3-7-sonnet-latest", maxTokens: 4096, temperature: 0.7, getSystemPrompt: () => { @@ -37,48 +39,48 @@ const CONFIG = { }; const context = { - pwd: getCommandOutput('pwd', 'current directory'), - files: getCommandOutput('ls -la', 'file listing'), - system: getCommandOutput('uname -a', 'system information'), + pwd: getCommandOutput("pwd", "current directory"), + files: getCommandOutput("ls -la", "file listing"), + system: getCommandOutput("uname -a", "system information"), datetime: new Date().toString(), }; return [ - 'You are an AI agent that can use tools to accomplish tasks.', - '', - 'Current Context:', + "You are an AI agent that can use tools to accomplish tasks.", + "", + "Current Context:", `Directory: ${context.pwd}`, - 'Files:', + "Files:", context.files, `System: ${context.system}`, `DateTime: ${context.datetime}`, - '', - 'You prefer to call tools in parallel when possible because it leads to faster execution and less resource usage.', - 'When done, call the sequenceComplete tool with your results to indicate that the sequence has completed.', - '', - 'For coding tasks:', - '0. Try to break large tasks into smaller sub-tasks that can be completed and verified sequentially.', + "", + "You prefer to call tools in parallel when possible because it leads to faster execution and less resource usage.", + "When done, call the sequenceComplete tool with your results to indicate that the sequence has completed.", + "", + "For coding tasks:", + "0. Try to break large tasks into smaller sub-tasks that can be completed and verified sequentially.", " - trying to make lots of changes in one go can make it really hard to identify when something doesn't work", - ' - use sub-agents for each sub-task, leaving the main agent in a supervisory role', - ' - when possible ensure the project compiles/builds and the tests pass after each sub-task', - ' - give the sub-agents the guidance and context necessary be successful', - '1. First understand the context by:', - ' - Reading README.md, CONTRIBUTING.md, and similar documentation', - ' - Checking project configuration files (e.g., package.json)', - ' - Understanding coding standards', - '2. Ensure changes:', - ' - Follow project conventions', - ' - Build successfully', - ' - Pass all tests', - '3. Update documentation as needed', - '4. Consider adding documentation if you encountered setup/understanding challenges', - '', - 'Feel free to use Google and Bing via the browser tools to search for information or for ideas when you get stuck.', - '', - 'When you run into issues or unexpected results, take a step back and read the project documentation and configuration files and look at other source files in the project for examples of what works.', - '', - 'Use sub-agents for parallel tasks, providing them with specific context they need rather than having them rediscover it.', - ].join('\\n'); + " - use sub-agents for each sub-task, leaving the main agent in a supervisory role", + " - when possible ensure the project compiles/builds and the tests pass after each sub-task", + " - give the sub-agents the guidance and context necessary be successful", + "1. First understand the context by:", + " - Reading README.md, CONTRIBUTING.md, and similar documentation", + " - Checking project configuration files (e.g., package.json)", + " - Understanding coding standards", + "2. Ensure changes:", + " - Follow project conventions", + " - Build successfully", + " - Pass all tests", + "3. Update documentation as needed", + "4. Consider adding documentation if you encountered setup/understanding challenges", + "", + "Feel free to use Google and Bing via the browser tools to search for information or for ideas when you get stuck.", + "", + "When you run into issues or unexpected results, take a step back and read the project documentation and configuration files and look at other source files in the project for examples of what works.", + "", + "Use sub-agents for parallel tasks, providing them with specific context they need rather than having them rediscover it.", + ].join("\\n"); }, }; @@ -93,11 +95,11 @@ function processResponse(response: Anthropic.Message) { const toolCalls: ToolUseContent[] = []; for (const message of response.content) { - if (message.type === 'text') { - content.push({ type: 'text', text: message.text }); - } else if (message.type === 'tool_use') { + if (message.type === "text") { + content.push({ type: "text", text: message.text }); + } else if (message.type === "tool_use") { const toolUse: ToolUseContent = { - type: 'tool_use', + type: "tool_use", name: message.name, id: message.id, input: message.input, @@ -115,6 +117,7 @@ async function executeTools( tools: Tool[], messages: Message[], logger: Logger, + workingDirectory?: string, ): Promise { if (toolCalls.length === 0) { return { sequenceCompleted: false, toolResults: [] }; @@ -123,15 +126,15 @@ async function executeTools( logger.verbose(`Executing ${toolCalls.length} tool calls`); // Check for respawn tool call - const respawnCall = toolCalls.find((call) => call.name === 'respawn'); + const respawnCall = toolCalls.find((call) => call.name === "respawn"); if (respawnCall) { return { sequenceCompleted: false, toolResults: [ { - type: 'tool_result', + type: "tool_result", tool_use_id: respawnCall.id, - content: 'Respawn initiated', + content: "Respawn initiated", }, ], respawn: { @@ -142,17 +145,19 @@ async function executeTools( const results = await Promise.all( toolCalls.map(async (call) => { - let toolResult = ''; + let toolResult = ""; try { - toolResult = await executeToolCall(call, tools, logger); + toolResult = await executeToolCall(call, tools, logger, { + workingDirectory, + }); } catch (error: any) { toolResult = `Error: Exception thrown during tool execution. Type: ${error.constructor.name}, Message: ${error.message}`; } return { - type: 'tool_result' as const, + type: "tool_result" as const, tool_use_id: call.id, content: toolResult, - isComplete: call.name === 'sequenceComplete', + isComplete: call.name === "sequenceComplete", }; }), ); @@ -166,10 +171,10 @@ async function executeTools( const sequenceCompleted = results.some((r) => r.isComplete); const completionResult = results.find((r) => r.isComplete)?.content; - messages.push({ role: 'user', content: toolResults }); + messages.push({ role: "user", content: toolResults }); if (sequenceCompleted) { - logger.verbose('Sequence completed', { completionResult }); + logger.verbose("Sequence completed", { completionResult }); } return { sequenceCompleted, completionResult, toolResults }; @@ -180,9 +185,10 @@ export const toolAgent = async ( tools: Tool[], logger: Logger, config = CONFIG, + workingDirectory?: string, ): Promise => { - logger.verbose('Starting agent execution'); - logger.verbose('Initial prompt:', initialPrompt); + logger.verbose("Starting agent execution"); + logger.verbose("Initial prompt:", initialPrompt); let totalInputTokens = 0; let totalOutputTokens = 0; @@ -194,12 +200,12 @@ export const toolAgent = async ( const client = new Anthropic({ apiKey }); const messages: Message[] = [ { - role: 'user', - content: [{ type: 'text', text: initialPrompt }], + role: "user", + content: [{ type: "text", text: initialPrompt }], }, ]; - logger.debug('User message:', initialPrompt); + logger.debug("User message:", initialPrompt); // Get the system prompt once at the start const systemPrompt = config.getSystemPrompt(); @@ -223,23 +229,22 @@ export const toolAgent = async ( description: t.description, input_schema: t.parameters as Anthropic.Tool.InputSchema, })), - tool_choice: { type: 'auto' }, + tool_choice: { type: "auto" }, }); if (!response.content.length) { - const result = { - result: - 'Agent returned empty message implying it is done its given task', - tokens: { - input: totalInputTokens, - output: totalOutputTokens, - }, - interactions, - }; - logger.verbose( - `Agent completed with ${result.tokens.input} input tokens, ${result.tokens.output} output tokens in ${result.interactions} interactions`, - ); - return result; + // Instead of treating empty response as completion, remind the agent + logger.verbose("Received empty response from agent, sending reminder"); + messages.push({ + role: "user", + content: [ + { + type: "text", + text: "I notice you sent an empty response. If you are done with your tasks, please call the sequenceComplete tool with your results. If you are waiting for other tools to complete, you can use the sleep tool to wait before checking again.", + }, + ], + }); + continue; } totalInputTokens += response.usage.input_tokens; @@ -249,13 +254,13 @@ export const toolAgent = async ( ); const { content, toolCalls } = processResponse(response); - messages.push({ role: 'assistant', content }); + messages.push({ role: "assistant", content }); // Log the assistant's message const assistantMessage = content - .filter((c) => c.type === 'text') + .filter((c) => c.type === "text") .map((c) => c.text) - .join('\\n'); + .join("\\n"); if (assistantMessage) { logger.info(assistantMessage); } @@ -265,15 +270,16 @@ export const toolAgent = async ( tools, messages, logger, + workingDirectory, ); if (respawn) { - logger.info('Respawning agent with new context'); + logger.info("Respawning agent with new context"); // Reset messages to just the new context messages.length = 0; messages.push({ - role: 'user', - content: [{ type: 'text', text: respawn.context }], + role: "user", + content: [{ type: "text", text: respawn.context }], }); continue; } @@ -282,7 +288,7 @@ export const toolAgent = async ( const result = { result: completionResult ?? - 'Sequence explicitly completed with an empty result', + "Sequence explicitly completed with an empty result", tokens: { input: totalInputTokens, output: totalOutputTokens, @@ -296,9 +302,9 @@ export const toolAgent = async ( } } - logger.warn('Maximum iterations reached'); + logger.warn("Maximum iterations reached"); const result = { - result: 'Maximum sub-agent iterations reach without successful completion', + result: "Maximum sub-agent iterations reach without successful completion", tokens: { input: totalInputTokens, output: totalOutputTokens, diff --git a/packages/agent/src/core/toolContext.ts b/packages/agent/src/core/toolContext.ts new file mode 100644 index 0000000..b10da57 --- /dev/null +++ b/packages/agent/src/core/toolContext.ts @@ -0,0 +1,20 @@ +import { Logger } from "../utils/logger.js"; + +import { ToolContext } from "./types.js"; + +export function createToolContext( + logger: Logger, + workingDirectory?: string, +): ToolContext { + return { + logger, + workingDirectory, + }; +} + +export function loggerToToolContext( + logger: Logger, + workingDirectory?: string, +): ToolContext { + return createToolContext(logger, workingDirectory); +} diff --git a/src/core/types.ts b/packages/agent/src/core/types.ts similarity index 81% rename from src/core/types.ts rename to packages/agent/src/core/types.ts index 4832b6b..4e17e89 100644 --- a/src/core/types.ts +++ b/packages/agent/src/core/types.ts @@ -1,8 +1,10 @@ -import { JsonSchema7Type } from 'zod-to-json-schema'; -import { Logger } from '../utils/logger.js'; +import { JsonSchema7Type } from "zod-to-json-schema"; + +import { Logger } from "../utils/logger.js"; export type ToolContext = { logger: Logger; + workingDirectory?: string; }; export type Tool, TReturn = any> = { @@ -24,16 +26,16 @@ export type ToolCall = { }; export type TextContent = { - type: 'text'; + type: "text"; text: string; }; export type ToolUseContent = { - type: 'tool_use'; + type: "tool_use"; } & ToolCall; export type AssistantMessage = { - role: 'assistant'; + role: "assistant"; content: (TextContent | ToolUseContent)[]; tokenUsage?: { promptTokens: number; @@ -43,13 +45,13 @@ export type AssistantMessage = { }; export type ToolResultContent = { - type: 'tool_result'; + type: "tool_result"; tool_use_id: string; content: string; }; export type UserMessage = { - role: 'user'; + role: "user"; content: (TextContent | ToolResultContent)[]; }; diff --git a/packages/agent/src/index.ts b/packages/agent/src/index.ts new file mode 100644 index 0000000..cdd60a6 --- /dev/null +++ b/packages/agent/src/index.ts @@ -0,0 +1,39 @@ +// Tools - IO +export * from "./tools/io/readFile.js"; +export * from "./tools/io/updateFile.js"; +export * from "./tools/io/fetch.js"; + +// Tools - System +export * from "./tools/system/shellStart.js"; +export * from "./tools/system/sleep.js"; +export * from "./tools/system/respawn.js"; +export * from "./tools/system/sequenceComplete.js"; +export * from "./tools/system/shellMessage.js"; +export * from "./tools/system/shellExecute.js"; + +// Tools - Browser +export * from "./tools/browser/BrowserManager.js"; +export * from "./tools/browser/types.js"; +export * from "./tools/browser/browseMessage.js"; +export * from "./tools/browser/browseStart.js"; +export * from "./tools/browser/PageController.js"; +export * from "./tools/browser/BrowserAutomation.js"; + +// Tools - Interaction +export * from "./tools/interaction/subAgent.js"; +export * from "./tools/interaction/userPrompt.js"; + +// Core +export * from "./core/toolContext.js"; +export * from "./core/executeToolCall.js"; +export * from "./core/types.js"; +export * from "./core/toolAgent.js"; + +// Utils +export * from "./tools/getTools.js"; +export * from "./utils/errors.js"; +export * from "./utils/sleep.js"; +export * from "./utils/errorToString.js"; +export * from "./utils/logger.js"; +export * from "./utils/mockLogger.js"; +export * from "./utils/stringifyLimited.js"; diff --git a/src/tools/browser/BrowserAutomation.ts b/packages/agent/src/tools/browser/BrowserAutomation.ts similarity index 89% rename from src/tools/browser/BrowserAutomation.ts rename to packages/agent/src/tools/browser/BrowserAutomation.ts index 52f3b83..7190bd0 100644 --- a/src/tools/browser/BrowserAutomation.ts +++ b/packages/agent/src/tools/browser/BrowserAutomation.ts @@ -1,5 +1,5 @@ -import { BrowserManager } from './BrowserManager.js'; -import { PageController } from './PageController.js'; +import { BrowserManager } from "./BrowserManager.js"; +import { PageController } from "./PageController.js"; export class BrowserAutomation { private static instance: BrowserAutomation; diff --git a/src/tools/browser/BrowserManager.ts b/packages/agent/src/tools/browser/BrowserManager.ts similarity index 83% rename from src/tools/browser/BrowserManager.ts rename to packages/agent/src/tools/browser/BrowserManager.ts index 9d1fff4..32c2724 100644 --- a/src/tools/browser/BrowserManager.ts +++ b/packages/agent/src/tools/browser/BrowserManager.ts @@ -1,11 +1,12 @@ -import { chromium } from '@playwright/test'; -import { v4 as uuidv4 } from 'uuid'; +import { chromium } from "@playwright/test"; +import { v4 as uuidv4 } from "uuid"; + import { BrowserConfig, BrowserSession, BrowserError, BrowserErrorCode, -} from './types.js'; +} from "./types.js"; export class BrowserManager { private sessions: Map = new Map(); @@ -25,7 +26,7 @@ export class BrowserManager { const context = await browser.newContext({ viewport: null, userAgent: - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", }); const page = await context.newPage(); @@ -43,7 +44,7 @@ export class BrowserManager { return session; } catch (error) { throw new BrowserError( - 'Failed to create browser session', + "Failed to create browser session", BrowserErrorCode.LAUNCH_FAILED, error, ); @@ -54,7 +55,7 @@ export class BrowserManager { const session = this.sessions.get(sessionId); if (!session) { throw new BrowserError( - 'Session not found', + "Session not found", BrowserErrorCode.SESSION_ERROR, ); } @@ -66,7 +67,7 @@ export class BrowserManager { this.sessions.delete(sessionId); } catch (error) { throw new BrowserError( - 'Failed to close session', + "Failed to close session", BrowserErrorCode.SESSION_ERROR, error, ); @@ -75,17 +76,17 @@ export class BrowserManager { private setupCleanup(session: BrowserSession): void { // Handle browser disconnection - session.browser.on('disconnected', () => { + session.browser.on("disconnected", () => { this.sessions.delete(session.id); }); // Handle process exit - process.on('exit', () => { + process.on("exit", () => { this.closeSession(session.id).catch(() => {}); }); // Handle unexpected errors - process.on('uncaughtException', () => { + process.on("uncaughtException", () => { this.closeSession(session.id).catch(() => {}); }); } @@ -101,7 +102,7 @@ export class BrowserManager { const session = this.sessions.get(sessionId); if (!session) { throw new BrowserError( - 'Session not found', + "Session not found", BrowserErrorCode.SESSION_ERROR, ); } diff --git a/src/tools/browser/PageController.ts b/packages/agent/src/tools/browser/PageController.ts similarity index 91% rename from src/tools/browser/PageController.ts rename to packages/agent/src/tools/browser/PageController.ts index 5a894de..c921c1f 100644 --- a/src/tools/browser/PageController.ts +++ b/packages/agent/src/tools/browser/PageController.ts @@ -1,11 +1,13 @@ -import { Page } from '@playwright/test'; +import { Page } from "@playwright/test"; + +import { errorToString } from "../../utils/errorToString.js"; + import { SelectorType, SelectorOptions, BrowserError, BrowserErrorCode, -} from './types.js'; -import { errorToString } from '../../utils/errorToString.js'; +} from "./types.js"; export class PageController { constructor(private page: Page) {} @@ -31,7 +33,7 @@ export class PageController { private validateSelector(selector: string, _type: SelectorType): void { if (!selector) { throw new BrowserError( - 'Invalid selector: empty string', + "Invalid selector: empty string", BrowserErrorCode.SELECTOR_INVALID, ); } @@ -48,7 +50,7 @@ export class PageController { this.getSelector(selector, options.type), ); await locator.waitFor({ - state: options.visible ? 'visible' : 'attached', + state: options.visible ? "visible" : "attached", timeout: options.timeout, }); } catch (error) { @@ -105,7 +107,7 @@ export class PageController { const locator = this.page.locator( this.getSelector(selector, options.type), ); - return (await locator.textContent()) || ''; + return (await locator.textContent()) || ""; } catch (error) { throw new BrowserError( `Failed to get text: ${errorToString(error)}`, diff --git a/src/tools/browser/browseMessage.ts b/packages/agent/src/tools/browser/browseMessage.ts similarity index 69% rename from src/tools/browser/browseMessage.ts rename to packages/agent/src/tools/browser/browseMessage.ts index aa69bb3..ed14b50 100644 --- a/src/tools/browser/browseMessage.ts +++ b/packages/agent/src/tools/browser/browseMessage.ts @@ -1,29 +1,31 @@ -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; -import { browserSessions, type BrowserAction, SelectorType } from './types.js'; -import { errorToString } from '../../utils/errorToString.js'; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; +import { errorToString } from "../../utils/errorToString.js"; + +import { browserSessions, type BrowserAction, SelectorType } from "./types.js"; // Schema for browser action const browserActionSchema = z .object({ - type: z.enum(['goto', 'click', 'type', 'wait', 'content', 'close']), + type: z.enum(["goto", "click", "type", "wait", "content", "close"]), url: z.string().url().optional(), selector: z.string().optional(), selectorType: z.nativeEnum(SelectorType).optional(), text: z.string().optional(), options: z.object({}).optional(), }) - .describe('Browser action to perform'); + .describe("Browser action to perform"); // Main parameter schema const parameterSchema = z.object({ - instanceId: z.string().describe('The ID returned by browseStart'), + instanceId: z.string().describe("The ID returned by browseStart"), action: browserActionSchema, description: z .string() .max(80) - .describe('The reason for this browser action (max 80 chars)'), + .describe("The reason for this browser action (max 80 chars)"), }); // Return schema @@ -49,8 +51,8 @@ const getSelector = (selector: string, type?: SelectorType): string => { }; export const browseMessageTool: Tool = { - name: 'browseMessage', - description: 'Performs actions in an active browser session', + name: "browseMessage", + description: "Performs actions in an active browser session", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), @@ -66,19 +68,19 @@ export const browseMessageTool: Tool = { const { page } = session; switch (action.type) { - case 'goto': { + case "goto": { if (!action.url) { - throw new Error('URL required for goto action'); + throw new Error("URL required for goto action"); } - await page.goto(action.url, { waitUntil: 'networkidle' }); + await page.goto(action.url, { waitUntil: "networkidle" }); const content = await page.content(); - logger.verbose('Navigation completed successfully'); - return { status: 'success', content }; + logger.verbose("Navigation completed successfully"); + return { status: "success", content }; } - case 'click': { + case "click": { if (!action.selector) { - throw new Error('Selector required for click action'); + throw new Error("Selector required for click action"); } const clickSelector = getSelector( action.selector, @@ -89,12 +91,12 @@ export const browseMessageTool: Tool = { logger.verbose( `Click action completed on selector: ${clickSelector}`, ); - return { status: 'success', content }; + return { status: "success", content }; } - case 'type': { + case "type": { if (!action.selector || !action.text) { - throw new Error('Selector and text required for type action'); + throw new Error("Selector and text required for type action"); } const typeSelector = getSelector( action.selector, @@ -102,12 +104,12 @@ export const browseMessageTool: Tool = { ); await page.fill(typeSelector, action.text); logger.verbose(`Type action completed on selector: ${typeSelector}`); - return { status: 'success' }; + return { status: "success" }; } - case 'wait': { + case "wait": { if (!action.selector) { - throw new Error('Selector required for wait action'); + throw new Error("Selector required for wait action"); } const waitSelector = getSelector( action.selector, @@ -115,21 +117,21 @@ export const browseMessageTool: Tool = { ); await page.waitForSelector(waitSelector); logger.verbose(`Wait action completed for selector: ${waitSelector}`); - return { status: 'success' }; + return { status: "success" }; } - case 'content': { + case "content": { const content = await page.content(); - logger.verbose('Page content retrieved successfully'); - return { status: 'success', content }; + logger.verbose("Page content retrieved successfully"); + return { status: "success", content }; } - case 'close': { + case "close": { await session.page.context().close(); await session.browser.close(); browserSessions.delete(instanceId); - logger.verbose('Browser session closed successfully'); - return { status: 'closed' }; + logger.verbose("Browser session closed successfully"); + return { status: "closed" }; } default: { @@ -139,9 +141,9 @@ export const browseMessageTool: Tool = { } } } catch (error) { - logger.error('Browser action failed:', { error }); + logger.error("Browser action failed:", { error }); return { - status: 'error', + status: "error", error: errorToString(error), }; } diff --git a/src/tools/browser/browseStart.ts b/packages/agent/src/tools/browser/browseStart.ts similarity index 64% rename from src/tools/browser/browseStart.ts rename to packages/agent/src/tools/browser/browseStart.ts index 5925678..9166c05 100644 --- a/src/tools/browser/browseStart.ts +++ b/packages/agent/src/tools/browser/browseStart.ts @@ -1,25 +1,27 @@ -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; -import { v4 as uuidv4 } from 'uuid'; -import { chromium } from '@playwright/test'; -import { browserSessions } from './types.js'; -import { errorToString } from '../../utils/errorToString.js'; +import { chromium } from "@playwright/test"; +import { v4 as uuidv4 } from "uuid"; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; +import { errorToString } from "../../utils/errorToString.js"; + +import { browserSessions } from "./types.js"; const parameterSchema = z.object({ - url: z.string().url().optional().describe('Initial URL to navigate to'), + url: z.string().url().optional().describe("Initial URL to navigate to"), headless: z .boolean() .optional() - .describe('Run browser in headless mode (default: true)'), + .describe("Run browser in headless mode (default: true)"), timeout: z .number() .optional() - .describe('Default timeout in milliseconds (default: 30000)'), + .describe("Default timeout in milliseconds (default: 30000)"), description: z .string() .max(80) - .describe('The reason for starting this browser session (max 80 chars)'), + .describe("The reason for starting this browser session (max 80 chars)"), }); const returnSchema = z.object({ @@ -33,8 +35,8 @@ type Parameters = z.infer; type ReturnType = z.infer; export const browseStartTool: Tool = { - name: 'browseStart', - description: 'Starts a new browser session with optional initial URL', + name: "browseStart", + description: "Starts a new browser session with optional initial URL", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), @@ -42,7 +44,7 @@ export const browseStartTool: Tool = { { url, headless = true, timeout = 30000 }, { logger }, ): Promise => { - logger.verbose(`Starting browser session${url ? ` at ${url}` : ''}`); + logger.verbose(`Starting browser session${url ? ` at ${url}` : ""}`); try { const instanceId = uuidv4(); @@ -56,7 +58,7 @@ export const browseStartTool: Tool = { const context = await browser.newContext({ viewport: null, userAgent: - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", }); // Create new page @@ -73,29 +75,29 @@ export const browseStartTool: Tool = { browserSessions.set(instanceId, session); // Setup cleanup handlers - browser.on('disconnected', () => { + browser.on("disconnected", () => { browserSessions.delete(instanceId); }); // Navigate to URL if provided - let content = ''; + let content = ""; if (url) { - await page.goto(url, { waitUntil: 'networkidle' }); + await page.goto(url, { waitUntil: "networkidle" }); content = await page.content(); } - logger.verbose('Browser session started successfully'); + logger.verbose("Browser session started successfully"); return { instanceId, - status: 'initialized', + status: "initialized", content: content || undefined, }; } catch (error) { logger.error(`Failed to start browser: ${errorToString(error)}`); return { - instanceId: '', - status: 'error', + instanceId: "", + status: "error", error: errorToString(error), }; } @@ -103,7 +105,7 @@ export const browseStartTool: Tool = { logParameters: ({ url, description }, { logger }) => { logger.info( - `Starting browser session${url ? ` at ${url}` : ''}, ${description}`, + `Starting browser session${url ? ` at ${url}` : ""}, ${description}`, ); }, diff --git a/src/tools/browser/browser-manager.test.ts b/packages/agent/src/tools/browser/browser-manager.test.ts similarity index 56% rename from src/tools/browser/browser-manager.test.ts rename to packages/agent/src/tools/browser/browser-manager.test.ts index cc6fe3f..975be07 100644 --- a/src/tools/browser/browser-manager.test.ts +++ b/packages/agent/src/tools/browser/browser-manager.test.ts @@ -1,8 +1,9 @@ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { BrowserManager } from './BrowserManager.js'; -import { BrowserError, BrowserErrorCode } from './types.js'; +import { describe, it, expect, beforeEach, afterEach } from "vitest"; -describe('BrowserManager', () => { +import { BrowserManager } from "./BrowserManager.js"; +import { BrowserError, BrowserErrorCode } from "./types.js"; + +describe("BrowserManager", () => { let browserManager: BrowserManager; beforeEach(() => { @@ -13,38 +14,38 @@ describe('BrowserManager', () => { await browserManager.closeAllSessions(); }); - describe('createSession', () => { - it('should create a new browser session', async () => { + describe("createSession", () => { + it("should create a new browser session", async () => { const session = await browserManager.createSession(); expect(session.id).toBeDefined(); expect(session.browser).toBeDefined(); expect(session.page).toBeDefined(); }); - it('should create a headless session when specified', async () => { + it("should create a headless session when specified", async () => { const session = await browserManager.createSession({ headless: true }); expect(session.id).toBeDefined(); }); - it('should apply custom timeout when specified', async () => { + it("should apply custom timeout when specified", async () => { const customTimeout = 500; const session = await browserManager.createSession({ defaultTimeout: customTimeout, }); // Verify timeout by attempting to wait for a non-existent element try { - await session.page.waitForSelector('#nonexistent', { + await session.page.waitForSelector("#nonexistent", { timeout: customTimeout - 100, }); } catch (error: any) { - expect(error.message).toContain('imeout'); + expect(error.message).toContain("imeout"); expect(error.message).toContain(`${customTimeout - 100}`); } }); }); - describe('closeSession', () => { - it('should close an existing session', async () => { + describe("closeSession", () => { + it("should close an existing session", async () => { const session = await browserManager.createSession(); await browserManager.closeSession(session.id); @@ -53,25 +54,25 @@ describe('BrowserManager', () => { }).toThrow(BrowserError); }); - it('should throw error when closing non-existent session', async () => { - await expect(browserManager.closeSession('invalid-id')).rejects.toThrow( - new BrowserError('Session not found', BrowserErrorCode.SESSION_ERROR), + it("should throw error when closing non-existent session", async () => { + await expect(browserManager.closeSession("invalid-id")).rejects.toThrow( + new BrowserError("Session not found", BrowserErrorCode.SESSION_ERROR), ); }); }); - describe('getSession', () => { - it('should return existing session', async () => { + describe("getSession", () => { + it("should return existing session", async () => { const session = await browserManager.createSession(); const retrieved = browserManager.getSession(session.id); expect(retrieved).toBe(session); }); - it('should throw error for non-existent session', () => { + it("should throw error for non-existent session", () => { expect(() => { - browserManager.getSession('invalid-id'); + browserManager.getSession("invalid-id"); }).toThrow( - new BrowserError('Session not found', BrowserErrorCode.SESSION_ERROR), + new BrowserError("Session not found", BrowserErrorCode.SESSION_ERROR), ); }); }); diff --git a/src/tools/browser/element-state.test.ts b/packages/agent/src/tools/browser/element-state.test.ts similarity index 74% rename from src/tools/browser/element-state.test.ts rename to packages/agent/src/tools/browser/element-state.test.ts index d4f26ac..f9b6022 100644 --- a/src/tools/browser/element-state.test.ts +++ b/packages/agent/src/tools/browser/element-state.test.ts @@ -1,11 +1,23 @@ -import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'; -import { BrowserManager } from './BrowserManager.js'; -import { BrowserSession } from './types.js'; - -describe('Element State Tests', () => { +import { + describe, + it, + expect, + beforeAll, + afterAll, + beforeEach, + vi, +} from "vitest"; + +import { BrowserManager } from "./BrowserManager.js"; +import { BrowserSession } from "./types.js"; + +// Set global timeout for all tests in this file +vi.setConfig({ testTimeout: 15000 }); + +describe("Element State Tests", () => { let browserManager: BrowserManager; let session: BrowserSession; - const baseUrl = 'https://the-internet.herokuapp.com'; + const baseUrl = "https://the-internet.herokuapp.com"; beforeAll(async () => { browserManager = new BrowserManager(); @@ -16,12 +28,12 @@ describe('Element State Tests', () => { await browserManager.closeAllSessions(); }); - describe('Checkbox Tests', () => { + describe("Checkbox Tests", () => { beforeEach(async () => { await session.page.goto(`${baseUrl}/checkboxes`); }); - it('should verify initial checkbox states', async () => { + it("should verify initial checkbox states", async () => { const checkboxes = await session.page.$$('input[type="checkbox"]'); expect(checkboxes).toHaveLength(2); @@ -37,10 +49,10 @@ describe('Element State Tests', () => { expect(initialStates[1]).toBe(true); }); - it('should toggle checkbox states', async () => { + it("should toggle checkbox states", async () => { const checkboxes = await session.page.$$('input[type="checkbox"]'); if (!checkboxes[0] || !checkboxes[1]) - throw new Error('Checkboxes not found'); + throw new Error("Checkboxes not found"); // Toggle first checkbox await checkboxes[0].click(); @@ -57,9 +69,9 @@ describe('Element State Tests', () => { expect(secondState).toBe(false); }); - it('should maintain checkbox states after page refresh', async () => { + it("should maintain checkbox states after page refresh", async () => { const checkboxes = await session.page.$$('input[type="checkbox"]'); - if (!checkboxes[0]) throw new Error('First checkbox not found'); + if (!checkboxes[0]) throw new Error("First checkbox not found"); await checkboxes[0].click(); // Toggle first checkbox await session.page.reload(); @@ -79,12 +91,12 @@ describe('Element State Tests', () => { }); }); - describe('Dynamic Controls Tests', () => { + describe("Dynamic Controls Tests", () => { beforeEach(async () => { await session.page.goto(`${baseUrl}/dynamic_controls`); }); - it('should handle enabled/disabled element states', async () => { + it("should handle enabled/disabled element states", async () => { // Wait for the input to be present and verify initial disabled state await session.page.waitForSelector('input[type="text"][disabled]'); @@ -92,8 +104,8 @@ describe('Element State Tests', () => { await session.page.click('button:has-text("Enable")'); // Wait for the message indicating the input is enabled - await session.page.waitForSelector('#message', { - state: 'visible', + await session.page.waitForSelector("#message", { + state: "visible", timeout: 5000, }); @@ -101,12 +113,12 @@ describe('Element State Tests', () => { const input = await session.page.waitForSelector( 'input[type="text"]:not([disabled])', { - state: 'visible', + state: "visible", timeout: 5000, }, ); - if (!input) throw new Error('Enabled input not found'); + if (!input) throw new Error("Enabled input not found"); const isEnabled = await input.evaluate( (el) => !(el as HTMLInputElement).disabled, diff --git a/packages/agent/src/tools/browser/form-interaction.test.ts b/packages/agent/src/tools/browser/form-interaction.test.ts new file mode 100644 index 0000000..04868f0 --- /dev/null +++ b/packages/agent/src/tools/browser/form-interaction.test.ts @@ -0,0 +1,100 @@ +import { + describe, + it, + expect, + beforeAll, + afterAll, + beforeEach, + vi, +} from "vitest"; + +import { BrowserManager } from "./BrowserManager.js"; +import { BrowserSession } from "./types.js"; + +// Set global timeout for all tests in this file +vi.setConfig({ testTimeout: 15000 }); + +describe("Form Interaction Tests", () => { + let browserManager: BrowserManager; + let session: BrowserSession; + const baseUrl = "https://the-internet.herokuapp.com"; + + beforeAll(async () => { + browserManager = new BrowserManager(); + session = await browserManager.createSession({ headless: true }); + }); + + afterAll(async () => { + await browserManager.closeAllSessions(); + }); + + beforeEach(async () => { + await session.page.goto(`${baseUrl}/login`); + }); + + it("should handle login form with invalid credentials", async () => { + await session.page.type("#username", "invalid_user"); + await session.page.type("#password", "invalid_pass"); + await session.page.click('button[type="submit"]'); + + const flashMessage = await session.page.waitForSelector("#flash"); + const messageText = await flashMessage?.evaluate((el) => el.textContent); + expect(messageText).toContain("Your username is invalid!"); + }); + + it("should clear form fields between attempts", async () => { + await session.page.type("#username", "test_user"); + await session.page.type("#password", "test_pass"); + + // Clear fields + await session.page.$eval( + "#username", + (el) => ((el as HTMLInputElement).value = ""), + ); + await session.page.$eval( + "#password", + (el) => ((el as HTMLInputElement).value = ""), + ); + + // Verify fields are empty + const username = await session.page.$eval( + "#username", + (el) => (el as HTMLInputElement).value, + ); + const password = await session.page.$eval( + "#password", + (el) => (el as HTMLInputElement).value, + ); + expect(username).toBe(""); + expect(password).toBe(""); + }); + + it("should maintain form state after page refresh", async () => { + const testUsername = "persistence_test"; + await session.page.type("#username", testUsername); + await session.page.reload(); + + // Form should be cleared after refresh + const username = await session.page.$eval( + "#username", + (el) => (el as HTMLInputElement).value, + ); + expect(username).toBe(""); + }); + + describe("Content Extraction", () => { + it("should extract form labels and placeholders", async () => { + const usernameLabel = await session.page.$eval( + 'label[for="username"]', + (el) => el.textContent, + ); + expect(usernameLabel).toBe("Username"); + + const passwordPlaceholder = await session.page.$eval( + "#password", + (el) => (el as HTMLInputElement).placeholder, + ); + expect(passwordPlaceholder).toBe(""); + }); + }); +}); diff --git a/packages/agent/src/tools/browser/navigation.test.ts b/packages/agent/src/tools/browser/navigation.test.ts new file mode 100644 index 0000000..9690a5f --- /dev/null +++ b/packages/agent/src/tools/browser/navigation.test.ts @@ -0,0 +1,67 @@ +import { describe, it, expect, beforeAll, afterAll, vi } from "vitest"; + +import { BrowserManager } from "./BrowserManager.js"; +import { BrowserSession } from "./types.js"; + +// Set global timeout for all tests in this file +vi.setConfig({ testTimeout: 15000 }); + +describe("Browser Navigation Tests", () => { + let browserManager: BrowserManager; + let session: BrowserSession; + const baseUrl = "https://the-internet.herokuapp.com"; + + beforeAll(async () => { + browserManager = new BrowserManager(); + session = await browserManager.createSession({ headless: true }); + }); + + afterAll(async () => { + await browserManager.closeAllSessions(); + }); + + it("should navigate to main page and verify content", async () => { + await session.page.goto(baseUrl); + const title = await session.page.title(); + expect(title).toBe("The Internet"); + + const headerText = await session.page.$eval( + "h1.heading", + (el) => el.textContent, + ); + expect(headerText).toBe("Welcome to the-internet"); + }); + + it("should navigate to login page and verify title", async () => { + await session.page.goto(`${baseUrl}/login`); + const title = await session.page.title(); + expect(title).toBe("The Internet"); + + const headerText = await session.page.$eval("h2", (el) => el.textContent); + expect(headerText).toBe("Login Page"); + }); + + it("should handle 404 pages appropriately", async () => { + await session.page.goto(`${baseUrl}/nonexistent`); + + // Wait for the page to stabilize + await session.page.waitForLoadState("networkidle"); + + // Check for 404 content instead of title since title may vary + const bodyText = await session.page.$eval("body", (el) => el.textContent); + expect(bodyText).toContain("Not Found"); + }); + + it("should handle navigation timeouts", async () => { + await expect( + session.page.goto(`${baseUrl}/slow`, { timeout: 1 }), + ).rejects.toThrow(); + }); + + it("should wait for network idle", async () => { + await session.page.goto(baseUrl, { + waitUntil: "networkidle", + }); + expect(session.page.url()).toBe(`${baseUrl}/`); + }); +}); diff --git a/src/tools/browser/types.ts b/packages/agent/src/tools/browser/types.ts similarity index 57% rename from src/tools/browser/types.ts rename to packages/agent/src/tools/browser/types.ts index 22ab62c..c1b4379 100644 --- a/src/tools/browser/types.ts +++ b/packages/agent/src/tools/browser/types.ts @@ -1,4 +1,4 @@ -import type { Browser, Page } from '@playwright/test'; +import type { Browser, Page } from "@playwright/test"; // Browser configuration export interface BrowserConfig { @@ -15,14 +15,14 @@ export interface BrowserSession { // Browser error codes export enum BrowserErrorCode { - LAUNCH_FAILED = 'LAUNCH_FAILED', - NAVIGATION_FAILED = 'NAVIGATION_FAILED', - SESSION_ERROR = 'SESSION_ERROR', - SELECTOR_ERROR = 'SELECTOR_ERROR', - TIMEOUT = 'TIMEOUT', - UNKNOWN = 'UNKNOWN', - SELECTOR_INVALID = 'SELECTOR_INVALID', - ELEMENT_NOT_FOUND = 'ELEMENT_NOT_FOUND', + LAUNCH_FAILED = "LAUNCH_FAILED", + NAVIGATION_FAILED = "NAVIGATION_FAILED", + SESSION_ERROR = "SESSION_ERROR", + SELECTOR_ERROR = "SELECTOR_ERROR", + TIMEOUT = "TIMEOUT", + UNKNOWN = "UNKNOWN", + SELECTOR_INVALID = "SELECTOR_INVALID", + ELEMENT_NOT_FOUND = "ELEMENT_NOT_FOUND", } // Browser error class @@ -33,17 +33,17 @@ export class BrowserError extends Error { public cause?: unknown, ) { super(message); - this.name = 'BrowserError'; + this.name = "BrowserError"; } } // Selector types for element interaction export enum SelectorType { - CSS = 'css', - XPATH = 'xpath', - TEXT = 'text', - ROLE = 'role', - TESTID = 'testid', + CSS = "css", + XPATH = "xpath", + TEXT = "text", + ROLE = "role", + TESTID = "testid", } // Selector options @@ -58,14 +58,14 @@ export const browserSessions: Map = new Map(); // Browser action types export type BrowserAction = - | { type: 'goto'; url: string } - | { type: 'click'; selector: string; selectorType?: SelectorType } + | { type: "goto"; url: string } + | { type: "click"; selector: string; selectorType?: SelectorType } | { - type: 'type'; + type: "type"; selector: string; text: string; selectorType?: SelectorType; } - | { type: 'wait'; selector: string; selectorType?: SelectorType } - | { type: 'content' } - | { type: 'close' }; + | { type: "wait"; selector: string; selectorType?: SelectorType } + | { type: "content" } + | { type: "close" }; diff --git a/src/tools/browser/wait-behavior.test.ts b/packages/agent/src/tools/browser/wait-behavior.test.ts similarity index 50% rename from src/tools/browser/wait-behavior.test.ts rename to packages/agent/src/tools/browser/wait-behavior.test.ts index 25142ae..c95efd4 100644 --- a/src/tools/browser/wait-behavior.test.ts +++ b/packages/agent/src/tools/browser/wait-behavior.test.ts @@ -6,14 +6,18 @@ import { afterAll, beforeEach, vi, -} from 'vitest'; -import { BrowserManager } from './BrowserManager.js'; -import { BrowserSession } from './types.js'; +} from "vitest"; -describe('Wait Behavior Tests', () => { +import { BrowserManager } from "./BrowserManager.js"; +import { BrowserSession } from "./types.js"; + +// Set global timeout for all tests in this file +vi.setConfig({ testTimeout: 15000 }); + +describe("Wait Behavior Tests", () => { let browserManager: BrowserManager; let session: BrowserSession; - const baseUrl = 'https://the-internet.herokuapp.com'; + const baseUrl = "https://the-internet.herokuapp.com"; beforeAll(async () => { browserManager = new BrowserManager(); @@ -24,69 +28,65 @@ describe('Wait Behavior Tests', () => { await browserManager.closeAllSessions(); }); - describe('Dynamic Loading Tests', () => { + describe("Dynamic Loading Tests", () => { beforeEach(async () => { await session.page.goto(`${baseUrl}/dynamic_loading/2`); }); - it('should handle dynamic loading with explicit waits', async () => { - // Set longer timeout for this test - vi.setConfig({ testTimeout: 10000 }); - await session.page.click('button'); + it("should handle dynamic loading with explicit waits", async () => { + await session.page.click("button"); // Wait for loading element to appear and then disappear - await session.page.waitForSelector('#loading'); - await session.page.waitForSelector('#loading', { state: 'hidden' }); + await session.page.waitForSelector("#loading"); + await session.page.waitForSelector("#loading", { state: "hidden" }); - const finishElement = await session.page.waitForSelector('#finish'); + const finishElement = await session.page.waitForSelector("#finish"); const finishText = await finishElement?.evaluate((el) => el.textContent); - expect(finishText).toBe('Hello World!'); + expect(finishText).toBe("Hello World!"); }); - it('should timeout on excessive wait times', async () => { - await session.page.click('button'); + it("should timeout on excessive wait times", async () => { + await session.page.click("button"); // Attempt to find a non-existent element with short timeout try { - await session.page.waitForSelector('#nonexistent', { timeout: 1000 }); + await session.page.waitForSelector("#nonexistent", { timeout: 1000 }); expect(true).toBe(false); // Should not reach here } catch (error: any) { - expect(error.message).toContain('Timeout'); + expect(error.message).toContain("Timeout"); } }); }); - describe('Dynamic Controls Tests', () => { + describe("Dynamic Controls Tests", () => { beforeEach(async () => { await session.page.goto(`${baseUrl}/dynamic_controls`); }); - it('should wait for element state changes', async () => { + it("should wait for element state changes", async () => { // Click remove button await session.page.click('button:has-text("Remove")'); // Wait for checkbox to be removed - await session.page.waitForSelector('#checkbox', { state: 'hidden' }); + await session.page.waitForSelector("#checkbox", { state: "hidden" }); // Verify gone message - const message = await session.page.waitForSelector('#message'); + const message = await session.page.waitForSelector("#message"); const messageText = await message?.evaluate((el) => el.textContent); expect(messageText).toContain("It's gone!"); }); - it('should handle multiple sequential dynamic changes', async () => { - // Set longer timeout for this test - vi.setConfig({ testTimeout: 10000 }); + it("should handle multiple sequential dynamic changes", async () => { // Remove checkbox await session.page.click('button:has-text("Remove")'); - await session.page.waitForSelector('#checkbox', { state: 'hidden' }); + await session.page.waitForSelector("#checkbox", { state: "hidden" }); // Add checkbox back await session.page.click('button:has-text("Add")'); - await session.page.waitForSelector('#checkbox'); + await session.page.waitForSelector("#checkbox"); // Verify checkbox is present - const checkbox = await session.page.$('#checkbox'); + const checkbox = await session.page.$("#checkbox"); expect(checkbox).toBeTruthy(); }); }); diff --git a/src/tools/getTools.test.ts b/packages/agent/src/tools/getTools.test.ts similarity index 67% rename from src/tools/getTools.test.ts rename to packages/agent/src/tools/getTools.test.ts index 6ef1156..6ab465b 100644 --- a/src/tools/getTools.test.ts +++ b/packages/agent/src/tools/getTools.test.ts @@ -1,14 +1,15 @@ -import { describe, it, expect } from 'vitest'; -import { getTools } from './getTools.js'; +import { describe, it, expect } from "vitest"; -describe('getTools', () => { - it('should return a successful result with tools', () => { +import { getTools } from "./getTools.js"; + +describe("getTools", () => { + it("should return a successful result with tools", () => { const tools = getTools(); expect(tools).toBeInstanceOf(Array); expect(tools.length).toBeGreaterThanOrEqual(5); // At least core tools }); - it('should include core tools', () => { + it("should include core tools", () => { const tools = getTools(); const toolNames = tools.map((tool) => tool.name); @@ -16,7 +17,7 @@ describe('getTools', () => { expect(toolNames.length).greaterThan(0); }); - it('should have unique tool names', () => { + it("should have unique tool names", () => { const tools = getTools(); const toolNames = tools.map((tool) => tool.name); const uniqueNames = new Set(toolNames); @@ -24,7 +25,7 @@ describe('getTools', () => { expect(toolNames).toHaveLength(uniqueNames.size); }); - it('should have valid schema for each tool', () => { + it("should have valid schema for each tool", () => { const tools = getTools(); for (const tool of tools) { @@ -38,11 +39,11 @@ describe('getTools', () => { } }); - it('should have executable functions', () => { + it("should have executable functions", () => { const tools = getTools(); for (const tool of tools) { - expect(tool.execute).toBeTypeOf('function'); + expect(tool.execute).toBeTypeOf("function"); } }); }); diff --git a/packages/agent/src/tools/getTools.ts b/packages/agent/src/tools/getTools.ts new file mode 100644 index 0000000..479f411 --- /dev/null +++ b/packages/agent/src/tools/getTools.ts @@ -0,0 +1,31 @@ +import { Tool } from "../core/types.js"; + +import { browseMessageTool } from "./browser/browseMessage.js"; +import { browseStartTool } from "./browser/browseStart.js"; +import { subAgentTool } from "./interaction/subAgent.js"; +import { userPromptTool } from "./interaction/userPrompt.js"; +import { fetchTool } from "./io/fetch.js"; +import { readFileTool } from "./io/readFile.js"; +import { updateFileTool } from "./io/updateFile.js"; +import { respawnTool } from "./system/respawn.js"; +import { sequenceCompleteTool } from "./system/sequenceComplete.js"; +import { shellMessageTool } from "./system/shellMessage.js"; +import { shellStartTool } from "./system/shellStart.js"; +import { sleepTool } from "./system/sleep.js"; + +export function getTools(): Tool[] { + return [ + subAgentTool, + readFileTool, + updateFileTool, + userPromptTool, + sequenceCompleteTool, + fetchTool, + shellStartTool, + shellMessageTool, + browseStartTool, + browseMessageTool, + respawnTool, + sleepTool, + ] as Tool[]; +} diff --git a/packages/agent/src/tools/interaction/subAgent.ts b/packages/agent/src/tools/interaction/subAgent.ts new file mode 100644 index 0000000..62415dc --- /dev/null +++ b/packages/agent/src/tools/interaction/subAgent.ts @@ -0,0 +1,126 @@ +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { toolAgent } from "../../core/toolAgent.js"; +import { Tool } from "../../core/types.js"; +import { getTools } from "../getTools.js"; + +const parameterSchema = z.object({ + description: z + .string() + .max(80) + .describe("A brief description of the sub-agent's purpose (max 80 chars)"), + goal: z + .string() + .describe("The main objective that the sub-agent needs to achieve"), + requirements: z + .array(z.string()) + .optional() + .describe("Specific requirements or constraints that must be met"), + context: z + .object({ + workingDirectory: z + .string() + .optional() + .describe("The directory where the sub-agent should operate"), + relevantFiles: z + .array(z.string()) + .optional() + .describe("List of files that are relevant to the task"), + projectContext: z + .string() + .optional() + .describe("Additional context about the project or environment"), + }) + .optional(), + successCriteria: z + .array(z.string()) + .optional() + .describe("Specific criteria that indicate successful completion"), + suggestedApproach: z + .string() + .optional() + .describe("Optional guidance on how to approach the task"), +}); + +const returnSchema = z + .string() + .describe( + "The response from the sub-agent including its reasoning and tool usage", + ); + +type Parameters = z.infer; +type ReturnType = z.infer; + +// Sub-agent specific configuration +const subAgentConfig = { + maxIterations: 50, + model: process.env.AGENT_MODEL || "claude-3-opus-20240229", + maxTokens: 4096, + temperature: 0.7, + getSystemPrompt: () => { + return [ + "You are a focused AI sub-agent handling a specific task.", + "You have access to the same tools as the main agent but should focus only on your assigned task.", + "When complete, call the sequenceComplete tool with your results.", + "Follow any specific conventions or requirements provided in the task context.", + "Ask the main agent for clarification if critical information is missing.", + ].join("\n"); + }, +}; + +export const subAgentTool: Tool = { + name: "subAgent", + description: + "Creates a sub-agent that has access to all tools to solve a specific task", + parameters: zodToJsonSchema(parameterSchema), + returns: zodToJsonSchema(returnSchema), + execute: async (params, { logger }) => { + // Validate parameters + const { goal, requirements, context, successCriteria, suggestedApproach } = + parameterSchema.parse(params); + + // Construct a well-structured prompt + const prompt = [ + `Goal: ${goal}`, + requirements?.length + ? `\nRequirements:\n${requirements.map((r) => `- ${r}`).join("\n")}` + : "", + context + ? `\nContext:\n${[ + context.workingDirectory + ? `- Working Directory: ${context.workingDirectory}` + : "", + context.relevantFiles?.length + ? `- Relevant Files:\n ${context.relevantFiles.map((f) => `- ${f}`).join("\n ")}` + : "", + context.projectContext + ? `- Project Context: ${context.projectContext}` + : "", + ] + .filter(Boolean) + .join("\n")}` + : "", + successCriteria?.length + ? `\nSuccess Criteria:\n${successCriteria.map((c) => `- ${c}`).join("\n")}` + : "", + suggestedApproach ? `\nSuggested Approach: ${suggestedApproach}` : "", + ] + .filter(Boolean) + .join("\n"); + + const tools = getTools().filter((tool) => tool.name !== "userPrompt"); + + // Update config if timeout is specified + const config = { + ...subAgentConfig, + }; + + const result = await toolAgent(prompt, tools, logger, config); + return result.result; // Return the result string directly + }, + logParameters: (input, { logger }) => { + logger.info(`Delegating task "${input.description}"`); + }, + logReturns: () => {}, +}; diff --git a/src/tools/interaction/userPrompt.ts b/packages/agent/src/tools/interaction/userPrompt.ts similarity index 70% rename from src/tools/interaction/userPrompt.ts rename to packages/agent/src/tools/interaction/userPrompt.ts index 9961a25..4542951 100644 --- a/src/tools/interaction/userPrompt.ts +++ b/packages/agent/src/tools/interaction/userPrompt.ts @@ -1,11 +1,13 @@ -import { Tool } from '../../core/types.js'; -import * as readline from 'readline'; -import chalk from 'chalk'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; +import * as readline from "readline"; + +import chalk from "chalk"; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; const parameterSchema = z.object({ - prompt: z.string().describe('The prompt message to display to the user'), + prompt: z.string().describe("The prompt message to display to the user"), }); const returnSchema = z.string().describe("The user's response"); @@ -14,8 +16,8 @@ type Parameters = z.infer; type ReturnType = z.infer; export const userPromptTool: Tool = { - name: 'userPrompt', - description: 'Prompts the user for input and returns their response', + name: "userPrompt", + description: "Prompts the user for input and returns their response", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), execute: async ({ prompt }, { logger }) => { @@ -33,7 +35,7 @@ export const userPromptTool: Tool = { } const response = await new Promise((resolve) => { - rl.question(chalk.green(prompt + ' '), (answer) => { + rl.question(chalk.green(prompt + " "), (answer) => { resolve(answer); }); }); diff --git a/src/tools/io/fetch.ts b/packages/agent/src/tools/io/fetch.ts similarity index 60% rename from src/tools/io/fetch.ts rename to packages/agent/src/tools/io/fetch.ts index 9082d16..538589e 100644 --- a/src/tools/io/fetch.ts +++ b/packages/agent/src/tools/io/fetch.ts @@ -1,23 +1,24 @@ -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; const parameterSchema = z.object({ method: z .string() .describe( - 'HTTP method to use (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)', + "HTTP method to use (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)", ), - url: z.string().describe('URL to make the request to'), + url: z.string().describe("URL to make the request to"), params: z .record(z.any()) .optional() - .describe('Optional query parameters to append to the URL'), + .describe("Optional query parameters to append to the URL"), body: z .record(z.any()) .optional() - .describe('Optional request body (for POST, PUT, PATCH requests)'), - headers: z.record(z.string()).optional().describe('Optional request headers'), + .describe("Optional request body (for POST, PUT, PATCH requests)"), + headers: z.record(z.string()).optional().describe("Optional request headers"), }); const returnSchema = z @@ -27,15 +28,15 @@ const returnSchema = z headers: z.record(z.string()), body: z.union([z.string(), z.record(z.any())]), }) - .describe('HTTP response including status, headers, and body'); + .describe("HTTP response including status, headers, and body"); type Parameters = z.infer; type ReturnType = z.infer; export const fetchTool: Tool = { - name: 'fetch', + name: "fetch", description: - 'Executes HTTP requests using native Node.js fetch API, for using APIs, not for browsing the web.', + "Executes HTTP requests using native Node.js fetch API, for using APIs, not for browsing the web.", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), execute: async ( @@ -47,7 +48,7 @@ export const fetchTool: Tool = { // Add query parameters if (params) { - logger.verbose('Adding query parameters:', params); + logger.verbose("Adding query parameters:", params); Object.entries(params).forEach(([key, value]) => urlObj.searchParams.append(key, value as string), ); @@ -58,41 +59,41 @@ export const fetchTool: Tool = { method, headers: { ...(body && - !['GET', 'HEAD'].includes(method) && { - 'content-type': 'application/json', + !["GET", "HEAD"].includes(method) && { + "content-type": "application/json", }), ...headers, }, ...(body && - !['GET', 'HEAD'].includes(method) && { + !["GET", "HEAD"].includes(method) && { body: JSON.stringify(body), }), }; - logger.verbose('Request options:', options); + logger.verbose("Request options:", options); const response = await fetch(urlObj.toString(), options); logger.verbose( `Request completed with status ${response.status} ${response.statusText}`, ); - const contentType = response.headers.get('content-type'); - const responseBody = contentType?.includes('application/json') + const contentType = response.headers.get("content-type"); + const responseBody = contentType?.includes("application/json") ? await response.json() : await response.text(); - logger.verbose('Response content-type:', contentType); + logger.verbose("Response content-type:", contentType); return { status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers), - body: responseBody as ReturnType['body'], + body: responseBody as ReturnType["body"], }; }, logParameters(params, { logger }) { const { method, url, params: queryParams } = params; logger.info( - `${method} ${url}${queryParams ? `?${new URLSearchParams(queryParams).toString()}` : ''}`, + `${method} ${url}${queryParams ? `?${new URLSearchParams(queryParams).toString()}` : ""}`, ); }, }; diff --git a/packages/agent/src/tools/io/readFile.test.ts b/packages/agent/src/tools/io/readFile.test.ts new file mode 100644 index 0000000..3dd3776 --- /dev/null +++ b/packages/agent/src/tools/io/readFile.test.ts @@ -0,0 +1,29 @@ +import { describe, it, expect } from "vitest"; + +import { MockLogger } from "../../utils/mockLogger.js"; + +import { readFileTool } from "./readFile.js"; + +const logger = new MockLogger(); + +describe("readFile", () => { + it("should read a file", async () => { + const { content } = await readFileTool.execute( + { path: "package.json", description: "test" }, + { logger }, + ); + expect(content).toContain("mycoder"); + }); + + it("should handle missing files", async () => { + try { + await readFileTool.execute( + { path: "nonexistent.txt", description: "test" }, + { logger }, + ); + expect(true).toBe(false); // Should not reach here + } catch (error: any) { + expect(error.message).toContain("ENOENT"); + } + }); +}); diff --git a/src/tools/io/readFile.ts b/packages/agent/src/tools/io/readFile.ts similarity index 59% rename from src/tools/io/readFile.ts rename to packages/agent/src/tools/io/readFile.ts index 41e7a51..0403ae7 100644 --- a/src/tools/io/readFile.ts +++ b/packages/agent/src/tools/io/readFile.ts @@ -1,30 +1,32 @@ -import * as fs from 'fs/promises'; -import * as path from 'path'; -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; +import * as fs from "fs/promises"; +import * as path from "path"; + +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; const OUTPUT_LIMIT = 10 * 1024; // 10KB limit const parameterSchema = z.object({ - path: z.string().describe('Path to the file to read'), + path: z.string().describe("Path to the file to read"), range: z .object({ start: z.number(), end: z.number(), }) .optional() - .describe('Range of bytes to read'), + .describe("Range of bytes to read"), maxSize: z .number() .optional() .describe( - 'Maximum size to read, prevents reading arbitrarily large files that blow up the context window', + "Maximum size to read, prevents reading arbitrarily large files that blow up the context window, max is 10KB", ), description: z .string() .max(80) - .describe('The reason you are reading this file (max 80 chars)'), + .describe("The reason you are reading this file (max 80 chars)"), }); const returnSchema = z.object({ @@ -43,18 +45,26 @@ type Parameters = z.infer; type ReturnType = z.infer; export const readFileTool: Tool = { - name: 'readFile', - description: 'Reads file content within size limits and optional range', + name: "readFile", + description: "Reads file content within size limits and optional range", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), - execute: async ({ path: filePath, range, maxSize = OUTPUT_LIMIT }) => { - const absolutePath = path.resolve(path.normalize(filePath)); + execute: async ( + { path: filePath, range, maxSize = OUTPUT_LIMIT }, + context, + ) => { + const normalizedPath = path.normalize(filePath); + const absolutePath = path.isAbsolute(normalizedPath) + ? normalizedPath + : context?.workingDirectory + ? path.join(context.workingDirectory, normalizedPath) + : path.resolve(normalizedPath); const stats = await fs.stat(absolutePath); const readSize = range ? range.end - range.start : stats.size; if (readSize > maxSize) { throw new Error( - `Requested size ${readSize} bytes exceeds maximum ${maxSize} bytes`, + `Requested size ${readSize} bytes exceeds maximum ${maxSize} bytes, make a request for a subset of the file using the range parameter`, ); } @@ -70,7 +80,7 @@ export const readFileTool: Tool = { ); return { path: filePath, - content: buffer.toString('utf8', 0, bytesRead), + content: buffer.toString("utf8", 0, bytesRead), size: stats.size, range, }; @@ -81,7 +91,7 @@ export const readFileTool: Tool = { return { path: filePath, - content: await fs.readFile(absolutePath, 'utf8'), + content: await fs.readFile(absolutePath, "utf8"), size: stats.size, }; }, diff --git a/src/tools/io/updateFile.test.ts b/packages/agent/src/tools/io/updateFile.test.ts similarity index 58% rename from src/tools/io/updateFile.test.ts rename to packages/agent/src/tools/io/updateFile.test.ts index 1ad597e..af5fdc3 100644 --- a/src/tools/io/updateFile.test.ts +++ b/packages/agent/src/tools/io/updateFile.test.ts @@ -1,32 +1,34 @@ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { join } from 'path'; -import { randomUUID } from 'crypto'; -import { mkdtemp } from 'fs/promises'; -import { tmpdir } from 'os'; -import { updateFileTool } from './updateFile.js'; -import { readFileTool } from './readFile.js'; -import { shellExecuteTool } from '../system/shellExecute.js'; -import { MockLogger } from '../../utils/mockLogger.js'; +import { randomUUID } from "crypto"; +import { mkdtemp } from "fs/promises"; +import { tmpdir } from "os"; +import { join } from "path"; + +import { describe, it, expect, beforeEach, afterEach } from "vitest"; + +import { MockLogger } from "../../utils/mockLogger.js"; +import { shellExecuteTool } from "../system/shellExecute.js"; + +import { readFileTool } from "./readFile.js"; +import { updateFileTool } from "./updateFile.js"; const logger = new MockLogger(); -// eslint-disable-next-line max-lines-per-function -describe('updateFile', () => { +describe("updateFile", () => { let testDir: string; beforeEach(async () => { - testDir = await mkdtemp(join(tmpdir(), 'updatefile-test-')); + testDir = await mkdtemp(join(tmpdir(), "updatefile-test-")); }); afterEach(async () => { await shellExecuteTool.execute( - { command: `rm -rf "${testDir}"`, description: 'test' }, + { command: `rm -rf "${testDir}"`, description: "test" }, { logger }, ); }); it("should rewrite a file's content", async () => { - const testContent = 'test content'; + const testContent = "test content"; const testPath = join(testDir, `${randomUUID()}.txt`); // Create and rewrite the file @@ -34,29 +36,29 @@ describe('updateFile', () => { { path: testPath, operation: { - command: 'rewrite', + command: "rewrite", content: testContent, }, - description: 'test', + description: "test", }, { logger }, ); // Verify return value expect(result.path).toBe(testPath); - expect(result.operation).toBe('rewrite'); + expect(result.operation).toBe("rewrite"); // Verify content const readResult = await readFileTool.execute( - { path: testPath, description: 'test' }, + { path: testPath, description: "test" }, { logger }, ); expect(readResult.content).toBe(testContent); }); - it('should append content to a file', async () => { - const initialContent = 'initial content\n'; - const appendContent = 'appended content'; + it("should append content to a file", async () => { + const initialContent = "initial content\n"; + const appendContent = "appended content"; const expectedContent = initialContent + appendContent; const testPath = join(testDir, `${randomUUID()}.txt`); @@ -65,10 +67,10 @@ describe('updateFile', () => { { path: testPath, operation: { - command: 'rewrite', + command: "rewrite", content: initialContent, }, - description: 'test', + description: "test", }, { logger }, ); @@ -78,31 +80,31 @@ describe('updateFile', () => { { path: testPath, operation: { - command: 'append', + command: "append", content: appendContent, }, - description: 'test', + description: "test", }, { logger }, ); // Verify return value expect(result.path).toBe(testPath); - expect(result.operation).toBe('append'); + expect(result.operation).toBe("append"); // Verify content const readResult = await readFileTool.execute( - { path: testPath, description: 'test' }, + { path: testPath, description: "test" }, { logger }, ); expect(readResult.content).toBe(expectedContent); }); - it('should update specific text in a file', async () => { - const initialContent = 'Hello world! This is a test.'; - const oldStr = 'world'; - const newStr = 'universe'; - const expectedContent = 'Hello universe! This is a test.'; + it("should update specific text in a file", async () => { + const initialContent = "Hello world! This is a test."; + const oldStr = "world"; + const newStr = "universe"; + const expectedContent = "Hello universe! This is a test."; const testPath = join(testDir, `${randomUUID()}.txt`); // Create initial file @@ -110,10 +112,10 @@ describe('updateFile', () => { { path: testPath, operation: { - command: 'rewrite', + command: "rewrite", content: initialContent, }, - description: 'test', + description: "test", }, { logger }, ); @@ -123,31 +125,31 @@ describe('updateFile', () => { { path: testPath, operation: { - command: 'update', + command: "update", oldStr, newStr, }, - description: 'test', + description: "test", }, { logger }, ); // Verify return value expect(result.path).toBe(testPath); - expect(result.operation).toBe('update'); + expect(result.operation).toBe("update"); // Verify content const readResult = await readFileTool.execute( - { path: testPath, description: 'test' }, + { path: testPath, description: "test" }, { logger }, ); expect(readResult.content).toBe(expectedContent); }); - it('should throw error when update finds multiple occurrences', async () => { - const initialContent = 'Hello world! This is a world test.'; - const oldStr = 'world'; - const newStr = 'universe'; + it("should throw error when update finds multiple occurrences", async () => { + const initialContent = "Hello world! This is a world test."; + const oldStr = "world"; + const newStr = "universe"; const testPath = join(testDir, `${randomUUID()}.txt`); // Create initial file @@ -155,10 +157,10 @@ describe('updateFile', () => { { path: testPath, operation: { - command: 'rewrite', + command: "rewrite", content: initialContent, }, - description: 'test', + description: "test", }, { logger }, ); @@ -169,41 +171,41 @@ describe('updateFile', () => { { path: testPath, operation: { - command: 'update', + command: "update", oldStr, newStr, }, - description: 'test', + description: "test", }, { logger }, ), - ).rejects.toThrow('Found 2 occurrences of oldStr, expected exactly 1'); + ).rejects.toThrow("Found 2 occurrences of oldStr, expected exactly 1"); }); it("should create parent directories if they don't exist", async () => { - const testContent = 'test content'; - const nestedPath = join(testDir, 'nested', 'dir', `${randomUUID()}.txt`); + const testContent = "test content"; + const nestedPath = join(testDir, "nested", "dir", `${randomUUID()}.txt`); // Create file in nested directory const result = await updateFileTool.execute( { path: nestedPath, operation: { - command: 'rewrite', + command: "rewrite", content: testContent, }, - description: 'test', + description: "test", }, { logger }, ); // Verify return value expect(result.path).toBe(nestedPath); - expect(result.operation).toBe('rewrite'); + expect(result.operation).toBe("rewrite"); // Verify content const readResult = await readFileTool.execute( - { path: nestedPath, description: 'test' }, + { path: nestedPath, description: "test" }, { logger }, ); expect(readResult.content).toBe(testContent); diff --git a/packages/agent/src/tools/io/updateFile.ts b/packages/agent/src/tools/io/updateFile.ts new file mode 100644 index 0000000..fa5f8b0 --- /dev/null +++ b/packages/agent/src/tools/io/updateFile.ts @@ -0,0 +1,92 @@ +import * as fs from "fs"; +import * as fsPromises from "fs/promises"; +import * as path from "path"; + +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; + +const updateOperationSchema = z.discriminatedUnion("command", [ + z.object({ + command: z.literal("update"), + oldStr: z.string().describe("Existing text to replace (must be unique)"), + newStr: z.string().describe("New text to insert"), + }), + z.object({ + command: z.literal("rewrite"), + content: z.string().describe("Complete new file content"), + }), + z.object({ + command: z.literal("append"), + content: z.string().describe("Content to append to file"), + }), +]); + +const parameterSchema = z.object({ + path: z.string().describe("Path to the file"), + operation: updateOperationSchema.describe("Update operation to perform"), + description: z + .string() + .max(80) + .describe("The reason you are modifying this file (max 80 chars)"), +}); + +const returnSchema = z.object({ + path: z.string().describe("Path to the updated file"), + operation: z.enum(["update", "rewrite", "append"]), +}); + +type Parameters = z.infer; +type ReturnType = z.infer; + +export const updateFileTool: Tool = { + name: "updateFile", + description: + "Creates a file or updates a file by rewriting, patching, or appending content", + parameters: zodToJsonSchema(parameterSchema), + returns: zodToJsonSchema(returnSchema), + execute: async ( + { path: filePath, operation }, + { logger, workingDirectory }, + ) => { + const normalizedPath = path.normalize(filePath); + const absolutePath = path.isAbsolute(normalizedPath) + ? normalizedPath + : workingDirectory + ? path.join(workingDirectory, normalizedPath) + : path.resolve(normalizedPath); + logger.verbose(`Updating file: ${absolutePath}`); + + await fsPromises.mkdir(path.dirname(absolutePath), { recursive: true }); + + if (operation.command === "update") { + const content = await fsPromises.readFile(absolutePath, "utf8"); + const occurrences = content.split(operation.oldStr).length - 1; + if (occurrences !== 1) { + throw new Error( + `Found ${occurrences} occurrences of oldStr, expected exactly 1`, + ); + } + await fsPromises.writeFile( + absolutePath, + content.replace(operation.oldStr, operation.newStr), + "utf8", + ); + } else if (operation.command === "append") { + await fsPromises.appendFile(absolutePath, operation.content, "utf8"); + } else { + await fsPromises.writeFile(absolutePath, operation.content, "utf8"); + } + + logger.verbose(`Operation complete: ${operation.command}`); + return { path: filePath, operation: operation.command }; + }, + logParameters: (input, { logger }) => { + const isFile = fs.existsSync(input.path); + logger.info( + `${isFile ? "Modifying" : "Creating"} "${input.path}", ${input.description}`, + ); + }, + logReturns: () => {}, +}; diff --git a/packages/agent/src/tools/system/respawn.test.ts b/packages/agent/src/tools/system/respawn.test.ts new file mode 100644 index 0000000..8f56631 --- /dev/null +++ b/packages/agent/src/tools/system/respawn.test.ts @@ -0,0 +1,22 @@ +import { describe, it, expect } from "vitest"; + +import { Logger } from "../../utils/logger"; + +import { respawnTool } from "./respawn"; + +describe("respawnTool", () => { + const mockLogger = new Logger({ name: "test" }); + + it("should have correct name and description", () => { + expect(respawnTool.name).toBe("respawn"); + expect(respawnTool.description).toContain("Resets the agent context"); + }); + + it("should execute and return confirmation message", async () => { + const result = await respawnTool.execute( + { respawnContext: "new context" }, + { logger: mockLogger }, + ); + expect(result).toBe("Respawn initiated"); + }); +}); diff --git a/packages/agent/src/tools/system/respawn.ts b/packages/agent/src/tools/system/respawn.ts new file mode 100644 index 0000000..e9b268a --- /dev/null +++ b/packages/agent/src/tools/system/respawn.ts @@ -0,0 +1,33 @@ +import { Tool, ToolContext } from "../../core/types.js"; + +export interface RespawnInput { + respawnContext: string; +} + +export const respawnTool: Tool = { + name: "respawn", + description: + "Resets the agent context to just the system prompt and provided context", + parameters: { + type: "object", + properties: { + respawnContext: { + type: "string", + description: "The context to keep after respawning", + }, + }, + required: ["respawnContext"], + additionalProperties: false, + }, + returns: { + type: "string", + description: "A message indicating that the respawn has been initiated", + }, + execute: ( + _params: Record, + _context: ToolContext, + ): Promise => { + // This is a special case tool - the actual respawn logic is handled in toolAgent + return Promise.resolve("Respawn initiated"); + }, +}; diff --git a/src/tools/system/sequenceComplete.ts b/packages/agent/src/tools/system/sequenceComplete.ts similarity index 57% rename from src/tools/system/sequenceComplete.ts rename to packages/agent/src/tools/system/sequenceComplete.ts index 380e20d..e05ddc3 100644 --- a/src/tools/system/sequenceComplete.ts +++ b/packages/agent/src/tools/system/sequenceComplete.ts @@ -1,21 +1,22 @@ -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; const parameterSchema = z.object({ - result: z.string().describe('The final result to return from the tool agent'), + result: z.string().describe("The final result to return from the tool agent"), }); const returnSchema = z .string() - .describe('This is returned to the caller of the tool agent.'); + .describe("This is returned to the caller of the tool agent."); type Parameters = z.infer; type ReturnType = z.infer; export const sequenceCompleteTool: Tool = { - name: 'sequenceComplete', - description: 'Completes the tool use sequence and returns the final result', + name: "sequenceComplete", + description: "Completes the tool use sequence and returns the final result", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), execute: ({ result }) => Promise.resolve(result), diff --git a/packages/agent/src/tools/system/shellExecute.test.ts b/packages/agent/src/tools/system/shellExecute.test.ts new file mode 100644 index 0000000..fef94b3 --- /dev/null +++ b/packages/agent/src/tools/system/shellExecute.test.ts @@ -0,0 +1,25 @@ +import { describe, it, expect } from "vitest"; + +import { MockLogger } from "../../utils/mockLogger.js"; + +import { shellExecuteTool } from "./shellExecute.js"; + +const logger = new MockLogger(); + +describe("shellExecute", () => { + it("should execute shell commands", async () => { + const { stdout } = await shellExecuteTool.execute( + { command: "echo 'test'", description: "test" }, + { logger }, + ); + expect(stdout).toContain("test"); + }); + + it("should handle command errors", async () => { + const { error } = await shellExecuteTool.execute( + { command: "nonexistentcommand", description: "test" }, + { logger }, + ); + expect(error).toContain("Command failed:"); + }); +}); diff --git a/src/tools/system/shellExecute.ts b/packages/agent/src/tools/system/shellExecute.ts similarity index 66% rename from src/tools/system/shellExecute.ts rename to packages/agent/src/tools/system/shellExecute.ts index 58e4789..3dacd52 100644 --- a/src/tools/system/shellExecute.ts +++ b/packages/agent/src/tools/system/shellExecute.ts @@ -1,24 +1,26 @@ -import { exec, ExecException } from 'child_process'; -import { promisify } from 'util'; -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; -import { errorToString } from '../../utils/errorToString.js'; +import { exec, ExecException } from "child_process"; +import { promisify } from "util"; + +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; +import { errorToString } from "../../utils/errorToString.js"; const execAsync = promisify(exec); const parameterSchema = z.object({ command: z .string() - .describe('The shell command to execute in MacOS bash format'), + .describe("The shell command to execute in MacOS bash format"), description: z .string() .max(80) - .describe('The reason this shell command is being run (max 80 chars)'), + .describe("The reason this shell command is being run (max 80 chars)"), timeout: z .number() .optional() - .describe('Timeout in milliseconds (optional, default 30000)'), + .describe("Timeout in milliseconds (optional, default 30000)"), }); const returnSchema = z @@ -30,7 +32,7 @@ const returnSchema = z error: z.string().optional(), }) .describe( - 'Command execution results including stdout, stderr, and exit code', + "Command execution results including stdout, stderr, and exit code", ); type Parameters = z.infer; @@ -42,9 +44,9 @@ interface ExtendedExecException extends ExecException { } export const shellExecuteTool: Tool = { - name: 'shellExecute', + name: "shellExecute", description: - 'Executes a bash shell command and returns its output, can do amazing things if you are a shell scripting wizard', + "Executes a bash shell command and returns its output, can do amazing things if you are a shell scripting wizard", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), @@ -62,7 +64,7 @@ export const shellExecuteTool: Tool = { maxBuffer: 10 * 1024 * 1024, // 10MB buffer }); - logger.verbose('Command executed successfully'); + logger.verbose("Command executed successfully"); logger.verbose(`stdout: ${stdout.trim()}`); if (stderr.trim()) { logger.verbose(`stderr: ${stderr.trim()}`); @@ -72,22 +74,22 @@ export const shellExecuteTool: Tool = { stdout: stdout.trim(), stderr: stderr.trim(), code: 0, - error: '', + error: "", command, }; } catch (error) { if (error instanceof Error) { const execError = error as ExtendedExecException; - const isTimeout = error.message.includes('timeout'); + const isTimeout = error.message.includes("timeout"); logger.verbose(`Command execution failed: ${error.message}`); return { error: isTimeout - ? 'Command execution timed out after ' + timeout + 'ms' + ? "Command execution timed out after " + timeout + "ms" : error.message, - stdout: execError.stdout?.trim() ?? '', - stderr: execError.stderr?.trim() ?? '', + stdout: execError.stdout?.trim() ?? "", + stderr: execError.stderr?.trim() ?? "", code: execError.code ?? -1, command, }; @@ -97,8 +99,8 @@ export const shellExecuteTool: Tool = { ); return { error: `Unknown error occurred: ${errorToString(error)}`, - stdout: '', - stderr: '', + stdout: "", + stderr: "", code: -1, command, }; diff --git a/src/tools/system/shellMessage.test.ts b/packages/agent/src/tools/system/shellMessage.test.ts similarity index 67% rename from src/tools/system/shellMessage.test.ts rename to packages/agent/src/tools/system/shellMessage.test.ts index 51ed74b..acb731e 100644 --- a/src/tools/system/shellMessage.test.ts +++ b/packages/agent/src/tools/system/shellMessage.test.ts @@ -1,8 +1,10 @@ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { processStates, shellStartTool } from './shellStart.js'; -import { MockLogger } from '../../utils/mockLogger.js'; -import { shellMessageTool, NodeSignals } from './shellMessage.js'; -import { sleep } from '../../utils/sleep.js'; +import { describe, it, expect, beforeEach, afterEach } from "vitest"; + +import { MockLogger } from "../../utils/mockLogger.js"; +import { sleep } from "../../utils/sleep.js"; + +import { shellMessageTool, NodeSignals } from "./shellMessage.js"; +import { processStates, shellStartTool } from "./shellStart.js"; const logger = new MockLogger(); @@ -10,15 +12,14 @@ const logger = new MockLogger(); const getInstanceId = ( result: Awaited>, ) => { - if (result.mode === 'async') { + if (result.mode === "async") { return result.instanceId; } - throw new Error('Expected async mode result'); + throw new Error("Expected async mode result"); }; -// eslint-disable-next-line max-lines-per-function -describe('shellMessageTool', () => { - let testInstanceId = ''; +describe("shellMessageTool", () => { + let testInstanceId = ""; beforeEach(() => { processStates.clear(); @@ -31,12 +32,12 @@ describe('shellMessageTool', () => { processStates.clear(); }); - it('should interact with a running process', async () => { + it("should interact with a running process", async () => { // Start a test process - force async mode with timeout const startResult = await shellStartTool.execute( { - command: 'cat', // cat will echo back input - description: 'Test interactive process', + command: "cat", // cat will echo back input + description: "Test interactive process", timeout: 50, // Force async mode for interactive process }, { logger }, @@ -48,22 +49,22 @@ describe('shellMessageTool', () => { const result = await shellMessageTool.execute( { instanceId: testInstanceId, - stdin: 'hello world', - description: 'Test interaction', + stdin: "hello world", + description: "Test interaction", }, { logger }, ); - expect(result.stdout).toBe('hello world'); - expect(result.stderr).toBe(''); + expect(result.stdout).toBe("hello world"); + expect(result.stderr).toBe(""); expect(result.completed).toBe(false); }); - it('should handle nonexistent process', async () => { + it("should handle nonexistent process", async () => { const result = await shellMessageTool.execute( { - instanceId: 'nonexistent-id', - description: 'Test invalid process', + instanceId: "nonexistent-id", + description: "Test invalid process", }, { logger }, ); @@ -72,12 +73,12 @@ describe('shellMessageTool', () => { expect(result.completed).toBe(false); }); - it('should handle process completion', async () => { + it("should handle process completion", async () => { // Start a quick process - force async mode const startResult = await shellStartTool.execute( { command: 'echo "test" && sleep 0.1', - description: 'Test completion', + description: "Test completion", timeout: 0, // Force async mode }, { logger }, @@ -91,7 +92,7 @@ describe('shellMessageTool', () => { const result = await shellMessageTool.execute( { instanceId, - description: 'Check completion', + description: "Check completion", }, { logger }, ); @@ -101,12 +102,12 @@ describe('shellMessageTool', () => { expect(processStates.has(instanceId)).toBe(true); }); - it('should handle SIGTERM signal correctly', async () => { + it("should handle SIGTERM signal correctly", async () => { // Start a long-running process const startResult = await shellStartTool.execute( { - command: 'sleep 10', - description: 'Test SIGTERM handling', + command: "sleep 10", + description: "Test SIGTERM handling", timeout: 0, // Force async mode }, { logger }, @@ -118,7 +119,7 @@ describe('shellMessageTool', () => { { instanceId, signal: NodeSignals.SIGTERM, - description: 'Send SIGTERM', + description: "Send SIGTERM", }, { logger }, ); @@ -129,7 +130,7 @@ describe('shellMessageTool', () => { const result2 = await shellMessageTool.execute( { instanceId, - description: 'Check on status', + description: "Check on status", }, { logger }, ); @@ -138,12 +139,12 @@ describe('shellMessageTool', () => { expect(result2.error).toBeUndefined(); }); - it('should handle signals on terminated process gracefully', async () => { + it("should handle signals on terminated process gracefully", async () => { // Start a process const startResult = await shellStartTool.execute( { - command: 'sleep 1', - description: 'Test signal handling on terminated process', + command: "sleep 1", + description: "Test signal handling on terminated process", timeout: 0, // Force async mode }, { logger }, @@ -156,7 +157,7 @@ describe('shellMessageTool', () => { { instanceId, signal: NodeSignals.SIGTERM, - description: 'Send signal to terminated process', + description: "Send signal to terminated process", }, { logger }, ); @@ -165,12 +166,12 @@ describe('shellMessageTool', () => { expect(result.completed).toBe(true); }); - it('should verify signaled flag after process termination', async () => { + it("should verify signaled flag after process termination", async () => { // Start a process const startResult = await shellStartTool.execute( { - command: 'sleep 5', - description: 'Test signal flag verification', + command: "sleep 5", + description: "Test signal flag verification", timeout: 0, // Force async mode }, { logger }, @@ -183,7 +184,7 @@ describe('shellMessageTool', () => { { instanceId, signal: NodeSignals.SIGTERM, - description: 'Send SIGTERM', + description: "Send SIGTERM", }, { logger }, ); @@ -194,7 +195,7 @@ describe('shellMessageTool', () => { const checkResult = await shellMessageTool.execute( { instanceId, - description: 'Check signal state', + description: "Check signal state", }, { logger }, ); diff --git a/src/tools/system/shellMessage.ts b/packages/agent/src/tools/system/shellMessage.ts similarity index 58% rename from src/tools/system/shellMessage.ts rename to packages/agent/src/tools/system/shellMessage.ts index 8feb346..b2279d9 100644 --- a/src/tools/system/shellMessage.ts +++ b/packages/agent/src/tools/system/shellMessage.ts @@ -1,58 +1,60 @@ -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; -import { processStates } from './shellStart.js'; -import { sleep } from '../../utils/sleep.js'; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; +import { sleep } from "../../utils/sleep.js"; + +import { processStates } from "./shellStart.js"; // Define NodeJS signals as an enum export enum NodeSignals { - SIGABRT = 'SIGABRT', - SIGALRM = 'SIGALRM', - SIGBUS = 'SIGBUS', - SIGCHLD = 'SIGCHLD', - SIGCONT = 'SIGCONT', - SIGFPE = 'SIGFPE', - SIGHUP = 'SIGHUP', - SIGILL = 'SIGILL', - SIGINT = 'SIGINT', - SIGIO = 'SIGIO', - SIGIOT = 'SIGIOT', - SIGKILL = 'SIGKILL', - SIGPIPE = 'SIGPIPE', - SIGPOLL = 'SIGPOLL', - SIGPROF = 'SIGPROF', - SIGPWR = 'SIGPWR', - SIGQUIT = 'SIGQUIT', - SIGSEGV = 'SIGSEGV', - SIGSTKFLT = 'SIGSTKFLT', - SIGSTOP = 'SIGSTOP', - SIGSYS = 'SIGSYS', - SIGTERM = 'SIGTERM', - SIGTRAP = 'SIGTRAP', - SIGTSTP = 'SIGTSTP', - SIGTTIN = 'SIGTTIN', - SIGTTOU = 'SIGTTOU', - SIGUNUSED = 'SIGUNUSED', - SIGURG = 'SIGURG', - SIGUSR1 = 'SIGUSR1', - SIGUSR2 = 'SIGUSR2', - SIGVTALRM = 'SIGVTALRM', - SIGWINCH = 'SIGWINCH', - SIGXCPU = 'SIGXCPU', - SIGXFSZ = 'SIGXFSZ', + SIGABRT = "SIGABRT", + SIGALRM = "SIGALRM", + SIGBUS = "SIGBUS", + SIGCHLD = "SIGCHLD", + SIGCONT = "SIGCONT", + SIGFPE = "SIGFPE", + SIGHUP = "SIGHUP", + SIGILL = "SIGILL", + SIGINT = "SIGINT", + SIGIO = "SIGIO", + SIGIOT = "SIGIOT", + SIGKILL = "SIGKILL", + SIGPIPE = "SIGPIPE", + SIGPOLL = "SIGPOLL", + SIGPROF = "SIGPROF", + SIGPWR = "SIGPWR", + SIGQUIT = "SIGQUIT", + SIGSEGV = "SIGSEGV", + SIGSTKFLT = "SIGSTKFLT", + SIGSTOP = "SIGSTOP", + SIGSYS = "SIGSYS", + SIGTERM = "SIGTERM", + SIGTRAP = "SIGTRAP", + SIGTSTP = "SIGTSTP", + SIGTTIN = "SIGTTIN", + SIGTTOU = "SIGTTOU", + SIGUNUSED = "SIGUNUSED", + SIGURG = "SIGURG", + SIGUSR1 = "SIGUSR1", + SIGUSR2 = "SIGUSR2", + SIGVTALRM = "SIGVTALRM", + SIGWINCH = "SIGWINCH", + SIGXCPU = "SIGXCPU", + SIGXFSZ = "SIGXFSZ", } const parameterSchema = z.object({ - instanceId: z.string().describe('The ID returned by shellStart'), - stdin: z.string().optional().describe('Input to send to process'), + instanceId: z.string().describe("The ID returned by shellStart"), + stdin: z.string().optional().describe("Input to send to process"), signal: z .nativeEnum(NodeSignals) .optional() - .describe('Signal to send to the process (e.g., SIGTERM, SIGINT)'), + .describe("Signal to send to the process (e.g., SIGTERM, SIGINT)"), description: z .string() .max(80) - .describe('The reason for this shell interaction (max 80 chars)'), + .describe("The reason for this shell interaction (max 80 chars)"), }); const returnSchema = z @@ -64,16 +66,16 @@ const returnSchema = z signaled: z.boolean().optional(), }) .describe( - 'Process interaction results including stdout, stderr, and completion status', + "Process interaction results including stdout, stderr, and completion status", ); type Parameters = z.infer; type ReturnType = z.infer; export const shellMessageTool: Tool = { - name: 'shellMessage', + name: "shellMessage", description: - 'Interacts with a running shell process, sending input and receiving output', + "Interacts with a running shell process, sending input and receiving output", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), @@ -82,7 +84,7 @@ export const shellMessageTool: Tool = { { logger }, ): Promise => { logger.verbose( - `Interacting with shell process ${instanceId}${stdin ? ' with input' : ''}${signal ? ` with signal ${signal}` : ''}`, + `Interacting with shell process ${instanceId}${stdin ? " with input" : ""}${signal ? ` with signal ${signal}` : ""}`, ); try { @@ -96,8 +98,8 @@ export const shellMessageTool: Tool = { const wasKilled = processState.process.kill(signal); if (!wasKilled) { return { - stdout: '', - stderr: '', + stdout: "", + stderr: "", completed: processState.state.completed, signaled: false, error: `Failed to send signal ${signal} to process (process may have already terminated)`, @@ -109,7 +111,7 @@ export const shellMessageTool: Tool = { // Send input if provided if (stdin) { if (!processState.process.stdin?.writable) { - throw new Error('Process stdin is not available'); + throw new Error("Process stdin is not available"); } processState.process.stdin.write(`${stdin}\n`); } @@ -118,14 +120,14 @@ export const shellMessageTool: Tool = { await sleep(100); // Get accumulated output - const stdout = processState.stdout.join(''); - const stderr = processState.stderr.join(''); + const stdout = processState.stdout.join(""); + const stderr = processState.stderr.join(""); // Clear the buffers processState.stdout = []; processState.stderr = []; - logger.verbose('Interaction completed successfully'); + logger.verbose("Interaction completed successfully"); if (stdout) { logger.verbose(`stdout: ${stdout.trim()}`); } @@ -144,8 +146,8 @@ export const shellMessageTool: Tool = { logger.verbose(`Process interaction failed: ${error.message}`); return { - stdout: '', - stderr: '', + stdout: "", + stderr: "", completed: false, error: error.message, }; @@ -154,8 +156,8 @@ export const shellMessageTool: Tool = { const errorMessage = String(error); logger.error(`Unknown error during process interaction: ${errorMessage}`); return { - stdout: '', - stderr: '', + stdout: "", + stderr: "", completed: false, error: `Unknown error occurred: ${errorMessage}`, }; @@ -165,7 +167,7 @@ export const shellMessageTool: Tool = { logParameters: (input, { logger }) => { const processState = processStates.get(input.instanceId); logger.info( - `Interacting with shell command "${processState ? processState.command : ''}", ${input.description}`, + `Interacting with shell command "${processState ? processState.command : ""}", ${input.description}`, ); }, logReturns: () => {}, diff --git a/src/tools/system/shellStart.test.ts b/packages/agent/src/tools/system/shellStart.test.ts similarity index 54% rename from src/tools/system/shellStart.test.ts rename to packages/agent/src/tools/system/shellStart.test.ts index c63138a..c48331d 100644 --- a/src/tools/system/shellStart.test.ts +++ b/packages/agent/src/tools/system/shellStart.test.ts @@ -1,11 +1,13 @@ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { processStates, shellStartTool } from './shellStart.js'; -import { MockLogger } from '../../utils/mockLogger.js'; -import { sleep } from '../../utils/sleep.js'; +import { describe, it, expect, beforeEach, afterEach } from "vitest"; + +import { MockLogger } from "../../utils/mockLogger.js"; +import { sleep } from "../../utils/sleep.js"; + +import { processStates, shellStartTool } from "./shellStart.js"; const logger = new MockLogger(); -describe('shellStartTool', () => { +describe("shellStartTool", () => { beforeEach(() => { processStates.clear(); }); @@ -17,63 +19,63 @@ describe('shellStartTool', () => { processStates.clear(); }); - it('should handle fast commands in sync mode', async () => { + it("should handle fast commands in sync mode", async () => { const result = await shellStartTool.execute( { command: 'echo "test"', - description: 'Test process', + description: "Test process", timeout: 500, // Generous timeout to ensure sync mode }, { logger }, ); - expect(result.mode).toBe('sync'); - if (result.mode === 'sync') { + expect(result.mode).toBe("sync"); + if (result.mode === "sync") { expect(result.exitCode).toBe(0); - expect(result.stdout).toBe('test'); + expect(result.stdout).toBe("test"); expect(result.error).toBeUndefined(); } }); - it('should switch to async mode for slow commands', async () => { + it("should switch to async mode for slow commands", async () => { const result = await shellStartTool.execute( { - command: 'sleep 1', - description: 'Slow command test', + command: "sleep 1", + description: "Slow command test", timeout: 50, // Short timeout to force async mode }, { logger }, ); - expect(result.mode).toBe('async'); - if (result.mode === 'async') { + expect(result.mode).toBe("async"); + if (result.mode === "async") { expect(result.instanceId).toBeDefined(); expect(result.error).toBeUndefined(); } }); - it('should handle invalid commands with sync error', async () => { + it("should handle invalid commands with sync error", async () => { const result = await shellStartTool.execute( { - command: 'nonexistentcommand', - description: 'Invalid command test', + command: "nonexistentcommand", + description: "Invalid command test", }, { logger }, ); - expect(result.mode).toBe('sync'); - if (result.mode === 'sync') { + expect(result.mode).toBe("sync"); + if (result.mode === "sync") { expect(result.exitCode).not.toBe(0); expect(result.error).toBeDefined(); } }); - it('should keep process in processStates in both modes', async () => { + it("should keep process in processStates in both modes", async () => { // Test sync mode const syncResult = await shellStartTool.execute( { command: 'echo "test"', - description: 'Sync completion test', + description: "Sync completion test", timeout: 500, }, { logger }, @@ -81,39 +83,39 @@ describe('shellStartTool', () => { // Even sync results should be in processStates expect(processStates.size).toBeGreaterThan(0); - expect(syncResult.mode).toBe('sync'); + expect(syncResult.mode).toBe("sync"); expect(syncResult.error).toBeUndefined(); - if (syncResult.mode === 'sync') { + if (syncResult.mode === "sync") { expect(syncResult.exitCode).toBe(0); } // Test async mode const asyncResult = await shellStartTool.execute( { - command: 'sleep 1', - description: 'Async completion test', + command: "sleep 1", + description: "Async completion test", timeout: 50, }, { logger }, ); - if (asyncResult.mode === 'async') { + if (asyncResult.mode === "async") { expect(processStates.has(asyncResult.instanceId)).toBe(true); } }); - it('should handle piped commands correctly in async mode', async () => { + it("should handle piped commands correctly in async mode", async () => { const result = await shellStartTool.execute( { command: 'grep "test"', - description: 'Pipe test', + description: "Pipe test", timeout: 50, // Force async for interactive command }, { logger }, ); - expect(result.mode).toBe('async'); - if (result.mode === 'async') { + expect(result.mode).toBe("async"); + if (result.mode === "async") { expect(result.instanceId).toBeDefined(); expect(result.error).toBeUndefined(); @@ -121,30 +123,30 @@ describe('shellStartTool', () => { expect(processState).toBeDefined(); if (processState?.process.stdin) { - processState.process.stdin.write('this is a test line\n'); - processState.process.stdin.write('not matching line\n'); - processState.process.stdin.write('another test here\n'); + processState.process.stdin.write("this is a test line\n"); + processState.process.stdin.write("not matching line\n"); + processState.process.stdin.write("another test here\n"); processState.process.stdin.end(); // Wait for output await sleep(200); // Check stdout in processState - expect(processState.stdout.join('')).toContain('test'); - expect(processState.stdout.join('')).not.toContain('not matching'); + expect(processState.stdout.join("")).toContain("test"); + expect(processState.stdout.join("")).not.toContain("not matching"); } } }); - it('should use default timeout of 10000ms', async () => { + it("should use default timeout of 10000ms", async () => { const result = await shellStartTool.execute( { - command: 'sleep 1', - description: 'Default timeout test', + command: "sleep 1", + description: "Default timeout test", }, { logger }, ); - expect(result.mode).toBe('sync'); + expect(result.mode).toBe("sync"); }); }); diff --git a/src/tools/system/shellStart.ts b/packages/agent/src/tools/system/shellStart.ts similarity index 70% rename from src/tools/system/shellStart.ts rename to packages/agent/src/tools/system/shellStart.ts index 34739cf..3ade4fd 100644 --- a/src/tools/system/shellStart.ts +++ b/packages/agent/src/tools/system/shellStart.ts @@ -1,10 +1,13 @@ -import { spawn } from 'child_process'; -import type { ChildProcess } from 'child_process'; -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; -import { v4 as uuidv4 } from 'uuid'; -import { errorToString } from '../../utils/errorToString.js'; +import { spawn } from "child_process"; + +import { v4 as uuidv4 } from "uuid"; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; +import { errorToString } from "../../utils/errorToString.js"; + +import type { ChildProcess } from "child_process"; // Define ProcessState type type ProcessState = { @@ -23,40 +26,40 @@ type ProcessState = { export const processStates: Map = new Map(); const parameterSchema = z.object({ - command: z.string().describe('The shell command to execute'), + command: z.string().describe("The shell command to execute"), description: z .string() .max(80) - .describe('The reason this shell command is being run (max 80 chars)'), + .describe("The reason this shell command is being run (max 80 chars)"), timeout: z .number() .optional() .describe( - 'Timeout in ms before switching to async mode (default: 10s, which usually is sufficient)', + "Timeout in ms before switching to async mode (default: 10s, which usually is sufficient)", ), }); const returnSchema = z.union([ z .object({ - mode: z.literal('sync'), + mode: z.literal("sync"), stdout: z.string(), stderr: z.string(), exitCode: z.number(), error: z.string().optional(), }) .describe( - 'Synchronous execution results when command completes within timeout', + "Synchronous execution results when command completes within timeout", ), z .object({ - mode: z.literal('async'), + mode: z.literal("async"), instanceId: z.string(), stdout: z.string(), stderr: z.string(), error: z.string().optional(), }) - .describe('Asynchronous execution results when command exceeds timeout'), + .describe("Asynchronous execution results when command exceeds timeout"), ]); type Parameters = z.infer; @@ -65,15 +68,15 @@ type ReturnType = z.infer; const DEFAULT_TIMEOUT = 1000 * 10; export const shellStartTool: Tool = { - name: 'shellStart', + name: "shellStart", description: - 'Starts a shell command with fast sync mode (default 100ms timeout) that falls back to async mode for longer-running commands', + "Starts a shell command with fast sync mode (default 100ms timeout) that falls back to async mode for longer-running commands", parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), execute: async ( { command, timeout = DEFAULT_TIMEOUT }, - { logger }, + { logger, workingDirectory }, ): Promise => { logger.verbose(`Starting shell command: ${command}`); @@ -84,7 +87,10 @@ export const shellStartTool: Tool = { // Split command into command and args // Use command directly with shell: true - const process = spawn(command, [], { shell: true }); + const process = spawn(command, [], { + shell: true, + cwd: workingDirectory, + }); const processState: ProcessState = { command, @@ -99,35 +105,35 @@ export const shellStartTool: Tool = { // Handle process events if (process.stdout) - process.stdout.on('data', (data) => { + process.stdout.on("data", (data) => { const output = data.toString(); processState.stdout.push(output); logger.verbose(`[${instanceId}] stdout: ${output.trim()}`); }); if (process.stderr) - process.stderr.on('data', (data) => { + process.stderr.on("data", (data) => { const output = data.toString(); processState.stderr.push(output); logger.verbose(`[${instanceId}] stderr: ${output.trim()}`); }); - process.on('error', (error) => { + process.on("error", (error) => { logger.error(`[${instanceId}] Process error: ${error.message}`); processState.state.completed = true; if (!hasResolved) { hasResolved = true; resolve({ - mode: 'async', + mode: "async", instanceId, - stdout: processState.stdout.join('').trim(), - stderr: processState.stderr.join('').trim(), + stdout: processState.stdout.join("").trim(), + stderr: processState.stderr.join("").trim(), error: error.message, }); } }); - process.on('exit', (code, signal) => { + process.on("exit", (code, signal) => { logger.verbose( `[${instanceId}] Process exited with code ${code} and signal ${signal}`, ); @@ -141,12 +147,12 @@ export const shellStartTool: Tool = { // If we haven't resolved yet, this happened within the timeout // so return sync results resolve({ - mode: 'sync', - stdout: processState.stdout.join('').trim(), - stderr: processState.stderr.join('').trim(), + mode: "sync", + stdout: processState.stdout.join("").trim(), + stderr: processState.stderr.join("").trim(), exitCode: code ?? 1, ...(code !== 0 && { - error: `Process exited with code ${code}${signal ? ` and signal ${signal}` : ''}`, + error: `Process exited with code ${code}${signal ? ` and signal ${signal}` : ""}`, }), }); } @@ -157,19 +163,19 @@ export const shellStartTool: Tool = { if (!hasResolved) { hasResolved = true; resolve({ - mode: 'async', + mode: "async", instanceId, - stdout: processState.stdout.join('').trim(), - stderr: processState.stderr.join('').trim(), + stdout: processState.stdout.join("").trim(), + stderr: processState.stderr.join("").trim(), }); } }, timeout); } catch (error) { logger.error(`Failed to start process: ${errorToString(error)}`); resolve({ - mode: 'sync', - stdout: '', - stderr: '', + mode: "sync", + stdout: "", + stderr: "", exitCode: 1, error: errorToString(error), }); @@ -186,7 +192,7 @@ export const shellStartTool: Tool = { ); }, logReturns: (output, { logger }) => { - if (output.mode === 'async') { + if (output.mode === "async") { logger.info(`Process started with instance ID: ${output.instanceId}`); } else { logger.info(`Process completed with exit code: ${output.exitCode}`); diff --git a/packages/agent/src/tools/system/sleep.test.ts b/packages/agent/src/tools/system/sleep.test.ts new file mode 100644 index 0000000..b951388 --- /dev/null +++ b/packages/agent/src/tools/system/sleep.test.ts @@ -0,0 +1,34 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +import { MockLogger } from "../../utils/mockLogger"; + +import { sleepTool } from "./sleep"; + +const logger = new MockLogger(); + +describe("sleep tool", () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + it("should sleep for the specified duration", async () => { + const sleepPromise = sleepTool.execute({ seconds: 2 }, { logger }); + + await vi.advanceTimersByTimeAsync(2000); + const result = await sleepPromise; + + expect(result).toEqual({ sleptFor: 2 }); + }); + + it("should reject negative sleep duration", async () => { + await expect( + sleepTool.execute({ seconds: -1 }, { logger }), + ).rejects.toThrow(); + }); + + it("should reject sleep duration over 1 hour", async () => { + await expect( + sleepTool.execute({ seconds: 3601 }, { logger }), + ).rejects.toThrow(); + }); +}); diff --git a/src/tools/system/sleep.ts b/packages/agent/src/tools/system/sleep.ts similarity index 63% rename from src/tools/system/sleep.ts rename to packages/agent/src/tools/system/sleep.ts index 915a4b6..20d98a2 100644 --- a/src/tools/system/sleep.ts +++ b/packages/agent/src/tools/system/sleep.ts @@ -1,7 +1,8 @@ -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; -import { Tool } from '../../core/types.js'; -import { sleep } from '../../utils/sleep.js'; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +import { Tool } from "../../core/types.js"; +import { sleep } from "../../utils/sleep.js"; const MAX_SLEEP_SECONDS = 3600; // 1 hour @@ -10,17 +11,17 @@ const parametersSchema = z.object({ .number() .min(0) .max(MAX_SLEEP_SECONDS) - .describe('Number of seconds to sleep (max 1 hour)'), + .describe("Number of seconds to sleep (max 1 hour)"), }); const returnsSchema = z.object({ - sleptFor: z.number().describe('Actual number of seconds slept'), + sleptFor: z.number().describe("Actual number of seconds slept"), }); export const sleepTool: Tool = { - name: 'sleep', + name: "sleep", description: - 'Pauses execution for the specified number of seconds, useful when waiting for async tools to make progress before checking on them', + "Pauses execution for the specified number of seconds, useful when waiting for async tools to make progress before checking on them", parameters: zodToJsonSchema(parametersSchema), returns: zodToJsonSchema(returnsSchema), async execute(params) { @@ -36,6 +37,6 @@ export const sleepTool: Tool = { return `sleeping for ${seconds} seconds`; }, logReturns() { - return ''; + return ""; }, }; diff --git a/src/utils/errorToString.ts b/packages/agent/src/utils/errorToString.ts similarity index 100% rename from src/utils/errorToString.ts rename to packages/agent/src/utils/errorToString.ts diff --git a/src/utils/errors.ts b/packages/agent/src/utils/errors.ts similarity index 100% rename from src/utils/errors.ts rename to packages/agent/src/utils/errors.ts diff --git a/src/utils/logger.test.ts b/packages/agent/src/utils/logger.test.ts similarity index 66% rename from src/utils/logger.test.ts rename to packages/agent/src/utils/logger.test.ts index 4319b2e..e66b058 100644 --- a/src/utils/logger.test.ts +++ b/packages/agent/src/utils/logger.test.ts @@ -1,16 +1,17 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { Logger, LogLevel } from './logger.js'; +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -describe('Logger', () => { +import { Logger, LogLevel } from "./logger.js"; + +describe("Logger", () => { let consoleSpy: { [key: string]: any }; beforeEach(() => { // Setup console spies before each test consoleSpy = { - log: vi.spyOn(console, 'log').mockImplementation(() => {}), - info: vi.spyOn(console, 'log').mockImplementation(() => {}), - warn: vi.spyOn(console, 'warn').mockImplementation(() => {}), - error: vi.spyOn(console, 'error').mockImplementation(() => {}), + log: vi.spyOn(console, "log").mockImplementation(() => {}), + info: vi.spyOn(console, "log").mockImplementation(() => {}), + warn: vi.spyOn(console, "warn").mockImplementation(() => {}), + error: vi.spyOn(console, "error").mockImplementation(() => {}), }; }); @@ -19,39 +20,39 @@ describe('Logger', () => { vi.clearAllMocks(); }); - describe('Basic logging functionality', () => { - const logger = new Logger({ name: 'TestLogger', logLevel: LogLevel.debug }); - const testMessage = 'Test message'; + describe("Basic logging functionality", () => { + const logger = new Logger({ name: "TestLogger", logLevel: LogLevel.debug }); + const testMessage = "Test message"; - it('should log debug messages', () => { + it("should log debug messages", () => { logger.debug(testMessage); expect(consoleSpy.log).toHaveBeenCalledWith( expect.stringContaining(testMessage), ); }); - it('should log verbose messages', () => { + it("should log verbose messages", () => { logger.verbose(testMessage); expect(consoleSpy.log).toHaveBeenCalledWith( expect.stringContaining(testMessage), ); }); - it('should log info messages', () => { + it("should log info messages", () => { logger.info(testMessage); expect(consoleSpy.log).toHaveBeenCalledWith( expect.stringContaining(testMessage), ); }); - it('should log warning messages', () => { + it("should log warning messages", () => { logger.warn(testMessage); expect(consoleSpy.warn).toHaveBeenCalledWith( expect.stringContaining(testMessage), ); }); - it('should log error messages', () => { + it("should log error messages", () => { logger.error(testMessage); expect(consoleSpy.error).toHaveBeenCalledWith( expect.stringContaining(testMessage), @@ -59,26 +60,26 @@ describe('Logger', () => { }); }); - describe('Nested logger functionality', () => { + describe("Nested logger functionality", () => { const parentLogger = new Logger({ - name: 'ParentLogger', + name: "ParentLogger", logLevel: LogLevel.debug, }); const childLogger = new Logger({ - name: 'ChildLogger', + name: "ChildLogger", parent: parentLogger, logLevel: LogLevel.debug, }); - const testMessage = 'Nested test message'; + const testMessage = "Nested test message"; - it('should include proper indentation for nested loggers', () => { + it("should include proper indentation for nested loggers", () => { childLogger.info(testMessage); expect(consoleSpy.log).toHaveBeenCalledWith( - expect.stringContaining(' '), // Two spaces of indentation + expect.stringContaining(" "), // Two spaces of indentation ); }); - it('should properly log messages at all levels with nested logger', () => { + it("should properly log messages at all levels with nested logger", () => { childLogger.debug(testMessage); expect(consoleSpy.log).toHaveBeenCalledWith( expect.stringContaining(testMessage), diff --git a/src/utils/logger.ts b/packages/agent/src/utils/logger.ts similarity index 91% rename from src/utils/logger.ts rename to packages/agent/src/utils/logger.ts index 539b6ec..c8cd80a 100644 --- a/src/utils/logger.ts +++ b/packages/agent/src/utils/logger.ts @@ -1,4 +1,4 @@ -import chalk, { ChalkInstance } from 'chalk'; +import chalk, { ChalkInstance } from "chalk"; export enum LogLevel { debug = 0, @@ -34,7 +34,7 @@ export const BasicLoggerStyler = { ): string => level === LogLevel.debug || level === LogLevel.verbose ? chalk.dim(prefix) - : '', + : "", }; const loggerStyle = BasicLoggerStyler; @@ -73,18 +73,17 @@ export class Logger { currentParent = currentParent.parent; } - this.prefix = ' '.repeat(offsetSpaces); + this.prefix = " ".repeat(offsetSpaces); } private toStrings(messages: unknown[]) { return messages .map((message) => - typeof message === 'object' + typeof message === "object" ? JSON.stringify(message, null, 2) - : // eslint-disable-next-line @typescript-eslint/no-base-to-string - String(message), + : String(message), ) - .join(' '); + .join(" "); } private formatMessages(level: LogLevel, messages: unknown[]): string { @@ -97,9 +96,9 @@ export class Logger { ); return formatted - .split('\n') + .split("\n") .map((line) => `${this.prefix}${prefix} ${messageColor(line)}`) - .join('\n'); + .join("\n"); } debug(...messages: unknown[]): void { diff --git a/src/utils/mockLogger.ts b/packages/agent/src/utils/mockLogger.ts similarity index 79% rename from src/utils/mockLogger.ts rename to packages/agent/src/utils/mockLogger.ts index e7bda6f..efa31ed 100644 --- a/src/utils/mockLogger.ts +++ b/packages/agent/src/utils/mockLogger.ts @@ -1,8 +1,8 @@ -import { Logger } from './logger.js'; +import { Logger } from "./logger.js"; export class MockLogger extends Logger { constructor() { - super({ name: 'mock' }); + super({ name: "mock" }); } debug(..._messages: any[]): void {} diff --git a/src/utils/sleep.ts b/packages/agent/src/utils/sleep.ts similarity index 100% rename from src/utils/sleep.ts rename to packages/agent/src/utils/sleep.ts diff --git a/src/utils/stringifyLimited.test.ts b/packages/agent/src/utils/stringifyLimited.test.ts similarity index 56% rename from src/utils/stringifyLimited.test.ts rename to packages/agent/src/utils/stringifyLimited.test.ts index b85435b..605858d 100644 --- a/src/utils/stringifyLimited.test.ts +++ b/packages/agent/src/utils/stringifyLimited.test.ts @@ -1,19 +1,20 @@ -import { describe, it, expect } from 'vitest'; -import { stringify2 } from './stringifyLimited.js'; +import { describe, it, expect } from "vitest"; -describe('stringify2', () => { - it('should stringify simple objects', () => { - const obj = { a: 1, b: 'test' }; +import { stringify2 } from "./stringifyLimited.js"; + +describe("stringify2", () => { + it("should stringify simple objects", () => { + const obj = { a: 1, b: "test" }; const result = stringify2(obj); const parsed = JSON.parse(result); - expect(parsed).toEqual({ a: '1', b: '"test"' }); + expect(parsed).toEqual({ a: "1", b: '"test"' }); }); - it('should handle nested objects', () => { + it("should handle nested objects", () => { const obj = { a: 1, b: { - c: 'test', + c: "test", d: [1, 2, 3], }, }; @@ -23,39 +24,39 @@ describe('stringify2', () => { expect(parsed.b).toBeTruthy(); }); - it('should truncate long values', () => { - const longString = 'x'.repeat(2000); + it("should truncate long values", () => { + const longString = "x".repeat(2000); const obj = { str: longString }; const result = stringify2(obj, 100); const parsed = JSON.parse(result); expect(parsed.str.length <= 100).toBeTruthy(); }); - it('should handle null and undefined', () => { + it("should handle null and undefined", () => { const obj = { nullValue: null, undefinedValue: undefined, }; const result = stringify2(obj); const parsed = JSON.parse(result); - expect(parsed.nullValue).toBe('null'); + expect(parsed.nullValue).toBe("null"); expect(parsed.undefinedValue).toBe(undefined); }); - it('should handle arrays', () => { + it("should handle arrays", () => { const obj = { - arr: [1, 'test', { nested: true }], + arr: [1, "test", { nested: true }], }; const result = stringify2(obj); const parsed = JSON.parse(result); expect(parsed.arr).toBeTruthy(); }); - it('should handle Date objects', () => { - const date = new Date('2024-01-01'); + it("should handle Date objects", () => { + const date = new Date("2024-01-01"); const obj = { date }; const result = stringify2(obj); const parsed = JSON.parse(result); - expect(parsed.date.includes('2024-01-01')).toBeTruthy(); + expect(parsed.date.includes("2024-01-01")).toBeTruthy(); }); }); diff --git a/src/utils/stringifyLimited.ts b/packages/agent/src/utils/stringifyLimited.ts similarity index 95% rename from src/utils/stringifyLimited.ts rename to packages/agent/src/utils/stringifyLimited.ts index e058f04..a76b500 100644 --- a/src/utils/stringifyLimited.ts +++ b/packages/agent/src/utils/stringifyLimited.ts @@ -8,7 +8,7 @@ export const stringify2 = ( .map(([key, val]) => [ key, val === null - ? 'null' + ? "null" : JSON.stringify(val, null, 2).slice(0, valueCharacterLimit), ]), ); diff --git a/tsconfig.json b/packages/agent/tsconfig.json similarity index 93% rename from tsconfig.json rename to packages/agent/tsconfig.json index 6dced66..fac12a9 100644 --- a/tsconfig.json +++ b/packages/agent/tsconfig.json @@ -34,6 +34,9 @@ "declaration": true, "declarationMap": true, + "incremental": true, + "tsBuildInfoFile": "dist/.tsbuildinfo", + // Advanced "skipLibCheck": true, "forceConsistentCasingInFileNames": true, diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore new file mode 100644 index 0000000..1a2a173 --- /dev/null +++ b/packages/cli/.gitignore @@ -0,0 +1,8 @@ +node_modules +dist +.env +runs +data +internal_docs +test +vitest.config.ts diff --git a/packages/cli/.npmignore b/packages/cli/.npmignore new file mode 100644 index 0000000..0be8273 --- /dev/null +++ b/packages/cli/.npmignore @@ -0,0 +1,14 @@ +.changeset +.vscode +dist/*.test.* +internal_docs +node_modules +src +.env +.gitignore +.npmignore +.prettierignore +eslint.config.js +pnpm-lock.yaml +tsconfig.json +vitest.config.js diff --git a/CHANGELOG.md b/packages/cli/CHANGELOG.md similarity index 85% rename from CHANGELOG.md rename to packages/cli/CHANGELOG.md index ba4555d..e51225a 100644 --- a/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,13 @@ # mycoder +## 0.1.3 + +### Patch Changes + +- Improved sub-agent directions, do not assume a lack of a response is an agent being done, rather look for explicit confirmation, allow for sub-agents to have optional custom working directorires, break agent framework into the mycoder-agent package +- Updated dependencies + - mycoder-agent@0.1.3 + ## 0.1.2 ### Patch Changes diff --git a/COMMIT_CONVENTION.md b/packages/cli/COMMIT_CONVENTION.md similarity index 100% rename from COMMIT_CONVENTION.md rename to packages/cli/COMMIT_CONVENTION.md diff --git a/packages/cli/LICENSE b/packages/cli/LICENSE new file mode 100644 index 0000000..87c6b31 --- /dev/null +++ b/packages/cli/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright © 2025-2025 mycoder authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 0000000..3786826 --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,127 @@ +# MyCoder CLI + +[![NPM Package][npm]][npm-url] +[![NPM Downloads][npm-downloads]][npmtrends-url] +[![CI Status][ci]][ci-url] +[![Discord][discord]][discord-url] + +## Overview + +MyCoder is a simple to install, powerful command-line AI agent that can perform arbitrary tasks with a particular focus on coding tasks. It uses the [mycoder-agent](https://www.npmjs.com/package/mycoder-agent) package to provide AI-powered automation capabilities. + +- 🤖 **AI-Powered**: Leverages Anthropic's Claude API for intelligent decision making +- 🛠️ **Extensible Tool System**: Modular architecture with various tool categories +- 🔄 **Parallel Execution**: Ability to spawn sub-agents for concurrent task processing +- 📝 **Self-Modification**: Can modify code, it was built and tested by writing itself +- 🔍 **Smart Logging**: Hierarchical, color-coded logging system for clear output +- 👤 **Human Compatible**: Uses README.md, project files and shell commands to build its own context + +Please join the MyCoder.ai discord for support: https://discord.gg/5K6TYrHGHt + +## WARNING and LIABILITY WAIVER + +This tool can do anything on your command line that you ask it to. It can delete files, install software, and even send data to remote servers. It is a powerful tool that should be used with caution. By using this tool, you agree that the authors and contributors are not responsible for any damage that may occur as a result of using this tool. + +## API Key Required + +Before using MyCoder, you must have an ANTHROPIC_API_KEY specified either: + +- As an environment variable, "export ANTHROPIC_API_KEY=[your-api-key]" or +- In a .env file in the folder you run `mycoder` from + +Get an API key from https://www.anthropic.com/api + +## Quick Start + +```bash +# Install globally (pnpm, bun, yarn also work) +npm install -g mycoder + +# Start MyCoder with a prompt +mycoder "fix all build errors and ensure the tests pass" + +# Start in interactive mode +mycoder -i + +# Read prompt from a file +mycoder --promptFile=your-prompt.txt +``` + +## CLI Options + +- `[prompt]`: Main prompt text (positional argument) +- `-i, --interactive`: Run in interactive mode, asking for prompts +- `-f, --file`: Read prompt from a specified file +- `--log`: Set log level (info, verbose, warn, error) +- `-h, --help`: Show help +- `-V, --version`: Show version + +## Example Use Cases & Prompts + +MyCoder excels at various software development tasks. Here are some example prompts: + +### Code Migration & Updates + +```bash +# Converting test framework +mycoder "Convert all Jest tests in the src/ directory to Vitest, updating any necessary configuration files and dependencies" + +# Dependency updates +mycoder "Update all dependencies to their latest versions, handle any breaking changes, and ensure all tests pass" +``` + +### Code Refactoring + +```bash +# Class refactoring +mycoder "Refactor the UserService class in src/services/UserService.ts to use the repository pattern, update all files that use this class, and ensure tests pass" + +# API modernization +mycoder "Convert all callback-based functions in the project to use async/await, update tests accordingly" +``` + +### Feature Implementation + +```bash +# CLI enhancement +mycoder "Add a new global --debug command line option that enables verbose logging throughout the application" + +# New functionality +mycoder "Create a new caching system for API responses using Redis, including configuration options and unit tests" +``` + +### Maintenance & Fixes + +```bash +# Build fixes +mycoder "Fix all TypeScript build errors and ensure all tests pass" + +# Test coverage +mycoder "Add unit tests for all untested functions in the src/utils directory, aiming for 80% coverage" +``` + +### Documentation + +```bash +# Documentation generation +mycoder "Generate comprehensive JSDoc documentation for all exported functions and update the API documentation in the docs/ directory" + +# Architecture documentation +mycoder "Analyze the current codebase and create detailed architecture documentation including component diagrams and data flow" +``` + +## Technical Requirements + +- Node.js >= 20.0.0 +- pnpm >= 10.2.1 + +[npm]: https://img.shields.io/npm/v/mycoder +[npm-downloads]: https://img.shields.io/npm/dw/mycoder +[npm]: https://img.shields.io/npm/v/mycoder +[npm-url]: https://www.npmjs.com/package/mycoder +[npm-downloads]: https://img.shields.io/npm/dw/mycoder +[npmtrends-url]: https://www.npmtrends.com/mycoder +[ci]: https://img.shields.io/github/checks-status/bhouston/mycoder/main +[ci-url]: https://github.com/bhouston/mycoder/actions +[discord]: https://img.shields.io/discord/1339025847331328000 +[discord-url]: https://discord.gg/5K6TYrHGHt diff --git a/SECURITY.md b/packages/cli/SECURITY.md similarity index 100% rename from SECURITY.md rename to packages/cli/SECURITY.md diff --git a/packages/cli/bin/cli.js b/packages/cli/bin/cli.js new file mode 100755 index 0000000..6cf1a59 --- /dev/null +++ b/packages/cli/bin/cli.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import "../dist/index.js"; diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 0000000..9eb48a8 --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,70 @@ +{ + "name": "mycoder", + "description": "A command line tool using agent that can do arbitrary tasks, including coding tasks", + "version": "0.1.3", + "type": "module", + "bin": "./bin/cli.js", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "packageManager": "pnpm@10.2.1", + "engines": { + "node": ">=18.0.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/drivecore/mycoder-monorepo.git" + }, + "homepage": "https://mycoder.ai", + "bugs": { + "url": "https://github.com/drivecore/mycoder-monorepo/issues" + }, + "scripts": { + "start": "node --no-deprecation dist/index.js", + "typecheck": "tsc --noEmit", + "build": "tsc", + "clean": "rimraf dist", + "clean:all": "rimraf dist node_modules", + "test": "vitest run", + "test:watch": "vitest", + "test:ci": "vitest --run --coverage", + "changeset": "changeset", + "version": "changeset version", + "prepublishOnly": "pnpm run clean && pnpm run build && pnpm run test" + }, + "keywords": [ + "ai", + "agent", + "mycoder", + "swe", + "swe-agent", + "claude", + "auto-coder", + "auto-gpt", + "typescript", + "openai", + "automation" + ], + "author": "Ben Houston", + "license": "MIT", + "dependencies": { + "chalk": "^5", + "dotenv": "^16", + "mycoder-agent": "workspace:*", + "semver": "^7.7.1", + "source-map-support": "^0.5", + "uuid": "^11", + "yargs": "^17", + "yargs-file-commands": "^0.0.20", + "zod": "^3", + "zod-to-json-schema": "^3" + }, + "devDependencies": { + "@types/node": "^18", + "@types/uuid": "^10", + "@types/yargs": "^17", + "rimraf": "^5", + "type-fest": "^4", + "typescript": "^5", + "vitest": "^3" + } +} diff --git a/packages/cli/src/commands/$default.ts b/packages/cli/src/commands/$default.ts new file mode 100644 index 0000000..f649d69 --- /dev/null +++ b/packages/cli/src/commands/$default.ts @@ -0,0 +1,109 @@ +import * as fs from "fs/promises"; +import { createInterface } from "readline/promises"; + +import { + toolAgent, + Logger, + getTools, + getAnthropicApiKeyError, +} from "mycoder-agent"; + +import { SharedOptions } from "../options.js"; +import { getPackageInfo } from "../utils/versionCheck.js"; + +import type { CommandModule, Argv } from "yargs"; + +interface DefaultArgs extends SharedOptions { + prompt?: string; +} + +export const command: CommandModule = { + command: "* [prompt]", + describe: "Execute a prompt or start interactive mode", + builder: (yargs: Argv): Argv => { + return yargs.positional("prompt", { + type: "string", + description: "The prompt to execute", + }) as Argv; + }, + handler: async (argv) => { + const logger = new Logger({ name: "Default" }); + const packageInfo = getPackageInfo(); + + logger.info( + `MyCoder v${packageInfo.version} - AI-powered coding assistant`, + ); + logger.warn( + "WARNING: This tool can do anything on your command line that you ask it to.", + "It can delete files, install software, and even send data to remote servers.", + "It is a powerful tool that should be used with caution.", + "By using this tool, you agree that the authors and contributors are not responsible for any damage that may occur as a result of using this tool.", + ); + try { + // Early API key check + if (!process.env.ANTHROPIC_API_KEY) { + logger.error(getAnthropicApiKeyError()); + process.exit(1); + } + + let prompt: string | undefined; + + // If promptFile is specified, read from file + if (argv.file) { + try { + prompt = await fs.readFile(argv.file, "utf-8"); + } catch (error: any) { + logger.error( + `Failed to read prompt file: ${argv.file}, ${error?.message}`, + ); + process.exit(1); + } + } + + // If interactive mode + if (argv.interactive) { + const readline = createInterface({ + input: process.stdin, + output: process.stdout, + }); + + try { + logger.info( + "Type your request below or 'help' for usage information. Use Ctrl+C to exit.", + ); + prompt = await readline.question("\n> "); + } finally { + readline.close(); + } + } else if (!prompt) { + // Use command line prompt if provided + prompt = argv.prompt; + } + + if (!prompt) { + logger.error( + "No prompt provided. Either specify a prompt, use --promptFile, or run in --interactive mode.", + ); + process.exit(1); + } + + // Add the standard suffix to all prompts + prompt += [ + "Please ask for clarifications if required or if the tasks is confusing.", + "If you need more context, don't be scared to create a sub-agent to investigate and generate report back, this can save a lot of time and prevent obvious mistakes.", + "Once the task is complete ask the user, via the userPrompt tool if the results are acceptable or if changes are needed or if there are additional follow on tasks.", + ].join("\n"); + + const tools = getTools(); + const result = await toolAgent(prompt, tools, logger); + const output = + typeof result.result === "string" + ? result.result + : JSON.stringify(result.result, null, 2); + logger.info("\n=== Result ===\n", output); + } catch (error) { + logger.error("An error occurred:", error); + process.exit(1); + } + }, +}; diff --git a/packages/cli/src/commands/tools.ts b/packages/cli/src/commands/tools.ts new file mode 100644 index 0000000..f445822 --- /dev/null +++ b/packages/cli/src/commands/tools.ts @@ -0,0 +1,88 @@ +import { getTools } from "mycoder-agent"; + +import type { CommandModule } from "yargs"; +import type { JsonSchema7Type } from "zod-to-json-schema"; + +interface ToolsArgs { + [key: string]: unknown; +} + +function formatSchema(schema: { + properties?: Record; + required?: string[]; +}) { + let output = ""; + + if (schema.properties) { + for (const [paramName, param] of Object.entries(schema.properties)) { + const required = schema.required?.includes(paramName) + ? "" + : " (optional)"; + const description = (param as any).description || ""; + output += `${paramName}${required}: ${description}\n`; + + if ((param as any).type) { + output += ` Type: ${(param as any).type}\n`; + } + if ((param as any).maxLength) { + output += ` Max Length: ${(param as any).maxLength}\n`; + } + if ((param as any).additionalProperties) { + output += ` Additional Properties: ${JSON.stringify((param as any).additionalProperties)}\n`; + } + } + } + + return output; +} + +export const command: CommandModule = { + command: "tools", + describe: "List all available tools and their capabilities", + handler: () => { + try { + const tools = getTools(); + + console.log("Available Tools:\\n"); + + for (const tool of tools) { + // Tool name and description + console.log(`${tool.name}`); + console.log("-".repeat(tool.name.length)); + console.log(`Description: ${tool.description}\\n`); + + // Parameters section + console.log("Parameters:"); + console.log( + formatSchema( + tool.parameters as { + properties?: Record; + required?: string[]; + }, + ), + ); + + // Returns section + console.log("Returns:"); + if (tool.returns) { + console.log( + formatSchema( + tool.returns as { + properties?: Record; + required?: string[]; + }, + ), + ); + } else { + console.log(" Type: any"); + console.log(" Description: Tool execution result or error\\n"); + } + + console.log(); // Add spacing between tools + } + } catch (error) { + console.error("Error listing tools:", error); + process.exit(1); + } + }, +}; diff --git a/src/index.ts b/packages/cli/src/index.ts similarity index 57% rename from src/index.ts rename to packages/cli/src/index.ts index 8069bc1..1979962 100644 --- a/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,18 +1,18 @@ -import * as dotenv from 'dotenv'; -import yargs from 'yargs'; -import { hideBin } from 'yargs/helpers'; -import { Logger, LogLevel } from './utils/logger.js'; -import { createRequire } from 'module'; -import { join } from 'path'; -import { fileURLToPath } from 'url'; -import { fileCommands } from 'yargs-file-commands'; -import { checkForUpdates } from './utils/versionCheck.js'; +import { createRequire } from "module"; +import { join } from "path"; +import { fileURLToPath } from "url"; -import sourceMapSupport from 'source-map-support'; +import * as dotenv from "dotenv"; +import { Logger, LogLevel, getTools } from "mycoder-agent"; +import sourceMapSupport from "source-map-support"; +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; +import { fileCommands } from "yargs-file-commands"; -import type { PackageJson } from 'type-fest'; -import { sharedOptions } from './options.js'; -import { getTools } from './tools/getTools.js'; +import { sharedOptions } from "./options.js"; +import { checkForUpdates } from "./utils/versionCheck.js"; + +import type { PackageJson } from "type-fest"; sourceMapSupport.install(); @@ -24,7 +24,7 @@ const nameToLogIndex = (logLevelName: string) => { const main = async () => { dotenv.config(); - const logger = new Logger({ name: 'Main' }); + const logger = new Logger({ name: "Main" }); const updateMessage = await checkForUpdates(); if (updateMessage) { @@ -34,14 +34,14 @@ const main = async () => { } // Error handling - process.on('SIGINT', () => { - logger.warn('\nGracefully shutting down...'); + process.on("SIGINT", () => { + logger.warn("\nGracefully shutting down..."); process.exit(0); }); - process.on('uncaughtException', (error) => { + process.on("uncaughtException", (error) => { logger.error( - 'Fatal error:', + "Fatal error:", error.constructor.name, error.message, error.stack, @@ -50,19 +50,19 @@ const main = async () => { }); const require = createRequire(import.meta.url); - const packageInfo = require('../package.json') as PackageJson; + const packageInfo = require("../package.json") as PackageJson; // Get the directory where commands are located const __filename = fileURLToPath(import.meta.url); - const commandsDir = join(__filename, '..', 'commands'); + const commandsDir = join(__filename, "..", "commands"); // Set up yargs with the new CLI interface await yargs(hideBin(process.argv)) .scriptName(packageInfo.name!) .version(packageInfo.version!) .options(sharedOptions) - .alias('h', 'help') - .alias('V', 'version') + .alias("h", "help") + .alias("V", "version") .middleware((argv) => { // Set up logger with the specified log level argv.logger = new Logger({ @@ -74,7 +74,7 @@ const main = async () => { .command( await fileCommands({ commandDirs: [commandsDir], - logLevel: 'info', + logLevel: "info", }), ) .strict() diff --git a/packages/cli/src/options.ts b/packages/cli/src/options.ts new file mode 100644 index 0000000..ea726a4 --- /dev/null +++ b/packages/cli/src/options.ts @@ -0,0 +1,28 @@ +import { LogLevel } from "mycoder-agent"; + +export type SharedOptions = { + readonly log: LogLevel; + readonly interactive: boolean; + readonly file?: string; +}; + +export const sharedOptions = { + log: { + type: "string", + alias: "l", + description: "Set minimum logging level", + default: "info", + choices: ["debug", "verbose", "info", "warn", "error"], + } as const, + interactive: { + type: "boolean", + alias: "i", + description: "Run in interactive mode, asking for prompts", + default: false, + } as const, + file: { + type: "string", + alias: "f", + description: "Read prompt from a file", + } as const, +}; diff --git a/src/settings/settings.ts b/packages/cli/src/settings/settings.ts similarity index 54% rename from src/settings/settings.ts rename to packages/cli/src/settings/settings.ts index 3721fa7..fba67f4 100644 --- a/src/settings/settings.ts +++ b/packages/cli/src/settings/settings.ts @@ -1,8 +1,8 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; -const settingsDir = path.join(os.homedir(), '.mycoder'); +const settingsDir = path.join(os.homedir(), ".mycoder"); export const getSettingsDir = (): string => { if (!fs.existsSync(settingsDir)) { diff --git a/packages/cli/src/utils/versionCheck.test.ts b/packages/cli/src/utils/versionCheck.test.ts new file mode 100644 index 0000000..76bfc0c --- /dev/null +++ b/packages/cli/src/utils/versionCheck.test.ts @@ -0,0 +1,196 @@ +import * as fs from "fs"; +import * as fsPromises from "fs/promises"; +import * as path from "path"; + +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; + +import { + generateUpgradeMessage, + fetchLatestVersion, + getPackageInfo, + checkForUpdates, +} from "./versionCheck.js"; + +// Mock the settings module +vi.mock("../settings/settings.js", () => ({ + getSettingsDir: vi.fn(), +})); + +// Import after mocking +// eslint-disable-next-line import/order +import { getSettingsDir } from "../settings/settings.js"; + +vi.mock("fs"); +vi.mock("fs/promises"); +vi.mock("mycoder-agent", () => ({ + Logger: vi.fn().mockImplementation(() => ({ + warn: vi.fn(), + })), + errorToString: vi.fn(), +})); + +describe("versionCheck", () => { + describe("generateUpgradeMessage", () => { + it("returns null when versions are the same", () => { + expect(generateUpgradeMessage("1.0.0", "1.0.0", "test-package")).toBe( + null, + ); + }); + + it("returns upgrade message when versions differ", () => { + const message = generateUpgradeMessage("1.0.0", "1.1.0", "test-package"); + expect(message).toContain("Update available: 1.0.0 → 1.1.0"); + expect(message).toContain("Run 'npm install -g test-package' to update"); + }); + + it("returns null when current version is higher", () => { + expect(generateUpgradeMessage("2.0.0", "1.0.0", "test-package")).toBe( + null, + ); + }); + }); + + describe("fetchLatestVersion", () => { + const mockFetch = vi.fn(); + const originalFetch = global.fetch; + + beforeEach(() => { + global.fetch = mockFetch; + }); + + afterEach(() => { + global.fetch = originalFetch; + vi.clearAllMocks(); + }); + + it("returns version when fetch succeeds", async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ version: "1.1.0" }), + }); + + const version = await fetchLatestVersion("test-package"); + expect(version).toBe("1.1.0"); + expect(mockFetch).toHaveBeenCalledWith( + "https://registry.npmjs.org/test-package/latest", + ); + }); + + it("throws error when fetch fails", async () => { + mockFetch.mockResolvedValueOnce({ + ok: false, + statusText: "Not Found", + }); + + await expect(fetchLatestVersion("test-package")).rejects.toThrow( + "Failed to fetch version info: Not Found", + ); + }); + + it("throws error when version is missing from response", async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({}), + }); + + await expect(fetchLatestVersion("test-package")).rejects.toThrow( + "Version info not found in response", + ); + }); + }); + + describe("getPackageInfo", () => { + it("returns package info from package.json", () => { + const info = getPackageInfo(); + expect(info).toHaveProperty("name"); + expect(info).toHaveProperty("version"); + expect(typeof info.name).toBe("string"); + expect(typeof info.version).toBe("string"); + }); + }); + + describe("checkForUpdates", () => { + const mockFetch = vi.fn(); + const originalFetch = global.fetch; + const mockSettingsDir = "/mock/settings/dir"; + const versionFilePath = path.join(mockSettingsDir, "lastVersionCheck"); + + beforeEach(() => { + global.fetch = mockFetch; + vi.resetAllMocks(); + // Mock getSettingsDir + (getSettingsDir as unknown as ReturnType).mockReturnValue( + mockSettingsDir, + ); + // Mock fs.existsSync + (fs.existsSync as unknown as ReturnType).mockReturnValue( + false, + ); + }); + + afterEach(() => { + global.fetch = originalFetch; + vi.clearAllMocks(); + }); + + it("returns null and initiates background check when no cached version", async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ version: "2.0.0" }), + }); + + const result = await checkForUpdates(); + expect(result).toBe(null); + + // Wait for setImmediate to complete + await new Promise((resolve) => setImmediate(resolve)); + + expect(mockFetch).toHaveBeenCalled(); + expect(fsPromises.writeFile).toHaveBeenCalledWith( + versionFilePath, + "2.0.0", + "utf8", + ); + }); + + it("returns upgrade message when cached version is newer", async () => { + (fs.existsSync as unknown as ReturnType).mockReturnValue( + true, + ); + ( + fsPromises.readFile as unknown as ReturnType + ).mockResolvedValue("2.0.0"); + + const result = await checkForUpdates(); + expect(result).toContain("Update available"); + }); + + it("handles errors gracefully during version check", async () => { + (fs.existsSync as unknown as ReturnType).mockReturnValue( + true, + ); + ( + fsPromises.readFile as unknown as ReturnType + ).mockRejectedValue(new Error("Test error")); + + const result = await checkForUpdates(); + expect(result).toBe(null); + }); + + it("handles errors gracefully during background update", async () => { + (fs.existsSync as unknown as ReturnType).mockReturnValue( + false, + ); + mockFetch.mockRejectedValue(new Error("Network error")); + + const result = await checkForUpdates(); + expect(result).toBe(null); + + // Wait for setImmediate to complete + await new Promise((resolve) => setImmediate(resolve)); + + // Verify the error was handled + expect(fsPromises.writeFile).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/utils/versionCheck.ts b/packages/cli/src/utils/versionCheck.ts similarity index 67% rename from src/utils/versionCheck.ts rename to packages/cli/src/utils/versionCheck.ts index ee844cb..2946a51 100644 --- a/src/utils/versionCheck.ts +++ b/packages/cli/src/utils/versionCheck.ts @@ -1,24 +1,26 @@ -import { Logger } from './logger.js'; -import chalk from 'chalk'; -import { createRequire } from 'module'; -import * as path from 'path'; -import type { PackageJson } from 'type-fest'; -import { getSettingsDir } from '../settings/settings.js'; -import * as fsPromises from 'fs/promises'; -import * as fs from 'fs'; -import * as semver from 'semver'; -import { errorToString } from './errorToString.js'; +import * as fs from "fs"; +import * as fsPromises from "fs/promises"; +import { createRequire } from "module"; +import * as path from "path"; + +import chalk from "chalk"; +import { Logger, errorToString } from "mycoder-agent"; +import * as semver from "semver"; + +import { getSettingsDir } from "../settings/settings.js"; + +import type { PackageJson } from "type-fest"; const require = createRequire(import.meta.url); -const logger = new Logger({ name: 'version-check' }); +const logger = new Logger({ name: "version-check" }); export function getPackageInfo(): { name: string; version: string; } { - const packageInfo = require('../../package.json') as PackageJson; + const packageInfo = require("../../package.json") as PackageJson; if (!packageInfo.name || !packageInfo.version) { - throw new Error('Unable to determine package info'); + throw new Error("Unable to determine package info"); } return { @@ -37,7 +39,7 @@ export async function fetchLatestVersion(packageName: string): Promise { const data = (await response.json()) as { version: string | undefined }; if (!data.version) { - throw new Error('Version info not found in response'); + throw new Error("Version info not found in response"); } return data.version; } @@ -59,11 +61,11 @@ export async function checkForUpdates(): Promise { const { name: packageName, version: currentVersion } = getPackageInfo(); const settingDir = getSettingsDir(); - const versionFilePath = path.join(settingDir, 'lastVersionCheck'); + const versionFilePath = path.join(settingDir, "lastVersionCheck"); if (fs.existsSync(versionFilePath)) { const lastVersionCheck = await fsPromises.readFile( versionFilePath, - 'utf8', + "utf8", ); return generateUpgradeMessage( currentVersion, @@ -74,16 +76,16 @@ export async function checkForUpdates(): Promise { fetchLatestVersion(packageName) .then(async (latestVersion) => { - return fsPromises.writeFile(versionFilePath, latestVersion, 'utf8'); + return fsPromises.writeFile(versionFilePath, latestVersion, "utf8"); }) .catch((error) => { - logger.warn('Error fetching latest version:', errorToString(error)); + logger.warn("Error fetching latest version:", errorToString(error)); }); return null; } catch (error) { // Log error but don't throw to handle gracefully - logger.warn('Error checking for updates:', errorToString(error)); + logger.warn("Error checking for updates:", errorToString(error)); return null; } } diff --git a/packages/cli/tests/cli.test.ts b/packages/cli/tests/cli.test.ts new file mode 100644 index 0000000..a2391d0 --- /dev/null +++ b/packages/cli/tests/cli.test.ts @@ -0,0 +1,20 @@ +import { execSync } from "child_process"; + +import { expect, test, describe } from "vitest"; + +import { version } from "../package.json"; + +describe("CLI", () => { + test("version command outputs correct version", () => { + const output = execSync("node ./bin/cli.js --version").toString(); + expect(output).toContain(version); + expect(output).not.toContain("AI-powered coding assistant"); + }); + + test("--help command outputs help", () => { + const output = execSync("node ./bin/cli.js --help").toString(); + expect(output).toContain("Commands:"); + expect(output).toContain("Positionals:"); + expect(output).toContain("Options:"); + }); +}); diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 0000000..204fe19 --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,48 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "node", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "typeRoots": ["./node_modules/@types"], + + "outDir": "./dist", + + // Strict Type Checking + "strict": true, + "noImplicitAny": false, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + + // Additional Checks + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + + // Module Resolution + "esModuleInterop": true, + "resolveJsonModule": true, + + // Source Map Support + "sourceMap": true, + + // Type Declaration Settings + "declaration": false, + "declarationMap": false, + + "incremental": true, + + "tsBuildInfoFile": "dist/.tsbuildinfo", + + // Advanced + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "allowJs": false, + "checkJs": false + }, + "include": ["src/**/*"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21db5e0..95d40fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,16 +7,62 @@ settings: importers: .: + dependencies: + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + devDependencies: + '@changesets/cli': + specifier: ^2.28.1 + version: 2.28.1 + '@eslint/js': + specifier: ^9 + version: 9.21.0 + '@typescript-eslint/eslint-plugin': + specifier: ^8.23.0 + version: 8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': + specifier: ^8.23.0 + version: 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + eslint: + specifier: ^9.0.0 + version: 9.21.0(jiti@2.4.2) + eslint-config-prettier: + specifier: ^9 + version: 9.1.0(eslint@9.21.0(jiti@2.4.2)) + eslint-import-resolver-typescript: + specifier: ^3.8.3 + version: 3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@2.4.2)) + eslint-plugin-import: + specifier: ^2 + version: 2.31.0(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@2.4.2)) + eslint-plugin-prettier: + specifier: ^5 + version: 5.2.3(eslint-config-prettier@9.1.0(eslint@9.21.0(jiti@2.4.2)))(eslint@9.21.0(jiti@2.4.2))(prettier@3.5.2) + eslint-plugin-promise: + specifier: ^7.2.1 + version: 7.2.1(eslint@9.21.0(jiti@2.4.2)) + eslint-plugin-unused-imports: + specifier: ^4.1.4 + version: 4.1.4(@typescript-eslint/eslint-plugin@8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2)) + prettier: + specifier: ^3.5.1 + version: 3.5.2 + typescript-eslint: + specifier: ^8.23.0 + version: 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + + packages/agent: dependencies: '@anthropic-ai/sdk': - specifier: ^0.36 - version: 0.36.3 + specifier: ^0.37 + version: 0.37.0 '@playwright/test': specifier: ^1.50.1 version: 1.50.1 '@vitest/browser': specifier: ^3.0.5 - version: 3.0.5(@types/node@18.19.75)(playwright@1.50.1)(typescript@5.7.3)(vite@5.4.14(@types/node@18.19.75))(vitest@3.0.5) + version: 3.0.6(@types/node@18.19.76)(playwright@1.50.1)(typescript@5.7.3)(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))(vitest@3.0.6) chalk: specifier: ^5 version: 5.4.1 @@ -26,6 +72,46 @@ importers: playwright: specifier: ^1.50.1 version: 1.50.1 + uuid: + specifier: ^11 + version: 11.1.0 + zod: + specifier: ^3 + version: 3.24.2 + zod-to-json-schema: + specifier: ^3 + version: 3.24.3(zod@3.24.2) + devDependencies: + '@types/node': + specifier: ^18 + version: 18.19.76 + '@types/uuid': + specifier: ^10 + version: 10.0.0 + rimraf: + specifier: ^5 + version: 5.0.10 + type-fest: + specifier: ^4 + version: 4.35.0 + typescript: + specifier: ^5 + version: 5.7.3 + vitest: + specifier: ^3 + version: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) + + packages/cli: + dependencies: + chalk: + specifier: ^5 + version: 5.4.1 + dotenv: + specifier: ^16 + version: 16.4.7 + mycoder-agent: + specifier: workspace:* + version: link:../agent semver: specifier: ^7.7.1 version: 7.7.1 @@ -34,82 +120,46 @@ importers: version: 0.5.21 uuid: specifier: ^11 - version: 11.0.5 + version: 11.1.0 yargs: specifier: ^17 version: 17.7.2 yargs-file-commands: - specifier: ^0.0.19 - version: 0.0.19(yargs@17.7.2) + specifier: ^0.0.20 + version: 0.0.20(yargs@17.7.2) zod: specifier: ^3 - version: 3.24.1 + version: 3.24.2 zod-to-json-schema: specifier: ^3 - version: 3.24.1(zod@3.24.1) + version: 3.24.3(zod@3.24.2) devDependencies: - '@changesets/cli': - specifier: ^2 - version: 2.27.12 - '@eslint/js': - specifier: ^9 - version: 9.20.0 '@types/node': specifier: ^18 - version: 18.19.75 + version: 18.19.76 '@types/uuid': specifier: ^10 version: 10.0.0 '@types/yargs': specifier: ^17 version: 17.0.33 - '@typescript-eslint/eslint-plugin': - specifier: ^8 - version: 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/parser': - specifier: ^8 - version: 8.24.0(eslint@9.20.1)(typescript@5.7.3) - eslint: - specifier: ^9 - version: 9.20.1 - eslint-config-prettier: - specifier: ^9 - version: 9.1.0(eslint@9.20.1) - eslint-plugin-import: - specifier: ^2 - version: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1) - eslint-plugin-prettier: - specifier: ^5 - version: 5.2.3(eslint-config-prettier@9.1.0(eslint@9.20.1))(eslint@9.20.1)(prettier@3.5.0) - eslint-plugin-promise: - specifier: ^7.2.1 - version: 7.2.1(eslint@9.20.1) - eslint-plugin-unused-imports: - specifier: ^4 - version: 4.1.4(@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1) - prettier: - specifier: ^3 - version: 3.5.0 rimraf: specifier: ^5 version: 5.0.10 type-fest: specifier: ^4 - version: 4.34.1 + version: 4.35.0 typescript: specifier: ^5 version: 5.7.3 - typescript-eslint: - specifier: ^8 - version: 8.24.0(eslint@9.20.1)(typescript@5.7.3) vitest: specifier: ^3 - version: 3.0.5(@types/node@18.19.75)(@vitest/browser@3.0.5)(msw@2.7.0(@types/node@18.19.75)(typescript@5.7.3)) + version: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) packages: - '@anthropic-ai/sdk@0.36.3': - resolution: {integrity: sha512-+c0mMLxL/17yFZ4P5+U6bTWiCSFZUKJddrv01ud2aFBWnTPLdRncYV76D3q1tqfnL7aCnhRtykFnoCFzvr4U3Q==} + '@anthropic-ai/sdk@0.37.0': + resolution: {integrity: sha512-tHjX2YbkUBwEgg0JZU3EFSSAQPoK4qQR/NFYa8Vtzd5UAyXzZksCw2In69Rml4R/TyHPBfRYaLK35XiOe33pjw==} '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} @@ -119,8 +169,8 @@ packages: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.26.7': - resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} + '@babel/runtime@7.26.9': + resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==} engines: {node: '>=6.9.0'} '@bundled-es-modules/cookie@2.0.1': @@ -132,30 +182,30 @@ packages: '@bundled-es-modules/tough-cookie@0.1.6': resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} - '@changesets/apply-release-plan@7.0.8': - resolution: {integrity: sha512-qjMUj4DYQ1Z6qHawsn7S71SujrExJ+nceyKKyI9iB+M5p9lCL55afuEd6uLBPRpLGWQwkwvWegDHtwHJb1UjpA==} + '@changesets/apply-release-plan@7.0.10': + resolution: {integrity: sha512-wNyeIJ3yDsVspYvHnEz1xQDq18D9ifed3lI+wxRQRK4pArUcuHgCTrHv0QRnnwjhVCQACxZ+CBih3wgOct6UXw==} - '@changesets/assemble-release-plan@6.0.5': - resolution: {integrity: sha512-IgvBWLNKZd6k4t72MBTBK3nkygi0j3t3zdC1zrfusYo0KpdsvnDjrMM9vPnTCLCMlfNs55jRL4gIMybxa64FCQ==} + '@changesets/assemble-release-plan@6.0.6': + resolution: {integrity: sha512-Frkj8hWJ1FRZiY3kzVCKzS0N5mMwWKwmv9vpam7vt8rZjLL1JMthdh6pSDVSPumHPshTTkKZ0VtNbE0cJHZZUg==} - '@changesets/changelog-git@0.2.0': - resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + '@changesets/changelog-git@0.2.1': + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - '@changesets/cli@2.27.12': - resolution: {integrity: sha512-9o3fOfHYOvBnyEn0mcahB7wzaA3P4bGJf8PNqGit5PKaMEFdsRixik+txkrJWd2VX+O6wRFXpxQL8j/1ANKE9g==} + '@changesets/cli@2.28.1': + resolution: {integrity: sha512-PiIyGRmSc6JddQJe/W1hRPjiN4VrMvb2VfQ6Uydy2punBioQrsxppyG5WafinKcW1mT0jOe/wU4k9Zy5ff21AA==} hasBin: true - '@changesets/config@3.0.5': - resolution: {integrity: sha512-QyXLSSd10GquX7hY0Mt4yQFMEeqnO5z/XLpbIr4PAkNNoQNKwDyiSrx4yd749WddusH1v3OSiA0NRAYmH/APpQ==} + '@changesets/config@3.1.1': + resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - '@changesets/get-dependents-graph@2.1.2': - resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} + '@changesets/get-dependents-graph@2.1.3': + resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} - '@changesets/get-release-plan@4.0.6': - resolution: {integrity: sha512-FHRwBkY7Eili04Y5YMOZb0ezQzKikTka4wL753vfUA5COSebt7KThqiuCN9BewE4/qFGgF/5t3AuzXx1/UAY4w==} + '@changesets/get-release-plan@4.0.8': + resolution: {integrity: sha512-MM4mq2+DQU1ZT7nqxnpveDMTkMBLnwNX44cX7NSxlXmr7f8hO6/S2MXNiXG54uf/0nYnefv0cfy4Czf/ZL/EKQ==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} @@ -166,162 +216,324 @@ packages: '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - '@changesets/parse@0.4.0': - resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + '@changesets/parse@0.4.1': + resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} - '@changesets/pre@2.0.1': - resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} + '@changesets/pre@2.0.2': + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - '@changesets/read@0.6.2': - resolution: {integrity: sha512-wjfQpJvryY3zD61p8jR87mJdyx2FIhEcdXhKUqkja87toMrP/3jtg/Yg29upN+N4Ckf525/uvV7a4tzBlpk6gg==} + '@changesets/read@0.6.3': + resolution: {integrity: sha512-9H4p/OuJ3jXEUTjaVGdQEhBdqoT2cO5Ts95JTFsQyawmKzpL8FnIeJSyhTDPW1MBRDnwZlHFEM9SpPwJDY5wIg==} - '@changesets/should-skip-package@0.1.1': - resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} '@changesets/types@4.1.0': resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - '@changesets/types@6.0.0': - resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} - '@changesets/write@0.3.2': - resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} + '@changesets/write@0.4.0': + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] os: [netbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -339,28 +551,24 @@ packages: resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.10.0': - resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.11.0': - resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==} + '@eslint/core@0.12.0': + resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.2.0': - resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + '@eslint/eslintrc@3.3.0': + resolution: {integrity: sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.20.0': - resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==} + '@eslint/js@9.21.0': + resolution: {integrity: sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.5': - resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} + '@eslint/plugin-kit@0.2.7': + resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': @@ -379,12 +587,12 @@ packages: resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.1': - resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + '@humanwhocodes/retry@0.4.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} engines: {node: '>=18.18'} - '@inquirer/confirm@5.1.5': - resolution: {integrity: sha512-ZB2Cz8KeMINUvoeDi7IrvghaVkYT2RB0Zb31EaLWOE87u276w4wnApv0SH2qWaJ3r0VSUa3BIuz7qAV2ZvsZlg==} + '@inquirer/confirm@5.1.6': + resolution: {integrity: sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -392,8 +600,8 @@ packages: '@types/node': optional: true - '@inquirer/core@10.1.6': - resolution: {integrity: sha512-Bwh/Zk6URrHwZnSSzAZAKH7YgGYi0xICIBDFOqBQoXNNAzBHw/bgXgLmChfp+GyR3PnChcTbiCTZGC6YJNJkMA==} + '@inquirer/core@10.1.7': + resolution: {integrity: sha512-AA9CQhlrt6ZgiSy6qoAigiA1izOa751ugX6ioSjqgJ+/Gd+tEN/TORk5sUYNjXuHWfW0r1n/a6ak4u/NqHHrtA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -418,9 +626,27 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -443,6 +669,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -468,98 +698,98 @@ packages: '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - '@rollup/rollup-android-arm-eabi@4.34.6': - resolution: {integrity: sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==} + '@rollup/rollup-android-arm-eabi@4.34.8': + resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.34.6': - resolution: {integrity: sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==} + '@rollup/rollup-android-arm64@4.34.8': + resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.34.6': - resolution: {integrity: sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==} + '@rollup/rollup-darwin-arm64@4.34.8': + resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.34.6': - resolution: {integrity: sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==} + '@rollup/rollup-darwin-x64@4.34.8': + resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.34.6': - resolution: {integrity: sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==} + '@rollup/rollup-freebsd-arm64@4.34.8': + resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.34.6': - resolution: {integrity: sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==} + '@rollup/rollup-freebsd-x64@4.34.8': + resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.34.6': - resolution: {integrity: sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==} + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.34.6': - resolution: {integrity: sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==} + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.34.6': - resolution: {integrity: sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==} + '@rollup/rollup-linux-arm64-gnu@4.34.8': + resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.34.6': - resolution: {integrity: sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==} + '@rollup/rollup-linux-arm64-musl@4.34.8': + resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.34.6': - resolution: {integrity: sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==} + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.34.6': - resolution: {integrity: sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==} + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.34.6': - resolution: {integrity: sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==} + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.34.6': - resolution: {integrity: sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==} + '@rollup/rollup-linux-s390x-gnu@4.34.8': + resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.34.6': - resolution: {integrity: sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==} + '@rollup/rollup-linux-x64-gnu@4.34.8': + resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.34.6': - resolution: {integrity: sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==} + '@rollup/rollup-linux-x64-musl@4.34.8': + resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.34.6': - resolution: {integrity: sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==} + '@rollup/rollup-win32-arm64-msvc@4.34.8': + resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.34.6': - resolution: {integrity: sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==} + '@rollup/rollup-win32-ia32-msvc@4.34.8': + resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.34.6': - resolution: {integrity: sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==} + '@rollup/rollup-win32-x64-msvc@4.34.8': + resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} cpu: [x64] os: [win32] @@ -582,6 +812,9 @@ packages: '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -591,14 +824,20 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node-fetch@2.6.12': resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@18.19.75': - resolution: {integrity: sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==} + '@types/node@18.19.76': + resolution: {integrity: sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==} + + '@types/node@20.17.19': + resolution: {integrity: sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==} '@types/statuses@2.0.5': resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} @@ -615,59 +854,59 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.24.0': - resolution: {integrity: sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==} + '@typescript-eslint/eslint-plugin@8.24.1': + resolution: {integrity: sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/parser@8.24.0': - resolution: {integrity: sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==} + '@typescript-eslint/parser@8.24.1': + resolution: {integrity: sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.24.0': - resolution: {integrity: sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==} + '@typescript-eslint/scope-manager@8.24.1': + resolution: {integrity: sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.24.0': - resolution: {integrity: sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==} + '@typescript-eslint/type-utils@8.24.1': + resolution: {integrity: sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/types@8.24.0': - resolution: {integrity: sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==} + '@typescript-eslint/types@8.24.1': + resolution: {integrity: sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.24.0': - resolution: {integrity: sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==} + '@typescript-eslint/typescript-estree@8.24.1': + resolution: {integrity: sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.24.0': - resolution: {integrity: sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==} + '@typescript-eslint/utils@8.24.1': + resolution: {integrity: sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.24.0': - resolution: {integrity: sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==} + '@typescript-eslint/visitor-keys@8.24.1': + resolution: {integrity: sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitest/browser@3.0.5': - resolution: {integrity: sha512-5WAWJoucuWcGYU5t0HPBY03k9uogbUEIu4pDmZHoB4Dt+6pXqzDbzEmxGjejZSitSYA3k/udYfuotKNxETVA3A==} + '@vitest/browser@3.0.6': + resolution: {integrity: sha512-FqKwCAkALZfNzGNx4YvRJa6HCWM2USWTjOdNO2egI/s6+3WkIl4xAlYISOARLJLDAI3yCXcpTtuUUF39K8TQgw==} peerDependencies: playwright: '*' safaridriver: '*' - vitest: 3.0.5 + vitest: 3.0.6 webdriverio: '*' peerDependenciesMeta: playwright: @@ -677,11 +916,11 @@ packages: webdriverio: optional: true - '@vitest/expect@3.0.5': - resolution: {integrity: sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==} + '@vitest/expect@3.0.6': + resolution: {integrity: sha512-zBduHf/ja7/QRX4HdP1DSq5XrPgdN+jzLOwaTq/0qZjYfgETNFCKf9nOAp2j3hmom3oTbczuUzrzg9Hafh7hNg==} - '@vitest/mocker@3.0.5': - resolution: {integrity: sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==} + '@vitest/mocker@3.0.6': + resolution: {integrity: sha512-KPztr4/tn7qDGZfqlSPQoF2VgJcKxnDNhmfR3VgZ6Fy1bO8T9Fc1stUiTXtqz0yG24VpD00pZP5f8EOFknjNuQ==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 @@ -691,20 +930,20 @@ packages: vite: optional: true - '@vitest/pretty-format@3.0.5': - resolution: {integrity: sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==} + '@vitest/pretty-format@3.0.6': + resolution: {integrity: sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==} - '@vitest/runner@3.0.5': - resolution: {integrity: sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==} + '@vitest/runner@3.0.6': + resolution: {integrity: sha512-JopP4m/jGoaG1+CBqubV/5VMbi7L+NQCJTu1J1Pf6YaUbk7bZtaq5CX7p+8sY64Sjn1UQ1XJparHfcvTTdu9cA==} - '@vitest/snapshot@3.0.5': - resolution: {integrity: sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==} + '@vitest/snapshot@3.0.6': + resolution: {integrity: sha512-qKSmxNQwT60kNwwJHMVwavvZsMGXWmngD023OHSgn873pV0lylK7dwBTfYP7e4URy5NiBCHHiQGA9DHkYkqRqg==} - '@vitest/spy@3.0.5': - resolution: {integrity: sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==} + '@vitest/spy@3.0.6': + resolution: {integrity: sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q==} - '@vitest/utils@3.0.5': - resolution: {integrity: sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==} + '@vitest/utils@3.0.6': + resolution: {integrity: sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==} abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} @@ -831,8 +1070,8 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - call-bind-apply-helpers@1.0.1: - resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} call-bind@1.0.8: @@ -847,8 +1086,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - chai@5.1.2: - resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} chalk@4.1.2: @@ -889,6 +1128,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -984,6 +1226,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} @@ -1011,16 +1257,22 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} es-to-primitive@1.3.0: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: @@ -1040,6 +1292,19 @@ packages: eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-typescript@3.8.3: + resolution: {integrity: sha512-A0bu4Ks2QqDWNpeEgTQMPTngaMhuDu4yv6xpftBMAf+1ziXnpx+eSR1WRfoPTe2BAiAjHFZ7kSNx1fvr5g5pmQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + eslint-module-utils@2.12.0: resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} engines: {node: '>=4'} @@ -1112,8 +1377,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.20.1: - resolution: {integrity: sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==} + eslint@9.21.0: + resolution: {integrity: sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1184,6 +1449,14 @@ packages: fastq@1.19.0: resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==} + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1204,8 +1477,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.3.2: - resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} @@ -1218,8 +1491,8 @@ packages: form-data-encoder@1.7.2: resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} - form-data@4.0.1: - resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} engines: {node: '>= 6'} formdata-node@4.4.1: @@ -1258,8 +1531,8 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.2.7: - resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} get-proto@1.0.1: @@ -1270,6 +1543,9 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1282,6 +1558,11 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true + glob@11.0.1: + resolution: {integrity: sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==} + engines: {node: 20 || >=22} + hasBin: true + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1338,8 +1619,9 @@ packages: headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} - human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + human-id@4.1.1: + resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} + hasBin: true humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} @@ -1380,6 +1662,9 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} + is-bun-module@1.3.0: + resolution: {integrity: sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -1484,6 +1769,14 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.1.0: + resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} + engines: {node: 20 || >=22} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1538,6 +1831,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.0.2: + resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} + engines: {node: 20 || >=22} + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -1565,6 +1862,10 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1583,15 +1884,15 @@ packages: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} - mrmime@2.0.0: - resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msw@2.7.0: - resolution: {integrity: sha512-BIodwZ19RWfCbYTxWTUfTXc+sg4OwjCAgxU1ZsgmggX/7S3LdUifsbUPJs61j0rWb19CZRGY5if77duhc0uXzw==} + msw@2.7.3: + resolution: {integrity: sha512-+mycXv8l2fEAjFZ5sjrtjJDmm2ceKGjrNbBr1durRg6VkU9fNUE/gsmQ51hWbHqs+l35W1iM+ZsmOD9Fd6lspw==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -1720,6 +2021,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -1727,8 +2032,8 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@2.0.2: - resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} @@ -1741,6 +2046,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -1759,8 +2068,8 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss@8.5.2: - resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -1776,8 +2085,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.5.0: - resolution: {integrity: sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==} + prettier@3.5.2: + resolution: {integrity: sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==} engines: {node: '>=14'} hasBin: true @@ -1831,6 +2140,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} @@ -1844,8 +2156,13 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rollup@4.34.6: - resolution: {integrity: sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==} + rimraf@6.0.1: + resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + engines: {node: 20 || >=22} + hasBin: true + + rollup@4.34.8: + resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1919,8 +2236,8 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sirv@3.0.0: - resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} engines: {node: '>=18'} slash@3.0.0: @@ -1944,6 +2261,9 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stable-hash@0.0.4: + resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2005,16 +2325,29 @@ packages: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} + terser@5.39.0: + resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} + engines: {node: '>=10'} + hasBin: true + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.12: + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + engines: {node: '>=12.0.0'} + tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2058,6 +2391,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.19.3: + resolution: {integrity: sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==} + engines: {node: '>=18.0.0'} + hasBin: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -2066,8 +2404,8 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - type-fest@4.34.1: - resolution: {integrity: sha512-6kSc32kT0rbwxD6QL1CYe8IqdzN/J/ILMrNK+HMQCKH3insCDRY/3ITb0vcBss0a3t72fzh2YSzj8ko1HgwT3g==} + type-fest@4.35.0: + resolution: {integrity: sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==} engines: {node: '>=16'} typed-array-buffer@1.0.3: @@ -2086,8 +2424,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.24.0: - resolution: {integrity: sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ==} + typescript-eslint@8.24.1: + resolution: {integrity: sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2105,6 +2443,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -2119,31 +2460,36 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - uuid@11.0.5: - resolution: {integrity: sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==} + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true - vite-node@3.0.5: - resolution: {integrity: sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==} + vite-node@3.0.6: + resolution: {integrity: sha512-s51RzrTkXKJrhNbUzQRsarjmAae7VmMPAsRT7lppVpIg6mK3zGthP9Hgz0YQQKuNcF+Ii7DfYk3Fxz40jRmePw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@5.4.14: - resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@6.1.1: + resolution: {integrity: sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.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: @@ -2158,17 +2504,21 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true - vitest@3.0.5: - resolution: {integrity: sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==} + vitest@3.0.6: + resolution: {integrity: sha512-/iL1Sc5VeDZKPDe58oGK4HUFLhw6b5XdY1MYawjuSaDA4sEfYlY9HnS6aCEG26fX+MgUi7MwlduTBHHAI/OvMA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.0.5 - '@vitest/ui': 3.0.5 + '@vitest/browser': 3.0.6 + '@vitest/ui': 3.0.6 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -2239,8 +2589,8 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + ws@8.18.1: + resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2255,8 +2605,13 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yargs-file-commands@0.0.19: - resolution: {integrity: sha512-uEHidtVw1HPp/ZFYK+8EnJ2eKVIGMxawWP761heBU2AtOJSg4VGvKLsGUGYaMuWI/UcyFK8HUUIsH6aM0mEqmQ==} + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + engines: {node: '>= 14'} + hasBin: true + + yargs-file-commands@0.0.20: + resolution: {integrity: sha512-biqQIob2T/UbV3HUUPv3qizboMIx7N+Ptcz7qd1lBz16w48LVyfsh8UDC7hqTWktPvMcPLwJ+9Gg6OAqJQF8ug==} engines: {node: '>=20.0.0'} peerDependencies: yargs: ^17 @@ -2277,19 +2632,19 @@ packages: resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} engines: {node: '>=18'} - zod-to-json-schema@3.24.1: - resolution: {integrity: sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==} + zod-to-json-schema@3.24.3: + resolution: {integrity: sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==} peerDependencies: zod: ^3.24.1 - zod@3.24.1: - resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + zod@3.24.2: + resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} snapshots: - '@anthropic-ai/sdk@0.36.3': + '@anthropic-ai/sdk@0.37.0': dependencies: - '@types/node': 18.19.75 + '@types/node': 18.19.76 '@types/node-fetch': 2.6.12 abort-controller: 3.0.0 agentkeepalive: 4.6.0 @@ -2307,7 +2662,7 @@ snapshots: '@babel/helper-validator-identifier@7.25.9': {} - '@babel/runtime@7.26.7': + '@babel/runtime@7.26.9': dependencies: regenerator-runtime: 0.14.1 @@ -2324,13 +2679,13 @@ snapshots: '@types/tough-cookie': 4.0.5 tough-cookie: 4.1.4 - '@changesets/apply-release-plan@7.0.8': + '@changesets/apply-release-plan@7.0.10': dependencies: - '@changesets/config': 3.0.5 + '@changesets/config': 3.1.1 '@changesets/get-version-range-type': 0.4.0 '@changesets/git': 3.0.2 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 detect-indent: 6.1.0 fs-extra: 7.0.1 @@ -2340,35 +2695,35 @@ snapshots: resolve-from: 5.0.0 semver: 7.7.1 - '@changesets/assemble-release-plan@6.0.5': + '@changesets/assemble-release-plan@6.0.6': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 semver: 7.7.1 - '@changesets/changelog-git@0.2.0': + '@changesets/changelog-git@0.2.1': dependencies: - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 - '@changesets/cli@2.27.12': + '@changesets/cli@2.28.1': dependencies: - '@changesets/apply-release-plan': 7.0.8 - '@changesets/assemble-release-plan': 6.0.5 - '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.5 + '@changesets/apply-release-plan': 7.0.10 + '@changesets/assemble-release-plan': 6.0.6 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.1 '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/get-release-plan': 4.0.6 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/get-release-plan': 4.0.8 '@changesets/git': 3.0.2 '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.1 - '@changesets/read': 0.6.2 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 - '@changesets/write': 0.3.2 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.3 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 @@ -2384,12 +2739,12 @@ snapshots: spawndamnit: 3.0.1 term-size: 2.2.1 - '@changesets/config@3.0.5': + '@changesets/config@3.1.1': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 + '@changesets/get-dependents-graph': 2.1.3 '@changesets/logger': 0.1.1 - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 micromatch: 4.0.8 @@ -2398,20 +2753,20 @@ snapshots: dependencies: extendable-error: 0.1.7 - '@changesets/get-dependents-graph@2.1.2': + '@changesets/get-dependents-graph@2.1.3': dependencies: - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 picocolors: 1.1.1 semver: 7.7.1 - '@changesets/get-release-plan@4.0.6': + '@changesets/get-release-plan@4.0.8': dependencies: - '@changesets/assemble-release-plan': 6.0.5 - '@changesets/config': 3.0.5 - '@changesets/pre': 2.0.1 - '@changesets/read': 0.6.2 - '@changesets/types': 6.0.0 + '@changesets/assemble-release-plan': 6.0.6 + '@changesets/config': 3.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.3 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 '@changesets/get-version-range-type@0.4.0': {} @@ -2428,116 +2783,197 @@ snapshots: dependencies: picocolors: 1.1.1 - '@changesets/parse@0.4.0': + '@changesets/parse@0.4.1': dependencies: - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 js-yaml: 3.14.1 - '@changesets/pre@2.0.1': + '@changesets/pre@2.0.2': dependencies: '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - '@changesets/read@0.6.2': + '@changesets/read@0.6.3': dependencies: '@changesets/git': 3.0.2 '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.0 - '@changesets/types': 6.0.0 + '@changesets/parse': 0.4.1 + '@changesets/types': 6.1.0 fs-extra: 7.0.1 p-filter: 2.1.0 picocolors: 1.1.1 - '@changesets/should-skip-package@0.1.1': + '@changesets/should-skip-package@0.1.2': dependencies: - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 '@changesets/types@4.1.0': {} - '@changesets/types@6.0.0': {} + '@changesets/types@6.1.0': {} - '@changesets/write@0.3.2': + '@changesets/write@0.4.0': dependencies: - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 fs-extra: 7.0.1 - human-id: 1.0.2 + human-id: 4.1.1 prettier: 2.8.8 - '@esbuild/aix-ppc64@0.21.5': + '@esbuild/aix-ppc64@0.24.2': + optional: true + + '@esbuild/aix-ppc64@0.25.0': + optional: true + + '@esbuild/android-arm64@0.24.2': + optional: true + + '@esbuild/android-arm64@0.25.0': + optional: true + + '@esbuild/android-arm@0.24.2': + optional: true + + '@esbuild/android-arm@0.25.0': + optional: true + + '@esbuild/android-x64@0.24.2': + optional: true + + '@esbuild/android-x64@0.25.0': + optional: true + + '@esbuild/darwin-arm64@0.24.2': + optional: true + + '@esbuild/darwin-arm64@0.25.0': + optional: true + + '@esbuild/darwin-x64@0.24.2': + optional: true + + '@esbuild/darwin-x64@0.25.0': + optional: true + + '@esbuild/freebsd-arm64@0.24.2': + optional: true + + '@esbuild/freebsd-arm64@0.25.0': + optional: true + + '@esbuild/freebsd-x64@0.24.2': + optional: true + + '@esbuild/freebsd-x64@0.25.0': + optional: true + + '@esbuild/linux-arm64@0.24.2': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/linux-arm64@0.25.0': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/linux-arm@0.24.2': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/linux-arm@0.25.0': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/linux-ia32@0.24.2': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/linux-ia32@0.25.0': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/linux-loong64@0.24.2': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/linux-loong64@0.25.0': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-mips64el@0.24.2': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-mips64el@0.25.0': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ppc64@0.24.2': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-ppc64@0.25.0': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-riscv64@0.24.2': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-riscv64@0.25.0': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-s390x@0.24.2': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.25.0': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.24.2': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/linux-x64@0.25.0': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/netbsd-arm64@0.24.2': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/netbsd-arm64@0.25.0': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/netbsd-x64@0.24.2': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/netbsd-x64@0.25.0': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/openbsd-arm64@0.24.2': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.20.1)': + '@esbuild/openbsd-arm64@0.25.0': + optional: true + + '@esbuild/openbsd-x64@0.24.2': + optional: true + + '@esbuild/openbsd-x64@0.25.0': + optional: true + + '@esbuild/sunos-x64@0.24.2': + optional: true + + '@esbuild/sunos-x64@0.25.0': + optional: true + + '@esbuild/win32-arm64@0.24.2': + optional: true + + '@esbuild/win32-arm64@0.25.0': + optional: true + + '@esbuild/win32-ia32@0.24.2': + optional: true + + '@esbuild/win32-ia32@0.25.0': + optional: true + + '@esbuild/win32-x64@0.24.2': + optional: true + + '@esbuild/win32-x64@0.25.0': + optional: true + + '@eslint-community/eslint-utils@4.4.1(eslint@9.21.0(jiti@2.4.2))': dependencies: - eslint: 9.20.1 + eslint: 9.21.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -2550,15 +2986,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/core@0.10.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/core@0.11.0': + '@eslint/core@0.12.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.2.0': + '@eslint/eslintrc@3.3.0': dependencies: ajv: 6.12.6 debug: 4.4.0 @@ -2572,13 +3004,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.20.0': {} + '@eslint/js@9.21.0': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.5': + '@eslint/plugin-kit@0.2.7': dependencies: - '@eslint/core': 0.10.0 + '@eslint/core': 0.12.0 levn: 0.4.1 '@humanfs/core@0.19.1': {} @@ -2592,19 +3024,19 @@ snapshots: '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.1': {} + '@humanwhocodes/retry@0.4.2': {} - '@inquirer/confirm@5.1.5(@types/node@18.19.75)': + '@inquirer/confirm@5.1.6(@types/node@18.19.76)': dependencies: - '@inquirer/core': 10.1.6(@types/node@18.19.75) - '@inquirer/type': 3.0.4(@types/node@18.19.75) + '@inquirer/core': 10.1.7(@types/node@18.19.76) + '@inquirer/type': 3.0.4(@types/node@18.19.76) optionalDependencies: - '@types/node': 18.19.75 + '@types/node': 18.19.76 - '@inquirer/core@10.1.6(@types/node@18.19.75)': + '@inquirer/core@10.1.7(@types/node@18.19.76)': dependencies: '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@18.19.75) + '@inquirer/type': 3.0.4(@types/node@18.19.76) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -2612,13 +3044,13 @@ snapshots: wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 18.19.75 + '@types/node': 18.19.76 '@inquirer/figures@1.0.10': {} - '@inquirer/type@3.0.4(@types/node@18.19.75)': + '@inquirer/type@3.0.4(@types/node@18.19.76)': optionalDependencies: - '@types/node': 18.19.75 + '@types/node': 18.19.76 '@isaacs/cliui@8.0.2': dependencies: @@ -2629,18 +3061,43 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + optional: true + + '@jridgewell/resolve-uri@3.1.2': + optional: true + + '@jridgewell/set-array@1.2.1': + optional: true + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + optional: true + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + optional: true + '@manypkg/find-root@1.1.0': dependencies: - '@babel/runtime': 7.26.7 + '@babel/runtime': 7.26.9 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 '@manypkg/get-packages@1.1.3': dependencies: - '@babel/runtime': 7.26.7 + '@babel/runtime': 7.26.9 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -2668,6 +3125,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.0 + '@nolyfill/is-core-module@1.0.39': {} + '@open-draft/deferred-promise@2.2.0': {} '@open-draft/logger@0.3.0': @@ -2688,61 +3147,61 @@ snapshots: '@polka/url@1.0.0-next.28': {} - '@rollup/rollup-android-arm-eabi@4.34.6': + '@rollup/rollup-android-arm-eabi@4.34.8': optional: true - '@rollup/rollup-android-arm64@4.34.6': + '@rollup/rollup-android-arm64@4.34.8': optional: true - '@rollup/rollup-darwin-arm64@4.34.6': + '@rollup/rollup-darwin-arm64@4.34.8': optional: true - '@rollup/rollup-darwin-x64@4.34.6': + '@rollup/rollup-darwin-x64@4.34.8': optional: true - '@rollup/rollup-freebsd-arm64@4.34.6': + '@rollup/rollup-freebsd-arm64@4.34.8': optional: true - '@rollup/rollup-freebsd-x64@4.34.6': + '@rollup/rollup-freebsd-x64@4.34.8': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.34.6': + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.34.6': + '@rollup/rollup-linux-arm-musleabihf@4.34.8': optional: true - '@rollup/rollup-linux-arm64-gnu@4.34.6': + '@rollup/rollup-linux-arm64-gnu@4.34.8': optional: true - '@rollup/rollup-linux-arm64-musl@4.34.6': + '@rollup/rollup-linux-arm64-musl@4.34.8': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.34.6': + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.34.6': + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.34.6': + '@rollup/rollup-linux-riscv64-gnu@4.34.8': optional: true - '@rollup/rollup-linux-s390x-gnu@4.34.6': + '@rollup/rollup-linux-s390x-gnu@4.34.8': optional: true - '@rollup/rollup-linux-x64-gnu@4.34.6': + '@rollup/rollup-linux-x64-gnu@4.34.8': optional: true - '@rollup/rollup-linux-x64-musl@4.34.6': + '@rollup/rollup-linux-x64-musl@4.34.8': optional: true - '@rollup/rollup-win32-arm64-msvc@4.34.6': + '@rollup/rollup-win32-arm64-msvc@4.34.8': optional: true - '@rollup/rollup-win32-ia32-msvc@4.34.6': + '@rollup/rollup-win32-ia32-msvc@4.34.8': optional: true - '@rollup/rollup-win32-x64-msvc@4.34.6': + '@rollup/rollup-win32-x64-msvc@4.34.8': optional: true '@rtsao/scc@1.1.0': {} @@ -2750,7 +3209,7 @@ snapshots: '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.26.2 - '@babel/runtime': 7.26.7 + '@babel/runtime': 7.26.9 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -2766,23 +3225,35 @@ snapshots: '@types/cookie@0.6.0': {} + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + optional: true + '@types/estree@1.0.6': {} '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} + '@types/ms@2.1.0': + optional: true + '@types/node-fetch@2.6.12': dependencies: - '@types/node': 18.19.75 - form-data: 4.0.1 + '@types/node': 20.17.19 + form-data: 4.0.2 '@types/node@12.20.55': {} - '@types/node@18.19.75': + '@types/node@18.19.76': dependencies: undici-types: 5.26.5 + '@types/node@20.17.19': + dependencies: + undici-types: 6.19.8 + '@types/statuses@2.0.5': {} '@types/tough-cookie@4.0.5': {} @@ -2795,15 +3266,15 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.24.0 - '@typescript-eslint/type-utils': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.24.0 - eslint: 9.20.1 + '@typescript-eslint/parser': 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.24.1 + '@typescript-eslint/type-utils': 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.24.1 + eslint: 9.21.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -2812,40 +3283,40 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: - '@typescript-eslint/scope-manager': 8.24.0 - '@typescript-eslint/types': 8.24.0 - '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.24.0 + '@typescript-eslint/scope-manager': 8.24.1 + '@typescript-eslint/types': 8.24.1 + '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.24.1 debug: 4.4.0 - eslint: 9.20.1 + eslint: 9.21.0(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.24.0': + '@typescript-eslint/scope-manager@8.24.1': dependencies: - '@typescript-eslint/types': 8.24.0 - '@typescript-eslint/visitor-keys': 8.24.0 + '@typescript-eslint/types': 8.24.1 + '@typescript-eslint/visitor-keys': 8.24.1 - '@typescript-eslint/type-utils@8.24.0(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.7.3) + '@typescript-eslint/utils': 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) debug: 4.4.0 - eslint: 9.20.1 + eslint: 9.21.0(jiti@2.4.2) ts-api-utils: 2.0.1(typescript@5.7.3) typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.24.0': {} + '@typescript-eslint/types@8.24.1': {} - '@typescript-eslint/typescript-estree@8.24.0(typescript@5.7.3)': + '@typescript-eslint/typescript-estree@8.24.1(typescript@5.7.3)': dependencies: - '@typescript-eslint/types': 8.24.0 - '@typescript-eslint/visitor-keys': 8.24.0 + '@typescript-eslint/types': 8.24.1 + '@typescript-eslint/visitor-keys': 8.24.1 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -2856,34 +3327,34 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.24.0(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/utils@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) - '@typescript-eslint/scope-manager': 8.24.0 - '@typescript-eslint/types': 8.24.0 - '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) - eslint: 9.20.1 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.24.1 + '@typescript-eslint/types': 8.24.1 + '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.7.3) + eslint: 9.21.0(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.24.0': + '@typescript-eslint/visitor-keys@8.24.1': dependencies: - '@typescript-eslint/types': 8.24.0 + '@typescript-eslint/types': 8.24.1 eslint-visitor-keys: 4.2.0 - '@vitest/browser@3.0.5(@types/node@18.19.75)(playwright@1.50.1)(typescript@5.7.3)(vite@5.4.14(@types/node@18.19.75))(vitest@3.0.5)': + '@vitest/browser@3.0.6(@types/node@18.19.76)(playwright@1.50.1)(typescript@5.7.3)(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))(vitest@3.0.6)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) - '@vitest/mocker': 3.0.5(msw@2.7.0(@types/node@18.19.75)(typescript@5.7.3))(vite@5.4.14(@types/node@18.19.75)) - '@vitest/utils': 3.0.5 + '@vitest/mocker': 3.0.6(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)) + '@vitest/utils': 3.0.6 magic-string: 0.30.17 - msw: 2.7.0(@types/node@18.19.75)(typescript@5.7.3) - sirv: 3.0.0 + msw: 2.7.3(@types/node@18.19.76)(typescript@5.7.3) + sirv: 3.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.5(@types/node@18.19.75)(@vitest/browser@3.0.5)(msw@2.7.0(@types/node@18.19.75)(typescript@5.7.3)) - ws: 8.18.0 + vitest: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) + ws: 8.18.1 optionalDependencies: playwright: 1.50.1 transitivePeerDependencies: @@ -2893,44 +3364,44 @@ snapshots: - utf-8-validate - vite - '@vitest/expect@3.0.5': + '@vitest/expect@3.0.6': dependencies: - '@vitest/spy': 3.0.5 - '@vitest/utils': 3.0.5 - chai: 5.1.2 + '@vitest/spy': 3.0.6 + '@vitest/utils': 3.0.6 + chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.5(msw@2.7.0(@types/node@18.19.75)(typescript@5.7.3))(vite@5.4.14(@types/node@18.19.75))': + '@vitest/mocker@3.0.6(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))': dependencies: - '@vitest/spy': 3.0.5 + '@vitest/spy': 3.0.6 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.7.0(@types/node@18.19.75)(typescript@5.7.3) - vite: 5.4.14(@types/node@18.19.75) + msw: 2.7.3(@types/node@18.19.76)(typescript@5.7.3) + vite: 6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) - '@vitest/pretty-format@3.0.5': + '@vitest/pretty-format@3.0.6': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.0.5': + '@vitest/runner@3.0.6': dependencies: - '@vitest/utils': 3.0.5 - pathe: 2.0.2 + '@vitest/utils': 3.0.6 + pathe: 2.0.3 - '@vitest/snapshot@3.0.5': + '@vitest/snapshot@3.0.6': dependencies: - '@vitest/pretty-format': 3.0.5 + '@vitest/pretty-format': 3.0.6 magic-string: 0.30.17 - pathe: 2.0.2 + pathe: 2.0.3 - '@vitest/spy@3.0.5': + '@vitest/spy@3.0.6': dependencies: tinyspy: 3.0.2 - '@vitest/utils@3.0.5': + '@vitest/utils@3.0.6': dependencies: - '@vitest/pretty-format': 3.0.5 + '@vitest/pretty-format': 3.0.6 loupe: 3.1.3 tinyrainbow: 2.0.0 @@ -2994,7 +3465,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-object-atoms: 1.1.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 is-string: 1.1.1 array-union@2.1.0: {} @@ -3006,21 +3477,21 @@ snapshots: es-abstract: 1.23.9 es-errors: 1.3.0 es-object-atoms: 1.1.1 - es-shim-unscopables: 1.0.2 + es-shim-unscopables: 1.1.0 array.prototype.flat@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 - es-shim-unscopables: 1.0.2 + es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 - es-shim-unscopables: 1.0.2 + es-shim-unscopables: 1.1.0 arraybuffer.prototype.slice@1.0.4: dependencies: @@ -3029,7 +3500,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 assertion-error@2.0.1: {} @@ -3065,26 +3536,26 @@ snapshots: cac@6.7.14: {} - call-bind-apply-helpers@1.0.1: + 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.1 + call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 set-function-length: 1.2.2 call-bound@1.0.3: dependencies: - call-bind-apply-helpers: 1.0.1 - get-intrinsic: 1.2.7 + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 callsites@3.1.0: {} - chai@5.1.2: + chai@5.2.0: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 @@ -3123,6 +3594,9 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@2.20.3: + optional: true + concat-map@0.0.1: {} cookie@0.7.2: {} @@ -3195,7 +3669,7 @@ snapshots: dunder-proto@1.0.1: dependencies: - call-bind-apply-helpers: 1.0.1 + call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 @@ -3205,6 +3679,11 @@ snapshots: emoji-regex@9.2.2: {} + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 @@ -3226,7 +3705,7 @@ snapshots: es-set-tostringtag: 2.1.0 es-to-primitive: 1.3.0 function.prototype.name: 1.1.8 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 get-proto: 1.0.1 get-symbol-description: 1.1.0 globalthis: 1.0.4 @@ -3277,11 +3756,11 @@ snapshots: es-set-tostringtag@2.1.0: dependencies: es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 - es-shim-unscopables@1.0.2: + es-shim-unscopables@1.1.0: dependencies: hasown: 2.0.2 @@ -3291,39 +3770,70 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.21.5: + esbuild@0.24.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + + esbuild@0.25.0: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + optional: true escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-config-prettier@9.1.0(eslint@9.20.1): + eslint-config-prettier@9.1.0(eslint@9.21.0(jiti@2.4.2)): dependencies: - eslint: 9.20.1 + eslint: 9.21.0(jiti@2.4.2) eslint-import-resolver-node@0.3.9: dependencies: @@ -3333,17 +3843,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1): + eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@2.4.2)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.0 + enhanced-resolve: 5.18.1 + eslint: 9.21.0(jiti@2.4.2) + get-tsconfig: 4.10.0 + is-bun-module: 1.3.0 + stable-hash: 0.0.4 + tinyglobby: 0.2.12 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@2.4.2)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - eslint: 9.20.1 + '@typescript-eslint/parser': 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + eslint: 9.21.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@9.21.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -3352,9 +3878,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.20.1 + eslint: 9.21.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@9.21.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -3366,31 +3892,31 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-prettier@5.2.3(eslint-config-prettier@9.1.0(eslint@9.20.1))(eslint@9.20.1)(prettier@3.5.0): + eslint-plugin-prettier@5.2.3(eslint-config-prettier@9.1.0(eslint@9.21.0(jiti@2.4.2)))(eslint@9.21.0(jiti@2.4.2))(prettier@3.5.2): dependencies: - eslint: 9.20.1 - prettier: 3.5.0 + eslint: 9.21.0(jiti@2.4.2) + prettier: 3.5.2 prettier-linter-helpers: 1.0.0 synckit: 0.9.2 optionalDependencies: - eslint-config-prettier: 9.1.0(eslint@9.20.1) + eslint-config-prettier: 9.1.0(eslint@9.21.0(jiti@2.4.2)) - eslint-plugin-promise@7.2.1(eslint@9.20.1): + eslint-plugin-promise@7.2.1(eslint@9.21.0(jiti@2.4.2)): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) - eslint: 9.20.1 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@2.4.2)) + eslint: 9.21.0(jiti@2.4.2) - eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1): + eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2)): dependencies: - eslint: 9.20.1 + eslint: 9.21.0(jiti@2.4.2) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) eslint-scope@8.2.0: dependencies: @@ -3401,18 +3927,18 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.20.1: + eslint@9.21.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.2 - '@eslint/core': 0.11.0 - '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.20.0 - '@eslint/plugin-kit': 0.2.5 + '@eslint/core': 0.12.0 + '@eslint/eslintrc': 3.3.0 + '@eslint/js': 9.21.0 + '@eslint/plugin-kit': 0.2.7 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 + '@humanwhocodes/retry': 0.4.2 '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 ajv: 6.12.6 @@ -3437,6 +3963,8 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 2.4.2 transitivePeerDependencies: - supports-color @@ -3496,6 +4024,10 @@ snapshots: dependencies: reusify: 1.0.4 + fdir@6.4.3(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -3516,10 +4048,10 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.3.2 + flatted: 3.3.3 keyv: 4.5.4 - flatted@3.3.2: {} + flatted@3.3.3: {} for-each@0.3.5: dependencies: @@ -3532,10 +4064,11 @@ snapshots: form-data-encoder@1.7.2: {} - form-data@4.0.1: + form-data@4.0.2: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 mime-types: 2.1.35 formdata-node@4.4.1: @@ -3576,9 +4109,9 @@ snapshots: get-caller-file@2.0.5: {} - get-intrinsic@1.2.7: + get-intrinsic@1.3.0: dependencies: - call-bind-apply-helpers: 1.0.1 + call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 @@ -3598,7 +4131,11 @@ snapshots: dependencies: call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 + + get-tsconfig@4.10.0: + dependencies: + resolve-pkg-maps: 1.0.0 glob-parent@5.1.2: dependencies: @@ -3617,6 +4154,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@11.0.1: + dependencies: + foreground-child: 3.3.0 + jackspeak: 4.1.0 + minimatch: 10.0.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + globals@14.0.0: {} globalthis@1.0.4: @@ -3665,7 +4211,7 @@ snapshots: headers-polyfill@4.0.3: {} - human-id@1.0.2: {} + human-id@4.1.1: {} humanize-ms@1.2.1: dependencies: @@ -3694,7 +4240,7 @@ snapshots: dependencies: call-bind: 1.0.8 call-bound: 1.0.3 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 is-async-function@2.1.1: dependencies: @@ -3713,6 +4259,10 @@ snapshots: call-bound: 1.0.3 has-tostringtag: 1.0.2 + is-bun-module@1.3.0: + dependencies: + semver: 7.7.1 + is-callable@1.2.7: {} is-core-module@2.16.1: @@ -3722,7 +4272,7 @@ snapshots: is-data-view@1.0.2: dependencies: call-bound: 1.0.3 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 is-typed-array: 1.1.15 is-date-object@1.1.0: @@ -3801,7 +4351,7 @@ snapshots: is-weakset@2.0.4: dependencies: call-bound: 1.0.3 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 is-windows@1.0.2: {} @@ -3815,6 +4365,13 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@4.1.0: + dependencies: + '@isaacs/cliui': 8.0.2 + + jiti@2.4.2: + optional: true + js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -3865,6 +4422,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.0.2: {} + lz-string@1.5.0: {} magic-string@0.30.17: @@ -3886,6 +4445,10 @@ snapshots: dependencies: mime-db: 1.52.0 + minimatch@10.0.1: + dependencies: + brace-expansion: 2.0.1 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -3900,16 +4463,16 @@ snapshots: mri@1.2.0: {} - mrmime@2.0.0: {} + mrmime@2.0.1: {} ms@2.1.3: {} - msw@2.7.0(@types/node@18.19.75)(typescript@5.7.3): + msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3): dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 5.1.5(@types/node@18.19.75) + '@inquirer/confirm': 5.1.6(@types/node@18.19.76) '@mswjs/interceptors': 0.37.6 '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 @@ -3922,7 +4485,7 @@ snapshots: path-to-regexp: 6.3.0 picocolors: 1.1.1 strict-event-emitter: 0.5.1 - type-fest: 4.34.1 + type-fest: 4.35.0 yargs: 17.7.2 optionalDependencies: typescript: 5.7.3 @@ -3991,7 +4554,7 @@ snapshots: own-keys@1.0.1: dependencies: - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 @@ -4038,11 +4601,16 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-scurry@2.0.0: + dependencies: + lru-cache: 11.0.2 + minipass: 7.1.2 + path-to-regexp@6.3.0: {} path-type@4.0.0: {} - pathe@2.0.2: {} + pathe@2.0.3: {} pathval@2.0.0: {} @@ -4050,6 +4618,8 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.2: {} + pify@4.0.1: {} playwright-core@1.50.1: {} @@ -4062,7 +4632,7 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss@8.5.2: + postcss@8.5.3: dependencies: nanoid: 3.3.8 picocolors: 1.1.1 @@ -4076,7 +4646,7 @@ snapshots: prettier@2.8.8: {} - prettier@3.5.0: {} + prettier@3.5.2: {} pretty-format@27.5.1: dependencies: @@ -4110,7 +4680,7 @@ snapshots: es-abstract: 1.23.9 es-errors: 1.3.0 es-object-atoms: 1.1.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -4133,6 +4703,8 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve@1.22.10: dependencies: is-core-module: 2.16.1 @@ -4145,29 +4717,34 @@ snapshots: dependencies: glob: 10.4.5 - rollup@4.34.6: + rimraf@6.0.1: + dependencies: + glob: 11.0.1 + package-json-from-dist: 1.0.1 + + rollup@4.34.8: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.34.6 - '@rollup/rollup-android-arm64': 4.34.6 - '@rollup/rollup-darwin-arm64': 4.34.6 - '@rollup/rollup-darwin-x64': 4.34.6 - '@rollup/rollup-freebsd-arm64': 4.34.6 - '@rollup/rollup-freebsd-x64': 4.34.6 - '@rollup/rollup-linux-arm-gnueabihf': 4.34.6 - '@rollup/rollup-linux-arm-musleabihf': 4.34.6 - '@rollup/rollup-linux-arm64-gnu': 4.34.6 - '@rollup/rollup-linux-arm64-musl': 4.34.6 - '@rollup/rollup-linux-loongarch64-gnu': 4.34.6 - '@rollup/rollup-linux-powerpc64le-gnu': 4.34.6 - '@rollup/rollup-linux-riscv64-gnu': 4.34.6 - '@rollup/rollup-linux-s390x-gnu': 4.34.6 - '@rollup/rollup-linux-x64-gnu': 4.34.6 - '@rollup/rollup-linux-x64-musl': 4.34.6 - '@rollup/rollup-win32-arm64-msvc': 4.34.6 - '@rollup/rollup-win32-ia32-msvc': 4.34.6 - '@rollup/rollup-win32-x64-msvc': 4.34.6 + '@rollup/rollup-android-arm-eabi': 4.34.8 + '@rollup/rollup-android-arm64': 4.34.8 + '@rollup/rollup-darwin-arm64': 4.34.8 + '@rollup/rollup-darwin-x64': 4.34.8 + '@rollup/rollup-freebsd-arm64': 4.34.8 + '@rollup/rollup-freebsd-x64': 4.34.8 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 + '@rollup/rollup-linux-arm-musleabihf': 4.34.8 + '@rollup/rollup-linux-arm64-gnu': 4.34.8 + '@rollup/rollup-linux-arm64-musl': 4.34.8 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 + '@rollup/rollup-linux-riscv64-gnu': 4.34.8 + '@rollup/rollup-linux-s390x-gnu': 4.34.8 + '@rollup/rollup-linux-x64-gnu': 4.34.8 + '@rollup/rollup-linux-x64-musl': 4.34.8 + '@rollup/rollup-win32-arm64-msvc': 4.34.8 + '@rollup/rollup-win32-ia32-msvc': 4.34.8 + '@rollup/rollup-win32-x64-msvc': 4.34.8 fsevents: 2.3.3 run-parallel@1.2.0: @@ -4178,7 +4755,7 @@ snapshots: dependencies: call-bind: 1.0.8 call-bound: 1.0.3 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 @@ -4204,7 +4781,7 @@ snapshots: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 gopd: 1.2.0 has-property-descriptors: 1.0.2 @@ -4236,14 +4813,14 @@ snapshots: dependencies: call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 object-inspect: 1.13.4 side-channel-weakmap@1.0.2: dependencies: call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 object-inspect: 1.13.4 side-channel-map: 1.0.1 @@ -4259,10 +4836,10 @@ snapshots: signal-exit@4.1.0: {} - sirv@3.0.0: + sirv@3.0.1: dependencies: '@polka/url': 1.0.0-next.28 - mrmime: 2.0.0 + mrmime: 2.0.1 totalist: 3.0.1 slash@3.0.0: {} @@ -4283,6 +4860,8 @@ snapshots: sprintf-js@1.0.3: {} + stable-hash@0.0.4: {} + stackback@0.0.2: {} statuses@2.0.1: {} @@ -4349,12 +4928,27 @@ snapshots: '@pkgr/core': 0.1.1 tslib: 2.8.1 + tapable@2.2.1: {} + term-size@2.2.1: {} + terser@5.39.0: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.14.0 + commander: 2.20.3 + source-map-support: 0.5.21 + optional: true + tinybench@2.9.0: {} tinyexec@0.3.2: {} + tinyglobby@0.2.12: + dependencies: + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + tinypool@1.0.2: {} tinyrainbow@2.0.0: {} @@ -4393,13 +4987,21 @@ snapshots: tslib@2.8.1: {} + tsx@4.19.3: + dependencies: + esbuild: 0.25.0 + get-tsconfig: 4.10.0 + optionalDependencies: + fsevents: 2.3.3 + optional: true + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 type-fest@0.21.3: {} - type-fest@4.34.1: {} + type-fest@4.35.0: {} typed-array-buffer@1.0.3: dependencies: @@ -4434,12 +5036,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.24.0(eslint@9.20.1)(typescript@5.7.3): + typescript-eslint@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - eslint: 9.20.1 + '@typescript-eslint/eslint-plugin': 8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + eslint: 9.21.0(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -4455,6 +5057,8 @@ snapshots: undici-types@5.26.5: {} + undici-types@6.19.8: {} + universalify@0.1.2: {} universalify@0.2.0: {} @@ -4468,17 +5072,18 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - uuid@11.0.5: {} + uuid@11.1.0: {} - vite-node@3.0.5(@types/node@18.19.75): + vite-node@3.0.6(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 - pathe: 2.0.2 - vite: 5.4.14(@types/node@18.19.75) + pathe: 2.0.3 + vite: 6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - '@types/node' + - jiti - less - lightningcss - sass @@ -4487,42 +5092,50 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml - vite@5.4.14(@types/node@18.19.75): + vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0): dependencies: - esbuild: 0.21.5 - postcss: 8.5.2 - rollup: 4.34.6 + esbuild: 0.24.2 + postcss: 8.5.3 + rollup: 4.34.8 optionalDependencies: - '@types/node': 18.19.75 + '@types/node': 18.19.76 fsevents: 2.3.3 - - vitest@3.0.5(@types/node@18.19.75)(@vitest/browser@3.0.5)(msw@2.7.0(@types/node@18.19.75)(typescript@5.7.3)): - dependencies: - '@vitest/expect': 3.0.5 - '@vitest/mocker': 3.0.5(msw@2.7.0(@types/node@18.19.75)(typescript@5.7.3))(vite@5.4.14(@types/node@18.19.75)) - '@vitest/pretty-format': 3.0.5 - '@vitest/runner': 3.0.5 - '@vitest/snapshot': 3.0.5 - '@vitest/spy': 3.0.5 - '@vitest/utils': 3.0.5 - chai: 5.1.2 + jiti: 2.4.2 + terser: 5.39.0 + tsx: 4.19.3 + yaml: 2.7.0 + + vitest@3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0): + dependencies: + '@vitest/expect': 3.0.6 + '@vitest/mocker': 3.0.6(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)) + '@vitest/pretty-format': 3.0.6 + '@vitest/runner': 3.0.6 + '@vitest/snapshot': 3.0.6 + '@vitest/spy': 3.0.6 + '@vitest/utils': 3.0.6 + chai: 5.2.0 debug: 4.4.0 expect-type: 1.1.0 magic-string: 0.30.17 - pathe: 2.0.2 + pathe: 2.0.3 std-env: 3.8.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 5.4.14(@types/node@18.19.75) - vite-node: 3.0.5(@types/node@18.19.75) + vite: 6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) + vite-node: 3.0.6(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 18.19.75 - '@vitest/browser': 3.0.5(@types/node@18.19.75)(playwright@1.50.1)(typescript@5.7.3)(vite@5.4.14(@types/node@18.19.75))(vitest@3.0.5) + '@types/debug': 4.1.12 + '@types/node': 18.19.76 + '@vitest/browser': 3.0.6(@types/node@18.19.76)(playwright@1.50.1)(typescript@5.7.3)(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))(vitest@3.0.6) transitivePeerDependencies: + - jiti - less - lightningcss - msw @@ -4532,6 +5145,8 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml web-streams-polyfill@4.0.0-beta.3: {} @@ -4611,11 +5226,14 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 - ws@8.18.0: {} + ws@8.18.1: {} y18n@5.0.8: {} - yargs-file-commands@0.0.19(yargs@17.7.2): + yaml@2.7.0: + optional: true + + yargs-file-commands@0.0.20(yargs@17.7.2): dependencies: yargs: 17.7.2 @@ -4635,8 +5253,8 @@ snapshots: yoctocolors-cjs@2.1.2: {} - zod-to-json-schema@3.24.1(zod@3.24.1): + zod-to-json-schema@3.24.3(zod@3.24.2): dependencies: - zod: 3.24.1 + zod: 3.24.2 - zod@3.24.1: {} + zod@3.24.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..dee51e9 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - "packages/*" diff --git a/prettier.config.ts b/prettier.config.ts deleted file mode 100644 index 6501f13..0000000 --- a/prettier.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -// prettier.config.ts, .prettierrc.ts, prettier.config.mts, or .prettierrc.mts - -import { type Config } from 'prettier'; - -const config: Config = { - trailingComma: 'all', - tabWidth: 2, - semi: true, - singleQuote: true, -}; - -export default config; diff --git a/src/commands/$default.ts b/src/commands/$default.ts deleted file mode 100644 index 49a14ab..0000000 --- a/src/commands/$default.ts +++ /dev/null @@ -1,103 +0,0 @@ -import * as fs from 'fs/promises'; -import { createInterface } from 'readline/promises'; -import { ArgumentsCamelCase, Argv } from 'yargs'; -import { toolAgent } from '../core/toolAgent.js'; -import { SharedOptions } from '../options.js'; -import { Logger } from '../utils/logger.js'; -import { getTools } from '../tools/getTools.js'; - -import { getAnthropicApiKeyError } from '../utils/errors.js'; -import { getPackageInfo } from '../utils/versionCheck.js'; - -interface Options extends SharedOptions { - prompt?: string; -} - -export const command = '* [prompt]'; -export const describe = 'Execute a prompt or start interactive mode'; - -export const builder = (yargs: Argv) => { - return yargs.positional('prompt', { - type: 'string', - description: 'The prompt to execute', - }); // Type assertion needed due to yargs typing complexity -}; - -export const handler = async (argv: ArgumentsCamelCase) => { - const logger = new Logger({ name: 'Default' }); - const packageInfo = getPackageInfo(); - - logger.info(`MyCoder v${packageInfo.version} - AI-powered coding assistant`); - logger.warn( - 'WARNING: This tool can do anything on your command line that you ask it to.', - 'It can delete files, install software, and even send data to remote servers.', - 'It is a powerful tool that should be used with caution.', - 'By using this tool, you agree that the authors and contributors are not responsible for any damage that may occur as a result of using this tool.', - ); - try { - // Early API key check - if (!process.env.ANTHROPIC_API_KEY) { - logger.error(getAnthropicApiKeyError()); - process.exit(1); - } - - let prompt: string | undefined; - - // If promptFile is specified, read from file - if (argv.file) { - try { - prompt = await fs.readFile(argv.file, 'utf-8'); - } catch (error: any) { - logger.error( - `Failed to read prompt file: ${argv.file}, ${error?.message}`, - ); - process.exit(1); - } - } - - // If interactive mode - if (argv.interactive) { - const readline = createInterface({ - input: process.stdin, - output: process.stdout, - }); - - try { - logger.info( - "Type your request below or 'help' for usage information. Use Ctrl+C to exit.", - ); - prompt = await readline.question('\n> '); - } finally { - readline.close(); - } - } else if (!prompt) { - // Use command line prompt if provided - prompt = argv.prompt; - } - - if (!prompt) { - logger.error( - 'No prompt provided. Either specify a prompt, use --promptFile, or run in --interactive mode.', - ); - process.exit(1); - } - - // Add the standard suffix to all prompts - prompt += [ - 'Please ask for clarifications if required or if the tasks is confusing.', - "If you need more context, don't be scared to create a sub-agent to investigate and generate report back, this can save a lot of time and prevent obvious mistakes.", - 'Once the task is complete ask the user, via the userPrompt tool if the results are acceptable or if changes are needed or if there are additional follow on tasks.', - ].join('\n'); - - const tools = getTools(); - const result = await toolAgent(prompt, tools, logger); - const output = - typeof result.result === 'string' - ? result.result - : JSON.stringify(result.result, null, 2); - logger.info('\n=== Result ===\n', output); - } catch (error) { - logger.error('An error occurred:', error); - process.exit(1); - } -}; diff --git a/src/commands/tools.ts b/src/commands/tools.ts deleted file mode 100644 index 6ba22d6..0000000 --- a/src/commands/tools.ts +++ /dev/null @@ -1,89 +0,0 @@ -import type { Argv } from 'yargs'; -import { getTools } from '../tools/getTools.js'; -import type { JsonSchema7Type } from 'zod-to-json-schema'; - -interface Options { - [key: string]: unknown; -} - -export const describe = 'List all available tools and their capabilities'; - -export const builder = (yargs: Argv) => { - return yargs; -}; - -function formatSchema(schema: { - properties?: Record; - required?: string[]; -}) { - let output = ''; - - if (schema.properties) { - for (const [paramName, param] of Object.entries(schema.properties)) { - const required = schema.required?.includes(paramName) - ? '' - : ' (optional)'; - const description = (param as any).description || ''; - output += `${paramName}${required}: ${description}\n`; - - if ((param as any).type) { - output += ` Type: ${(param as any).type}\n`; - } - if ((param as any).maxLength) { - output += ` Max Length: ${(param as any).maxLength}\n`; - } - if ((param as any).additionalProperties) { - output += ` Additional Properties: ${JSON.stringify((param as any).additionalProperties)}\n`; - } - } - } - - return output; -} - -export const handler = () => { - try { - const tools = getTools(); - - console.log('Available Tools:\\n'); - - for (const tool of tools) { - // Tool name and description - console.log(`${tool.name}`); - console.log('-'.repeat(tool.name.length)); - console.log(`Description: ${tool.description}\\n`); - - // Parameters section - console.log('Parameters:'); - console.log( - formatSchema( - tool.parameters as { - properties?: Record; - required?: string[]; - }, - ), - ); - - // Returns section - console.log('Returns:'); - if (tool.returns) { - console.log( - formatSchema( - tool.returns as { - properties?: Record; - required?: string[]; - }, - ), - ); - } else { - console.log(' Type: any'); - console.log(' Description: Tool execution result or error\\n'); - } - - console.log(); // Add spacing between tools - } - } catch (error) { - console.error('Error listing tools:', error); - process.exit(1); - } -}; diff --git a/src/core/toolAgent.test.ts b/src/core/toolAgent.test.ts deleted file mode 100644 index f6281d0..0000000 --- a/src/core/toolAgent.test.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { executeToolCall } from './executeToolCall.js'; -import { Tool } from './types.js'; -import { toolAgent } from './toolAgent.js'; -import { MockLogger } from '../utils/mockLogger.js'; - -const logger = new MockLogger(); - -// Mock configuration for testing -const testConfig = { - maxIterations: 50, - model: 'claude-3-5-sonnet-20241022', - maxTokens: 4096, - temperature: 0.7, - getSystemPrompt: () => 'Test system prompt', -}; - -// Mock Anthropic client response -const mockResponse = { - content: [ - { - type: 'tool_use', - name: 'sequenceComplete', - id: '1', - input: { result: 'Test complete' }, - }, - ], - usage: { input_tokens: 10, output_tokens: 10 }, -}; - -// Mock Anthropic SDK -vi.mock('@anthropic-ai/sdk', () => ({ - default: class { - messages = { - create: () => mockResponse, - }; - }, -})); - -describe('toolAgent', () => { - beforeEach(() => { - process.env.ANTHROPIC_API_KEY = 'test-key'; - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - - // Mock tool for testing - const mockTool: Tool = { - name: 'mockTool', - description: 'A mock tool for testing', - parameters: { - type: 'object', - properties: { - input: { - type: 'string', - description: 'Test input', - }, - }, - required: ['input'], - }, - returns: { - type: 'string', - description: 'The processed result', - }, - execute: ({ input }) => Promise.resolve(`Processed: ${input}`), - }; - - const sequenceCompleteTool: Tool = { - name: 'sequenceComplete', - description: 'Completes the sequence', - parameters: { - type: 'object', - properties: { - result: { - type: 'string', - description: 'The final result', - }, - }, - required: ['result'], - }, - returns: { - type: 'string', - description: 'The final result', - }, - execute: ({ result }) => Promise.resolve(result), - }; - - it('should execute tool calls', async () => { - const result = await executeToolCall( - { - id: '1', - name: 'mockTool', - input: { input: 'test' }, - }, - [mockTool], - logger, - ); - - expect(result.includes('Processed: test')).toBeTruthy(); - }); - - it('should handle unknown tools', async () => { - await expect( - executeToolCall( - { - id: '1', - name: 'nonexistentTool', - input: {}, - }, - [mockTool], - logger, - ), - ).rejects.toThrow("No tool with the name 'nonexistentTool' exists."); - }); - - it('should handle tool execution errors', async () => { - const errorTool: Tool = { - name: 'errorTool', - description: 'A tool that always fails', - parameters: { - type: 'object', - properties: {}, - required: [], - }, - returns: { - type: 'string', - description: 'Error message', - }, - execute: () => { - throw new Error('Deliberate failure'); - }, - }; - - await expect( - executeToolCall( - { - id: '1', - name: 'errorTool', - input: {}, - }, - [errorTool], - logger, - ), - ).rejects.toThrow('Deliberate failure'); - }); - - // New tests for async system prompt - it('should handle async system prompt', async () => { - const result = await toolAgent( - 'Test prompt', - [sequenceCompleteTool], - logger, - testConfig, - ); - - expect(result.result).toBe('Test complete'); - expect(result.tokens.input).toBe(10); - expect(result.tokens.output).toBe(10); - }); -}); diff --git a/src/core/toolContext.ts b/src/core/toolContext.ts deleted file mode 100644 index 5a8bc86..0000000 --- a/src/core/toolContext.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Logger } from '../utils/logger.js'; -import { ToolContext } from './types.js'; - -export function createToolContext(logger: Logger): ToolContext { - return { - logger, - }; -} - -export function loggerToToolContext(logger: Logger): ToolContext { - return createToolContext(logger); -} diff --git a/src/options.ts b/src/options.ts deleted file mode 100644 index 60f774b..0000000 --- a/src/options.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { LogLevel } from './utils/logger.js'; - -export type SharedOptions = { - readonly log: LogLevel; - readonly interactive: boolean; - readonly file?: string; -}; - -export const sharedOptions = { - log: { - type: 'string', - alias: 'l', - description: 'Set minimum logging level', - default: 'info', - choices: ['debug', 'verbose', 'info', 'warn', 'error'], - } as const, - interactive: { - type: 'boolean', - alias: 'i', - description: 'Run in interactive mode, asking for prompts', - default: false, - } as const, - file: { - type: 'string', - alias: 'f', - description: 'Read prompt from a file', - } as const, -}; diff --git a/src/tools/browser/form-interaction.test.ts b/src/tools/browser/form-interaction.test.ts deleted file mode 100644 index 1e4f690..0000000 --- a/src/tools/browser/form-interaction.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'; -import { BrowserManager } from './BrowserManager.js'; -import { BrowserSession } from './types.js'; - -describe('Form Interaction Tests', () => { - let browserManager: BrowserManager; - let session: BrowserSession; - const baseUrl = 'https://the-internet.herokuapp.com'; - - beforeAll(async () => { - browserManager = new BrowserManager(); - session = await browserManager.createSession({ headless: true }); - }); - - afterAll(async () => { - await browserManager.closeAllSessions(); - }); - - beforeEach(async () => { - await session.page.goto(`${baseUrl}/login`); - }); - - it('should handle login form with invalid credentials', async () => { - await session.page.type('#username', 'invalid_user'); - await session.page.type('#password', 'invalid_pass'); - await session.page.click('button[type="submit"]'); - - const flashMessage = await session.page.waitForSelector('#flash'); - const messageText = await flashMessage?.evaluate((el) => el.textContent); - expect(messageText).toContain('Your username is invalid!'); - }); - - it('should clear form fields between attempts', async () => { - await session.page.type('#username', 'test_user'); - await session.page.type('#password', 'test_pass'); - - // Clear fields - await session.page.$eval( - '#username', - (el) => ((el as HTMLInputElement).value = ''), - ); - await session.page.$eval( - '#password', - (el) => ((el as HTMLInputElement).value = ''), - ); - - // Verify fields are empty - const username = await session.page.$eval( - '#username', - (el) => (el as HTMLInputElement).value, - ); - const password = await session.page.$eval( - '#password', - (el) => (el as HTMLInputElement).value, - ); - expect(username).toBe(''); - expect(password).toBe(''); - }); - - it('should maintain form state after page refresh', async () => { - const testUsername = 'persistence_test'; - await session.page.type('#username', testUsername); - await session.page.reload(); - - // Form should be cleared after refresh - const username = await session.page.$eval( - '#username', - (el) => (el as HTMLInputElement).value, - ); - expect(username).toBe(''); - }); - - describe('Content Extraction', () => { - it('should extract form labels and placeholders', async () => { - const usernameLabel = await session.page.$eval( - 'label[for="username"]', - (el) => el.textContent, - ); - expect(usernameLabel).toBe('Username'); - - const passwordPlaceholder = await session.page.$eval( - '#password', - (el) => (el as HTMLInputElement).placeholder, - ); - expect(passwordPlaceholder).toBe(''); - }); - }); -}); diff --git a/src/tools/browser/navigation.test.ts b/src/tools/browser/navigation.test.ts deleted file mode 100644 index 0a9b090..0000000 --- a/src/tools/browser/navigation.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -import { BrowserManager } from './BrowserManager.js'; -import { BrowserSession } from './types.js'; - -describe('Browser Navigation Tests', () => { - let browserManager: BrowserManager; - let session: BrowserSession; - const baseUrl = 'https://the-internet.herokuapp.com'; - - beforeAll(async () => { - browserManager = new BrowserManager(); - session = await browserManager.createSession({ headless: true }); - }); - - afterAll(async () => { - await browserManager.closeAllSessions(); - }); - - it('should navigate to main page and verify content', async () => { - await session.page.goto(baseUrl); - const title = await session.page.title(); - expect(title).toBe('The Internet'); - - const headerText = await session.page.$eval( - 'h1.heading', - (el) => el.textContent, - ); - expect(headerText).toBe('Welcome to the-internet'); - }); - - it('should navigate to login page and verify title', async () => { - await session.page.goto(`${baseUrl}/login`); - const title = await session.page.title(); - expect(title).toBe('The Internet'); - - const headerText = await session.page.$eval('h2', (el) => el.textContent); - expect(headerText).toBe('Login Page'); - }); - - it('should handle 404 pages appropriately', async () => { - await session.page.goto(`${baseUrl}/nonexistent`); - - // Wait for the page to stabilize - await session.page.waitForLoadState('networkidle'); - - // Check for 404 content instead of title since title may vary - const bodyText = await session.page.$eval('body', (el) => el.textContent); - expect(bodyText).toContain('Not Found'); - }); - - it('should handle navigation timeouts', async () => { - await expect( - session.page.goto(`${baseUrl}/slow`, { timeout: 1 }), - ).rejects.toThrow(); - }); - - it('should wait for network idle', async () => { - await session.page.goto(baseUrl, { - waitUntil: 'networkidle', - }); - expect(session.page.url()).toBe(`${baseUrl}/`); - }); -}); diff --git a/src/tools/getTools.ts b/src/tools/getTools.ts deleted file mode 100644 index e75e7e3..0000000 --- a/src/tools/getTools.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { subAgentTool } from './interaction/subAgent.js'; -import { readFileTool } from './io/readFile.js'; -import { userPromptTool } from './interaction/userPrompt.js'; -import { sequenceCompleteTool } from './system/sequenceComplete.js'; -import { fetchTool } from './io/fetch.js'; -import { Tool } from '../core/types.js'; -import { updateFileTool } from './io/updateFile.js'; -import { shellStartTool } from './system/shellStart.js'; -import { shellMessageTool } from './system/shellMessage.js'; -import { browseMessageTool } from './browser/browseMessage.js'; -import { browseStartTool } from './browser/browseStart.js'; -import { respawnTool } from './system/respawn.js'; -import { sleepTool } from './system/sleep.js'; - -export function getTools(): Tool[] { - return [ - subAgentTool, - readFileTool, - updateFileTool, - userPromptTool, - sequenceCompleteTool, - fetchTool, - shellStartTool, - shellMessageTool, - browseStartTool, - browseMessageTool, - respawnTool, - sleepTool, - ] as Tool[]; -} diff --git a/src/tools/interaction/subAgent.test.ts b/src/tools/interaction/subAgent.test.ts deleted file mode 100644 index f451d21..0000000 --- a/src/tools/interaction/subAgent.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { subAgentTool } from './subAgent.js'; -import { MockLogger } from '../../utils/mockLogger.js'; - -const logger = new MockLogger(); - -// Mock Anthropic client response -const mockResponse = { - content: [ - { - type: 'tool_use', - name: 'sequenceComplete', - id: '1', - input: { result: 'Sub-agent task complete' }, - }, - ], - usage: { input_tokens: 10, output_tokens: 10 }, -}; - -// Mock Anthropic SDK -vi.mock('@anthropic-ai/sdk', () => ({ - default: class { - messages = { - create: () => mockResponse, - }; - }, -})); - -describe('subAgent', () => { - beforeEach(() => { - process.env.ANTHROPIC_API_KEY = 'test-key'; - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - - it('should create and execute a sub-agent', async () => { - const result = await subAgentTool.execute( - { - prompt: 'Test sub-agent task', - description: 'A test agent for unit testing', - }, - { logger }, - ); - - expect(result.toString()).toContain('Sub-agent task complete'); - }); - - it('should handle errors gracefully', async () => { - // Remove API key to trigger error - delete process.env.ANTHROPIC_API_KEY; - - await expect( - subAgentTool.execute( - { - prompt: 'Test task', - description: 'An agent that should fail', - }, - { logger }, - ), - ).rejects.toThrow('ANTHROPIC_API_KEY environment variable is not set'); - }); - - it('should validate description length', async () => { - const longDescription = - 'This is a very long description that exceeds the maximum allowed length of 80 characters and should cause validation to fail'; - - await expect( - subAgentTool.execute( - { - prompt: 'Test task', - description: longDescription, - }, - { logger }, - ), - ).rejects.toThrow(); - }); -}); diff --git a/src/tools/interaction/subAgent.ts b/src/tools/interaction/subAgent.ts deleted file mode 100644 index 273e435..0000000 --- a/src/tools/interaction/subAgent.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { toolAgent } from '../../core/toolAgent.js'; -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; -import { getTools } from '../getTools.js'; - -const parameterSchema = z.object({ - prompt: z.string().describe('The prompt/task for the sub-agent'), - description: z - .string() - .max(80) - .describe("A brief description of the sub-agent's purpose (max 80 chars)"), -}); - -const returnSchema = z - .string() - .describe( - 'The response from the sub-agent including its reasoning and tool usage', - ); - -type Parameters = z.infer; -type ReturnType = z.infer; - -// Sub-agent specific configuration -const subAgentConfig = { - maxIterations: 50, - model: 'claude-3-5-sonnet-20241022', - maxTokens: 4096, - temperature: 0.7, - getSystemPrompt: () => { - return [ - 'You are a focused AI sub-agent handling a specific task.', - 'You have access to the same tools as the main agent but should focus only on your assigned task.', - 'When complete, call the sequenceComplete tool with your results.', - 'Follow any specific conventions or requirements provided in the task context.', - 'Ask the main agent for clarification if critical information is missing.', - ].join('\n'); - }, -}; - -export const subAgentTool: Tool = { - name: 'subAgent', - description: - 'Creates a sub-agent that has access to all tools to solve a specific task', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), - execute: async (params, { logger }) => { - // Validate parameters - const { prompt } = parameterSchema.parse(params); - - const tools = getTools().filter((tool) => tool.name !== 'userPrompt'); - - const result = await toolAgent(prompt, tools, logger, subAgentConfig); - return result.result; // Return the result string directly - }, - logParameters: (input, { logger }) => { - logger.info(`Delegating task "${input.description}"`); - }, - logReturns: () => {}, -}; diff --git a/src/tools/io/readFile.test.ts b/src/tools/io/readFile.test.ts deleted file mode 100644 index 0346a01..0000000 --- a/src/tools/io/readFile.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { readFileTool } from './readFile.js'; -import { MockLogger } from '../../utils/mockLogger.js'; - -const logger = new MockLogger(); - -describe('readFile', () => { - it('should read a file', async () => { - const { content } = await readFileTool.execute( - { path: 'package.json', description: 'test' }, - { logger }, - ); - expect(content).toContain('mycoder'); - }); - - it('should handle missing files', async () => { - try { - await readFileTool.execute( - { path: 'nonexistent.txt', description: 'test' }, - { logger }, - ); - expect(true).toBe(false); // Should not reach here - } catch (error: any) { - expect(error.message).toContain('ENOENT'); - } - }); -}); diff --git a/src/tools/io/updateFile.ts b/src/tools/io/updateFile.ts deleted file mode 100644 index 2684160..0000000 --- a/src/tools/io/updateFile.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as fsPromises from 'fs/promises'; -import * as fs from 'fs'; -import * as path from 'path'; -import { Tool } from '../../core/types.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; - -const updateOperationSchema = z.discriminatedUnion('command', [ - z.object({ - command: z.literal('update'), - oldStr: z.string().describe('Existing text to replace (must be unique)'), - newStr: z.string().describe('New text to insert'), - }), - z.object({ - command: z.literal('rewrite'), - content: z.string().describe('Complete new file content'), - }), - z.object({ - command: z.literal('append'), - content: z.string().describe('Content to append to file'), - }), -]); - -const parameterSchema = z.object({ - path: z.string().describe('Path to the file'), - operation: updateOperationSchema.describe('Update operation to perform'), - description: z - .string() - .max(80) - .describe('The reason you are modifying this file (max 80 chars)'), -}); - -const returnSchema = z.object({ - path: z.string().describe('Path to the updated file'), - operation: z.enum(['update', 'rewrite', 'append']), -}); - -type Parameters = z.infer; -type ReturnType = z.infer; - -export const updateFileTool: Tool = { - name: 'updateFile', - description: - 'Creates a file or updates a file by rewriting, patching, or appending content', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), - execute: async ({ path: filePath, operation }, { logger }) => { - const absolutePath = path.resolve(path.normalize(filePath)); - logger.verbose(`Updating file: ${absolutePath}`); - - await fsPromises.mkdir(path.dirname(absolutePath), { recursive: true }); - - if (operation.command === 'update') { - const content = await fsPromises.readFile(absolutePath, 'utf8'); - const occurrences = content.split(operation.oldStr).length - 1; - if (occurrences !== 1) { - throw new Error( - `Found ${occurrences} occurrences of oldStr, expected exactly 1`, - ); - } - await fsPromises.writeFile( - absolutePath, - content.replace(operation.oldStr, operation.newStr), - 'utf8', - ); - } else if (operation.command === 'append') { - await fsPromises.appendFile(absolutePath, operation.content, 'utf8'); - } else { - await fsPromises.writeFile(absolutePath, operation.content, 'utf8'); - } - - logger.verbose(`Operation complete: ${operation.command}`); - return { path: filePath, operation: operation.command }; - }, - logParameters: (input, { logger }) => { - const isFile = fs.existsSync(input.path); - logger.info( - `${isFile ? 'Modifying' : 'Creating'} "${input.path}", ${input.description}`, - ); - }, - logReturns: () => {}, -}; diff --git a/src/tools/system/respawn.ts b/src/tools/system/respawn.ts deleted file mode 100644 index e0158bd..0000000 --- a/src/tools/system/respawn.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Tool, ToolContext } from '../../core/types.js'; - -export interface RespawnInput { - respawnContext: string; -} - -export const respawnTool: Tool = { - name: 'respawn', - description: - 'Resets the agent context to just the system prompt and provided context', - parameters: { - type: 'object', - properties: { - respawnContext: { - type: 'string', - description: 'The context to keep after respawning', - }, - }, - required: ['respawnContext'], - additionalProperties: false, - }, - returns: { - type: 'string', - description: 'A message indicating that the respawn has been initiated', - }, - execute: ( - _params: Record, - _context: ToolContext, - ): Promise => { - // This is a special case tool - the actual respawn logic is handled in toolAgent - return Promise.resolve('Respawn initiated'); - }, -}; diff --git a/src/tools/system/shellExecute.test.ts b/src/tools/system/shellExecute.test.ts deleted file mode 100644 index 603a854..0000000 --- a/src/tools/system/shellExecute.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { shellExecuteTool } from './shellExecute.js'; -import { MockLogger } from '../../utils/mockLogger.js'; - -const logger = new MockLogger(); - -describe('shellExecute', () => { - it('should execute shell commands', async () => { - const { stdout } = await shellExecuteTool.execute( - { command: "echo 'test'", description: 'test' }, - { logger }, - ); - expect(stdout).toContain('test'); - }); - - it('should handle command errors', async () => { - const { error } = await shellExecuteTool.execute( - { command: 'nonexistentcommand', description: 'test' }, - { logger }, - ); - expect(error).toContain('Command failed:'); - }); -}); diff --git a/src/types/browser.ts b/src/types/browser.ts deleted file mode 100644 index 99f6d62..0000000 --- a/src/types/browser.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { z } from 'zod'; - -export const BrowseStartSchema = z.object({ - headless: z.boolean().optional().default(true), - description: z.string().max(80), -}); - -export type BrowseStartParams = z.infer; - -export const SelectorTypeSchema = z.enum(['css', 'xpath']); - -export const BrowseActionSchema = z.discriminatedUnion('action', [ - z.object({ - action: z.literal('navigate'), - url: z.string().url(), - }), - z.object({ - action: z.literal('click'), - selector: z.string(), - selectorType: SelectorTypeSchema, - }), - z.object({ - action: z.literal('type'), - selector: z.string(), - selectorType: SelectorTypeSchema, - text: z.string(), - }), -]); - -export const BrowseMessageSchema = z.object({ - instanceId: z.string(), - description: z.string().max(80), - action: BrowseActionSchema.optional(), - takeScreenshot: z.boolean().optional().default(false), - endSession: z.boolean().optional().default(false), -}); - -export type BrowseMessageParams = z.infer; diff --git a/src/types/browserTools.ts b/src/types/browserTools.ts deleted file mode 100644 index b96bf37..0000000 --- a/src/types/browserTools.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { z } from 'zod'; - -// Browser Types -export const BrowserTypeEnum = z.enum(['chromium', 'firefox', 'webkit']); -export type BrowserType = z.infer; - -export const BrowserActionEnum = z.enum(['navigate', 'click', 'type', 'end']); -export type BrowserAction = z.infer; - -// browseStart interfaces and schemas -export interface BrowseStartOptions { - headless?: boolean; - slowMo?: number; - timeout?: number; - viewport?: { - width: number; - height: number; - }; -} - -export const BrowseStartOptionsSchema = z.object({ - headless: z.boolean().optional(), - slowMo: z.number().optional(), - timeout: z.number().optional(), - viewport: z - .object({ - width: z.number(), - height: z.number(), - }) - .optional(), -}); - -export interface BrowseStartParams { - browserType: BrowserType; - options?: BrowseStartOptions; - description: string; -} - -export const BrowseStartParamsSchema = z.object({ - browserType: BrowserTypeEnum, - options: BrowseStartOptionsSchema.optional(), - description: z.string().max(80), -}); - -export interface BrowseStartResult { - sessionId: string; - success: boolean; - error?: string; -} - -// browseMessage interfaces and schemas -export interface BrowseMessageActionParams { - navigate?: { url: string }; - click?: { selector: string }; - type?: { - selector: string; - text: string; - }; - end?: boolean; -} - -export const BrowseMessageActionParamsSchema = z.object({ - navigate: z.object({ url: z.string().url() }).optional(), - click: z.object({ selector: z.string() }).optional(), - type: z - .object({ - selector: z.string(), - text: z.string(), - }) - .optional(), - end: z.boolean().optional(), -}); - -export interface BrowseMessageParams { - sessionId: string; - action: BrowserAction; - actionParams: BrowseMessageActionParams; - description: string; -} - -export const BrowseMessageParamsSchema = z.object({ - sessionId: z.string(), - action: BrowserActionEnum, - actionParams: BrowseMessageActionParamsSchema, - description: z.string().max(80), -}); - -export interface BrowseMessageResult { - success: boolean; - content?: string; - consoleLogs?: string[]; - error?: string; -} diff --git a/src/utils/versionCheck.test.ts b/src/utils/versionCheck.test.ts deleted file mode 100644 index 26e3362..0000000 --- a/src/utils/versionCheck.test.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { - generateUpgradeMessage, - fetchLatestVersion, - getPackageInfo, - checkForUpdates, -} from './versionCheck.js'; -import * as fs from 'fs'; -import * as fsPromises from 'fs/promises'; -import * as path from 'path'; -import { getSettingsDir } from '../settings/settings.js'; - -vi.mock('fs'); -vi.mock('fs/promises'); -vi.mock('../settings/settings.js'); - -describe('versionCheck', () => { - describe('generateUpgradeMessage', () => { - it('returns null when versions are the same', () => { - expect(generateUpgradeMessage('1.0.0', '1.0.0', 'test-package')).toBe( - null, - ); - }); - - it('returns upgrade message when versions differ', () => { - const message = generateUpgradeMessage('1.0.0', '1.1.0', 'test-package'); - expect(message).toContain('Update available: 1.0.0 → 1.1.0'); - expect(message).toContain("Run 'npm install -g test-package' to update"); - }); - - it('returns null when current version is higher', () => { - expect(generateUpgradeMessage('2.0.0', '1.0.0', 'test-package')).toBe( - null, - ); - }); - }); - - describe('fetchLatestVersion', () => { - const mockFetch = vi.fn(); - const originalFetch = global.fetch; - - beforeEach(() => { - global.fetch = mockFetch; - }); - - afterEach(() => { - global.fetch = originalFetch; - vi.clearAllMocks(); - }); - - it('returns version when fetch succeeds', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => Promise.resolve({ version: '1.1.0' }), - }); - - const version = await fetchLatestVersion('test-package'); - expect(version).toBe('1.1.0'); - expect(mockFetch).toHaveBeenCalledWith( - 'https://registry.npmjs.org/test-package/latest', - ); - }); - - it('throws error when fetch fails', async () => { - mockFetch.mockResolvedValueOnce({ - ok: false, - statusText: 'Not Found', - }); - - await expect(fetchLatestVersion('test-package')).rejects.toThrow( - 'Failed to fetch version info: Not Found', - ); - }); - - it('throws error when version is missing from response', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => Promise.resolve({}), - }); - - await expect(fetchLatestVersion('test-package')).rejects.toThrow( - 'Version info not found in response', - ); - }); - }); - - describe('getPackageInfo', () => { - it('returns package info from package.json', () => { - const info = getPackageInfo(); - expect(info).toHaveProperty('name'); - expect(info).toHaveProperty('version'); - expect(typeof info.name).toBe('string'); - expect(typeof info.version).toBe('string'); - }); - }); - - describe('checkForUpdates', () => { - const mockFetch = vi.fn(); - const originalFetch = global.fetch; - const mockSettingsDir = '/mock/settings/dir'; - const versionFilePath = path.join(mockSettingsDir, 'lastVersionCheck'); - - beforeEach(() => { - global.fetch = mockFetch; - vi.mocked(getSettingsDir).mockReturnValue(mockSettingsDir); - vi.mocked(fs.existsSync).mockReturnValue(false); - }); - - afterEach(() => { - global.fetch = originalFetch; - vi.clearAllMocks(); - }); - - it('returns null and initiates background check when no cached version', async () => { - vi.mocked(fs.existsSync).mockReturnValue(false); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => Promise.resolve({ version: '2.0.0' }), - }); - - const result = await checkForUpdates(); - expect(result).toBe(null); - - // Wait for setImmediate to complete - await new Promise((resolve) => setImmediate(resolve)); - - expect(mockFetch).toHaveBeenCalled(); - expect(fsPromises.writeFile).toHaveBeenCalledWith( - versionFilePath, - '2.0.0', - 'utf8', - ); - }); - - it('returns upgrade message when cached version is newer', async () => { - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fsPromises.readFile).mockResolvedValue('2.0.0'); - - const result = await checkForUpdates(); - expect(result).toContain('Update available'); - }); - - it('handles errors gracefully during version check', async () => { - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fsPromises.readFile).mockRejectedValue(new Error('Test error')); - - const result = await checkForUpdates(); - expect(result).toBe(null); - }); - - it('handles errors gracefully during background update', async () => { - vi.mocked(fs.existsSync).mockReturnValue(false); - mockFetch.mockRejectedValue(new Error('Network error')); - - const result = await checkForUpdates(); - expect(result).toBe(null); - - // Wait for setImmediate to complete - await new Promise((resolve) => setImmediate(resolve)); - - // Verify the error was handled - expect(fsPromises.writeFile).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/tests/cli.test.ts b/tests/cli.test.ts deleted file mode 100644 index 279156b..0000000 --- a/tests/cli.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { expect, test, describe } from 'vitest'; -import { execSync } from 'child_process'; -import { version } from '../package.json'; - -describe('CLI', () => { - test('version command outputs correct version', () => { - const output = execSync('npx mycoder --version').toString(); - expect(output.trim()).toContain(version); - expect(output.trim()).not.toContain('AI-powered coding assistant'); - }); - - test('-h command outputs help', () => { - const output = execSync('npx mycoder -h').toString(); - expect(output.trim()).toContain('Commands:'); - expect(output.trim()).toContain('Positionals:'); - expect(output.trim()).toContain('Options:'); - }); -}); diff --git a/tests/core/toolAgent.respawn.test.ts b/tests/core/toolAgent.respawn.test.ts deleted file mode 100644 index b6b3019..0000000 --- a/tests/core/toolAgent.respawn.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { toolAgent } from '../../src/core/toolAgent.js'; -import { getTools } from '../../src/tools/getTools.js'; -import { Logger } from '../../src/utils/logger.js'; - -// Mock Anthropic SDK -vi.mock('@anthropic-ai/sdk', () => { - return { - default: vi.fn().mockImplementation(() => ({ - messages: { - create: vi - .fn() - .mockResolvedValueOnce({ - content: [ - { - type: 'tool_use', - name: 'respawn', - id: 'test-id', - input: { respawnContext: 'new context' }, - }, - ], - usage: { input_tokens: 10, output_tokens: 10 }, - }) - .mockResolvedValueOnce({ - content: [], - usage: { input_tokens: 5, output_tokens: 5 }, - }), - }, - })), - }; -}); - -describe('toolAgent respawn functionality', () => { - const mockLogger = new Logger({ name: 'test' }); - const tools = getTools(); - - beforeEach(() => { - process.env.ANTHROPIC_API_KEY = 'test-key'; - vi.clearAllMocks(); - }); - - it('should handle respawn tool calls', async () => { - const result = await toolAgent('initial prompt', tools, mockLogger, { - maxIterations: 2, // Need at least 2 iterations for respawn + empty response - model: 'test-model', - maxTokens: 100, - temperature: 0, - getSystemPrompt: () => 'test system prompt', - }); - - expect(result.result).toBe( - 'Agent returned empty message implying it is done its given task', - ); - }); -}); diff --git a/tests/tools/respawn.test.ts b/tests/tools/respawn.test.ts deleted file mode 100644 index 9c74683..0000000 --- a/tests/tools/respawn.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, it, expect, vi } from 'vitest'; -import { respawnTool } from '../../src/tools/system/respawn.js'; -import { Logger } from '../../src/utils/logger.js'; - -describe('respawnTool', () => { - const mockLogger = new Logger({ name: 'test' }); - - it('should have correct name and description', () => { - expect(respawnTool.name).toBe('respawn'); - expect(respawnTool.description).toContain('Resets the agent context'); - }); - - it('should have correct parameter schema', () => { - expect(respawnTool.parameters.type).toBe('object'); - expect(respawnTool.parameters.properties.respawnContext).toBeDefined(); - expect(respawnTool.parameters.required).toContain('respawnContext'); - }); - - it('should execute and return confirmation message', async () => { - const result = await respawnTool.execute( - { respawnContext: 'new context' }, - { logger: mockLogger }, - ); - expect(result).toBe('Respawn initiated'); - }); -}); diff --git a/tests/tools/system/sleep.test.ts b/tests/tools/system/sleep.test.ts deleted file mode 100644 index 3b481f0..0000000 --- a/tests/tools/system/sleep.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { sleepTool } from '../../../src/tools/system/sleep'; - -describe('sleep tool', () => { - beforeEach(() => { - vi.useFakeTimers(); - }); - - it('should sleep for the specified duration', async () => { - const startTime = Date.now(); - const sleepPromise = sleepTool.execute({ seconds: 2 }); - - await vi.advanceTimersByTimeAsync(2000); - const result = await sleepPromise; - - expect(result).toEqual({ sleptFor: 2 }); - }); - - it('should reject negative sleep duration', async () => { - await expect(sleepTool.execute({ seconds: -1 })).rejects.toThrow(); - }); - - it('should reject sleep duration over 1 hour', async () => { - await expect(sleepTool.execute({ seconds: 3601 })).rejects.toThrow(); - }); - - it('should format log parameters correctly', () => { - expect(sleepTool.logParameters({ seconds: 5 })).toBe('sleeping for 5 seconds'); - }); - - it('should format log returns correctly', () => { - expect(sleepTool.logReturns({ sleptFor: 5 })).toBe(''); - }); -}); diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json deleted file mode 100644 index 9dbaf76..0000000 --- a/tsconfig.eslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": true - }, - "include": ["src", "test"] -} diff --git a/vitest.config.ts b/vitest.config.ts deleted file mode 100644 index ce7359a..0000000 --- a/vitest.config.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - test: { - include: ['src/**/*.test.ts', 'tests/**/*.test.ts'], - environment: 'node', - globals: true, - // Default timeout for all tests - testTimeout: 10000, - // Timeout for hook operations - hookTimeout: 10000, - // Specific environment configurations - environmentOptions: { - // Browser-specific timeouts when running browser tests - browser: { - testTimeout: 30000, - hookTimeout: 30000, - }, - }, - // Browser tests configuration - browser: { - enabled: true, - provider: 'playwright', - headless: true, - }, - }, -});