diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 5757f05ab1e..e655ac6ac4a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -2,11 +2,17 @@ name: Bug Report description: Clearly report a bug with detailed repro steps labels: ["bug"] body: + - type: markdown + attributes: + value: | + **Thanks for your report!** Please check existing issues first: + 👉 https://github.com/RooVetGit/Roo-Code/issues + - type: input id: version attributes: label: App Version - description: Specify exactly which version you're using (e.g., v3.3.1) + description: What version of Roo Code are you using? (e.g., v3.3.1) validations: required: true @@ -14,22 +20,28 @@ body: id: provider attributes: label: API Provider - description: Choose the API provider involved - multiple: false options: - - OpenRouter - Anthropic - - Google Gemini + - AWS Bedrock + - Chutes AI - DeepSeek - - OpenAI - - OpenAI Compatible - - GCP Vertex AI - - Amazon Bedrock - - Requesty - Glama - - VS Code LM API + - Google Gemini + - Google Vertex AI + - Groq + - Human Relay Provider + - LiteLLM - LM Studio + - Mistral AI - Ollama + - OpenAI + - OpenAI Compatible + - OpenRouter + - Requesty + - Unbound + - VS Code Language Model API + - xAI (Grok) + - Not Applicable / Other validations: required: true @@ -37,44 +49,38 @@ body: id: model attributes: label: Model Used - description: Clearly specify the exact model (e.g., Claude 3.7 Sonnet) - validations: - required: true - - - type: textarea - id: what-happened - attributes: - label: Actual vs. Expected Behavior - description: Clearly state what actually happened and what you expected instead. - placeholder: Provide precise details of the issue here. + description: Exact model name (e.g., Claude 3.7 Sonnet). Use N/A if irrelevant. validations: required: true - type: textarea id: steps attributes: - label: Detailed Steps to Reproduce + label: 🔁 Steps to Reproduce description: | - List the exact steps someone must follow to reproduce this bug: - 1. Starting conditions (software state, settings, environment) - 2. Precise actions taken (every click, selection, input) - 3. Clearly observe and report outcomes - value: | - 1. - 2. - 3. + Help us see what you saw. Give clear, numbered steps: + + 1. Setup (OS, extension version, settings) + 2. Exact actions (clicks, input, files, commands) + 3. What happened after each step + + Think like you're writing a recipe. Without this, we can't reproduce the issue. validations: required: true - type: textarea - id: logs + id: what-happened attributes: - label: Relevant API Request Output - description: Paste relevant API logs or outputs here (formatted automatically as code) - render: shell + label: 💥 Outcome Summary (Optional) + description: | + Recap what went wrong in one or two lines. Use this if the bug is weird, unexpected, or needs extra context. + + Example: "Expected code to run, but got an empty response and no error." + placeholder: Expected ___, but got ___. - type: textarea - id: additional-context + id: logs attributes: - label: Additional Context - description: Include extra details, screenshots, or related issues. + label: 📄 Relevant Logs or Errors + description: Paste API logs, terminal output, or errors here. Use triple backticks (```) for code formatting. + render: shell \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..ac84be3b78e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,76 @@ +name: Detailed Feature Proposal +description: Propose a specific, actionable feature or enhancement for implementation +labels: ["proposal", "enhancement"] +body: + - type: markdown + attributes: + value: | + **Thank you for proposing a detailed feature for Roo Code!** + + This template is for submitting specific, actionable proposals that you or others intend to implement after discussion and approval. It's a key part of our [Issue-First Approach](../../CONTRIBUTING.md). + + - **For general ideas or less defined suggestions**, please use [GitHub Discussions](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) first. + - **Before submitting**, please search existing [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues) and [Discussions](https://github.com/RooVetGit/Roo-Code/discussions) to avoid duplicates. + + For guidance or to discuss your idea, join the [Roo Code Discord](https://discord.gg/roocode) and DM **Hannes Rudolph** (`hrudolph`). + + A maintainer (especially @hannesrudolph) will review this proposal. **Do not start implementation until this proposal is approved and assigned.** + - type: textarea + id: problem-description + attributes: + label: What problem does this proposed feature solve? + description: Clearly describe the problem, use case, or opportunity this feature addresses. Why is this change needed? + placeholder: e.g., "Users currently cannot..." or "It would be beneficial if..." + validations: + required: true + + - type: textarea + id: proposed-solution + attributes: + label: Describe the proposed solution in detail + description: Provide a clear and comprehensive description of the feature or enhancement. How should it work? What are the key functionalities? + placeholder: Include details on user interaction, expected behavior, and potential impact. + validations: + required: true + + - type: textarea + id: technical-details + attributes: + label: Technical considerations or implementation details (optional) + description: If you have thoughts on how this could be implemented, or specific technical aspects to consider, please share them. + placeholder: e.g., "This might involve changes to X component..." or "We should consider Y library..." + + - type: textarea + id: alternatives-considered + attributes: + label: Describe alternatives considered (if any) + description: What other ways could this problem be solved or this functionality be achieved? Why is your proposed solution preferred? + placeholder: Briefly outline any alternative approaches and why they were not chosen. + + - type: textarea + id: additional-context + attributes: + label: Additional Context & Mockups + description: Add any other context, mockups, screenshots, or links that help illustrate the proposal. + + - type: checkboxes + id: checklist + attributes: + label: Proposal Checklist + description: Please confirm the following before submitting. + options: + - label: I have searched existing Issues and Discussions to ensure this proposal is not a duplicate. + required: true + - label: This proposal is for a specific, actionable change intended for implementation (not a general idea). + required: true + - label: I understand that this proposal requires review and approval before any development work begins. + required: true + + - type: checkboxes + id: willingness-to-contribute + attributes: + label: Are you interested in implementing this feature if approved? + description: (This is optional and does not affect the proposal's consideration) + options: + - label: Yes, I would like to contribute to implementing this feature. + required: false \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index de7e461cb9c..e013eea2774 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,35 +1,83 @@ -## Context + + +### Related GitHub Issue - + -## Implementation +Closes: # + +### Description -Some description of HOW you achieved it. Perhaps give a high level description of the program flow. Did you need to refactor something? What tradeoffs did you take? Are there things in here which you’d particularly like people to pay close attention to? +### Test Procedure + -## Screenshots +### Type of Change -| before | after | -| ------ | ----- | -| | | + -## How to Test +- [ ] 🐛 **Bug Fix**: Non-breaking change that fixes an issue. +- [ ] ✨ **New Feature**: Non-breaking change that adds functionality. +- [ ] 💥 **Breaking Change**: Fix or feature that would cause existing functionality to not work as expected. +- [ ] ♻️ **Refactor**: Code change that neither fixes a bug nor adds a feature. +- [ ] 💅 **Style**: Changes that do not affect the meaning of the code (white-space, formatting, etc.). +- [ ] 📚 **Documentation**: Updates to documentation files. +- [ ] ⚙️ **Build/CI**: Changes to the build process or CI configuration. +- [ ] 🧹 **Chore**: Other changes that don't modify `src` or test files. - + +- [ ] **Issue Linked**: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above). +- [ ] **Scope**: My changes are focused on the linked issue (one major feature/fix per PR). +- [ ] **Self-Review**: I have performed a thorough self-review of my code. +- [ ] **Code Quality**: + - [ ] My code adheres to the project's style guidelines. + - [ ] There are no new linting errors or warnings (`npm run lint`). + - [ ] All debug code (e.g., `console.log`) has been removed. +- [ ] **Testing**: + - [ ] New and/or updated tests have been added to cover my changes. + - [ ] All tests pass locally (`npm test`). + - [ ] The application builds successfully with my changes. +- [ ] **Branch Hygiene**: My branch is up-to-date (rebased) with the `main` branch. +- [ ] **Documentation Impact**: I have considered if my changes require documentation updates (see "Documentation Updates" section below). +- [ ] **Changeset**: A changeset has been created using `npm run changeset` if this PR includes user-facing changes or dependency updates. +- [ ] **Contribution Guidelines**: I have read and agree to the [Contributor Guidelines](../CONTRIBUTING.md). -A straightforward scenario of how to test your changes will help reviewers that are not familiar with the part of the code that you are changing but want to see it in action. This section can include a description or step-by-step instructions of how to get to the state of v2 that your change affects. +### Screenshots / Videos -A "How To Test" section can look something like this: + -- Sign in with a user with tracks -- Activate `show_awesome_cat_gifs` feature (add `?feature.show_awesome_cat_gifs=1` to your URL) -- You should see a GIF with cats dancing +### Documentation Updates + -## Get in Touch +### Additional Notes - + diff --git a/.github/workflows/build-vsix.yml b/.github/workflows/build-vsix.yml new file mode 100644 index 00000000000..ab6bd95129c --- /dev/null +++ b/.github/workflows/build-vsix.yml @@ -0,0 +1,45 @@ +name: Build VSIX + +on: + pull_request: + types: [labeled] + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + if: github.event.label.name == 'build' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Install all dependencies + run: npm run install:all + + - name: Build Extension + run: npm run build + + - name: Upload VSIX artifact + uses: actions/upload-artifact@v4 + with: + name: extension-vsix + path: bin/*.vsix + + - name: Comment PR with artifact link + if: github.event_name == 'pull_request' + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + Build successful! 🚀 + You can download the VSIX extension [here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index 7e027d0fc9a..667d0b6bf80 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -78,8 +78,10 @@ jobs: run: npm run install:all - name: Compile (to build and copy WASM files) run: npm run compile - - name: Run unit tests + - name: Run jest unit tests run: npx jest --silent + - name: Run vitest unit tests + run: npx vitest run --silent test-webview: runs-on: ${{ matrix.os }} diff --git a/.github/workflows/marketplace-publish.yml b/.github/workflows/marketplace-publish.yml index 0080b10687b..834f38caf3f 100644 --- a/.github/workflows/marketplace-publish.yml +++ b/.github/workflows/marketplace-publish.yml @@ -29,9 +29,7 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: Install Dependencies - run: | - npm install -g vsce ovsx - npm run install:all + run: npm run install:all - name: Create .env file run: echo "POSTHOG_API_KEY=${{ secrets.POSTHOG_API_KEY }}" >> .env - name: Package Extension diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml index 18e978a07e6..2fadbb83d9d 100644 --- a/.github/workflows/update-contributors.yml +++ b/.github/workflows/update-contributors.yml @@ -14,10 +14,10 @@ jobs: pull-requests: write # Needed for creating PRs steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18' cache: 'npm' diff --git a/.gitignore b/.gitignore index 12777329698..3a541f1b094 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ out out-* node_modules coverage/ +mock/ .DS_Store diff --git a/.roo/rules-translate/001-general-rules.md b/.roo/rules-translate/001-general-rules.md index 61d232bbf70..bdb18bea641 100644 --- a/.roo/rules-translate/001-general-rules.md +++ b/.roo/rules-translate/001-general-rules.md @@ -1,6 +1,6 @@ # 1. SUPPORTED LANGUAGES AND LOCATION -- Localize all strings into the following locale files: ca, de, en, es, fr, hi, it, ja, ko, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW +- Localize all strings into the following locale files: ca, de, en, es, fr, hi, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW - The VSCode extension has two main areas that require localization: - Core Extension: src/i18n/locales/ (extension backend) - WebView UI: webview-ui/src/i18n/locales/ (user interface) diff --git a/.roo/rules/rules.md b/.roo/rules/rules.md index 4351fc4b81f..bf3f863a0b3 100644 --- a/.roo/rules/rules.md +++ b/.roo/rules/rules.md @@ -16,4 +16,4 @@ # Adding a New Setting -To add a new setting that persists its state, follow the steps in cline_docs/settings.md +To add a new setting that persists its state, follow the steps in docs/settings.md diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 47112d72281..850dcc07983 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -41,7 +41,17 @@ "type": "npm", "script": "watch:esbuild", "group": "build", - "problemMatcher": "$esbuild-watch", + "problemMatcher": { + "owner": "esbuild", + "pattern": { + "regexp": "^$" + }, + "background": { + "activeOnStart": true, + "beginsPattern": "\\[watch\\] build started", + "endsPattern": "\\[watch\\] build finished" + } + }, "isBackground": true, "presentation": { "group": "watch", diff --git a/.vscodeignore b/.vscodeignore index 1bc718e577e..1cc3c46cd43 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -33,7 +33,7 @@ jest.* .pearai-agent-ignore .pearai-agent-modes benchmark/** -cline_docs/** +docs/** e2e/** evals/** locales/** diff --git a/CHANGELOG.md b/CHANGELOG.md index d2b44282d69..da8fdbeb21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,116 @@ # Roo Code Changelog +## [3.17.2] - 2025-05-15 + +- Revert "Switch to the new Roo message parser" (appears to cause a tool parsing bug) +- Lock the versions of vsce and ovsx + +## [3.17.1] - 2025-05-15 + +- Fix the display of the command to execute during approval +- Fix incorrect reserved tokens calculation on OpenRouter (thanks @daniel-lxs!) + +## [3.17.0] - 2025-05-14 + +- Enable Gemini implicit caching +- Add "when to use" section to mode definitions to enable better orchestration +- Add experimental feature to intelligently condense the task context instead of truncating it +- Fix one of the causes of the gray screen issue (thanks @xyOz-dev!) +- Focus improvements for better UI interactions (thanks Cline!) +- Switch to the new Roo message parser for improved performance (thanks Cline!) +- Enable source maps for improved debugging (thanks @KJ7LNW!) +- Update OpenRouter provider to use provider-specific model info (thanks @daniel-lxs!) +- Fix Requesty cost/token reporting (thanks @dtrugman!) +- Improve command execution UI +- Add more in-app links to relevant documentation +- Update the new task tool description and the ask mode custom instructions in the system prompt +- Add IPC types to roo-code.d.ts +- Add build VSIX workflow to pull requests (thanks @SmartManoj!) +- Improve apply_diff tool to intelligently deduce line numbers (thanks @samhvw8!) +- Fix command validation for shell array indexing (thanks @KJ7LNW!) +- Handle diagnostics that point at a directory URI (thanks @daniel-lxs!) +- Fix "Current ask promise was ignored" error (thanks @zxdvd!) + +## [3.16.6] - 2025-05-12 + +- Restore "Improve provider profile management in the external API" +- Fix to subtask sequencing (thanks @wkordalski!) +- Fix webview terminal output processing error (thanks @KJ7LNW!) +- Fix textarea empty string fallback logic (thanks @elianiva!) + +## [3.16.5] - 2025-05-10 + +- Revert "Improve provider profile management in the external API" until we track down a bug with defaults + +## [3.16.4] - 2025-05-09 + +- Improve provider profile management in the external API +- Enforce provider selection in OpenRouter by using 'only' parameter and disabling fallbacks (thanks @shariqriazz!) +- Fix display issues with long profile names (thanks @cannuri!) +- Prevent terminal focus theft on paste after command execution (thanks @MuriloFP!) +- Save OpenAI compatible custom headers correctly +- Fix race condition when updating prompts (thanks @elianiva!) +- Fix display issues in high contrast themes (thanks @zhangtony239!) +- Fix not being able to use specific providers on Openrouter (thanks @daniel-lxs!) +- Show properly formatted multi-line commands in preview (thanks @KJ7LNW!) +- Handle unsupported language errors gracefully in read_file tool (thanks @KJ7LNW!) +- Enhance focus styles in select-dropdown and fix docs URL (thanks @zhangtony239!) +- Properly handle mode name overflow in UI (thanks @elianiva!) +- Fix project MCP always allow issue (thanks @aheizi!) + +## [3.16.3] - 2025-05-08 + +- Revert Tailwind migration while we fix a few spots +- Add Elixir file extension support in language parser (thanks @pfitz!) + +## [3.16.2] - 2025-05-07 + +- Clarify XML tool use formatting instructions +- Error handling code cleanup (thanks @monkeyDluffy6017!) + +## [3.16.1] - 2025-05-07 + +- Add LiteLLM provider support +- Improve stability by detecting and preventing tool loops +- Add Dutch localization (thanks @Githubguy132010!) +- Add editor name to telemetry for better analytics +- Migrate to Tailwind CSS for improved UI consistency +- Fix footer button wrapping in About section on narrow screens (thanks @ecmasx!) +- Update evals defaults +- Update dependencies to latest versions + +## [3.16.0] - 2025-05-06 + +- Add vertical tab navigation to the settings (thanks @dlab-anton) +- Add Groq and Chutes API providers (thanks @shariqriazz) +- Clickable code references in code block (thanks @KJ7LNW) +- Improve accessibility of ato-approve toggles (thanks @Deon588) +- Requesty provider fixes (thanks @dtrugman) +- Fix migration and persistence of per-mode API profiles (thanks @alasano) +- Fix usage of `path.basename` in the extension webview (thanks @samhvw8) +- Fix display issue of the programming language dropdown in the code block component (thanks @zhangtony239) +- MCP server errors are now captured and shown in a new "Errors" tab (thanks @robertheadley) +- Error logging will no longer break MCP functionality if the server is properly connected (thanks @ksze) +- You can now toggle the `terminal.integrated.inheritEnv` VSCode setting directly for the Roo Code settings (thanks @KJ7LNW) +- Add `gemini-2.5-pro-preview-05-06` to the Vertex and Gemini providers (thanks @zetaloop) +- Ensure evals exercises are up-to-date before running evals (thanks @shariqriazz) +- Lots of general UI improvements (thanks @elianiva) +- Organize provider settings into separate components +- Improved icons and translations for the code block component +- Add support for tests that use ESM libraries +- Move environment detail generation to a separate module +- Enable prompt caching by default for supported Gemini models + +## [3.15.5] - 2025-05-05 + +- Update @google/genai to 0.12 (includes some streaming completion bug fixes) +- Rendering performance improvements for code blocks in chat (thanks @KJ7LNW) + +## [3.15.4] - 2025-05-04 + +- Fix a nasty bug that would cause Roo Code to hang, particularly in orchestrator mode +- Improve Gemini caching efficiency + ## [3.15.3] - 2025-05-02 - Terminal: Fix empty command bug diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index bb04e1abc23..06190290ebb 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +**English** • [Català](locales/ca/CODE_OF_CONDUCT.md) • [Deutsch](locales/de/CODE_OF_CONDUCT.md) • [Español](locales/es/CODE_OF_CONDUCT.md) • [Français](locales/fr/CODE_OF_CONDUCT.md) • [हिंदी](locales/hi/CODE_OF_CONDUCT.md) • [Italiano](locales/it/CODE_OF_CONDUCT.md) • [Nederlands](locales/nl/CODE_OF_CONDUCT.md) • [Русский](locales/ru/CODE_OF_CONDUCT.md) + +[日本語](locales/ja/CODE_OF_CONDUCT.md) • [한국어](locales/ko/CODE_OF_CONDUCT.md) • [Polski](locales/pl/CODE_OF_CONDUCT.md) • [Português (BR)](locales/pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](locales/tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](locales/vi/CODE_OF_CONDUCT.md) • [简体中文](locales/zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](locales/zh-TW/CODE_OF_CONDUCT.md) + # Contributor Covenant Code of Conduct ## Our Pledge diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c41ebf6de39..bbbd15590f3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,177 +1,129 @@ -# PearAI Contributing -Go [here](https://github.com/trypear/pearai-master/blob/main/README.md) for the most up-to-date setup and contributing information to get started contributing to PearAI. +**English** • [Català](locales/ca/CONTRIBUTING.md) • [Deutsch](locales/de/CONTRIBUTING.md) • [Español](locales/es/CONTRIBUTING.md) • [Français](locales/fr/CONTRIBUTING.md) • [हिंदी](locales/hi/CONTRIBUTING.md) • [Italiano](locales/it/CONTRIBUTING.md) • [Nederlands](locales/nl/CONTRIBUTING.md) • [Русский](locales/ru/CONTRIBUTING.md) -______________ -## Roo Code Contribution Guide +[日本語](locales/ja/CONTRIBUTING.md) • [한국어](locales/ko/CONTRIBUTING.md) • [Polski](locales/pl/CONTRIBUTING.md) • [Português (BR)](locales/pt-BR/CONTRIBUTING.md) • [Türkçe](locales/tr/CONTRIBUTING.md) • [Tiếng Việt](locales/vi/CONTRIBUTING.md) • [简体中文](locales/zh-CN/CONTRIBUTING.md) • [繁體中文](locales/zh-TW/CONTRIBUTING.md) -We're thrilled you're interested in contributing to Roo Code. Whether you're fixing a bug, adding a feature, or improving our docs, every contribution makes Roo Code smarter! To keep our community vibrant and welcoming, all members must adhere to our [Code of Conduct](CODE_OF_CONDUCT.md). +# Contributing to Roo Code -## Join Our Community +Roo Code is a community-driven project, and we deeply value every contribution. To streamline collaboration, we operate on an [Issue-First](#issue-first-approach) basis, meaning all [Pull Requests (PRs)](#submitting-a-pull-request) must first be linked to a GitHub Issue. Please review this guide carefully. -We strongly encourage all contributors to join our [Discord community](https://discord.gg/roocode)! Being part of our Discord server helps you: +## Table of Contents -- Get real-time help and guidance on your contributions -- Connect with other contributors and core team members -- Stay updated on project developments and priorities -- Participate in discussions that shape Roo Code's future -- Find collaboration opportunities with other developers +- [Before You Contribute](#before-you-contribute) +- [Finding & Planning Your Contribution](#finding--planning-your-contribution) +- [Development & Submission Process](#development--submission-process) +- [Legal](#legal) -## Reporting Bugs or Issues +## Before You Contribute -Bug reports help make Roo Code better for everyone! Before creating a new issue, please [search existing ones](https://github.com/RooVetGit/Roo-Code/issues) to avoid duplicates. When you're ready to report a bug, head over to our [issues page](https://github.com/RooVetGit/Roo-Code/issues/new/choose) where you'll find a template to help you with filling out the relevant information. +### 1. Code of Conduct -
- 🔐 Important: If you discover a security vulnerability, please use the Github security tool to report it privately. -
+All contributors must adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md). -## Deciding What to Work On +### 2. Project Roadmap -Looking for a good first contribution? Check out issues in the "Issue [Unassigned]" section of our [Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1) Github Project. These are specifically curated for new contributors and areas where we'd love some help! +Our roadmap guides the project's direction. Align your contributions with these key goals: -We also welcome contributions to our [documentation](https://docs.roocode.com/)! Whether it's fixing typos, improving existing guides, or creating new educational content - we'd love to build a community-driven repository of resources that helps everyone get the most out of Roo Code. You can click "Edit this page" on any page to quickly get to the right spot in Github to edit the file, or you can dive directly into https://github.com/RooVetGit/Roo-Code-Docs. +### Reliability First -If you're planning to work on a bigger feature, please create a [feature request](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) first so we can discuss whether it aligns with Roo Code's vision. You may also want to check our [Project Roadmap](#project-roadmap) below to see if your idea fits with our strategic direction. +- Ensure diff editing and command execution are consistently reliable. +- Reduce friction points that deter regular usage. +- Guarantee smooth operation across all locales and platforms. +- Expand robust support for a wide variety of AI providers and models. -## Project Roadmap +### Enhanced User Experience -Roo Code has a clear development roadmap that guides our priorities and future direction. Understanding our roadmap can help you: +- Streamline the UI/UX for clarity and intuitiveness. +- Continuously improve the workflow to meet the high expectations developers have for daily-use tools. -- Align your contributions with project goals -- Identify areas where your expertise would be most valuable -- Understand the context behind certain design decisions -- Find inspiration for new features that support our vision +### Leading on Agent Performance -Our current roadmap focuses on six key pillars: +- Establish comprehensive evaluation benchmarks (evals) to measure real-world productivity. +- Make it easy for everyone to easily run and interpret these evals. +- Ship improvements that demonstrate clear increases in eval scores. -### Provider Support +Mention alignment with these areas in your PRs. -We aim to support as many providers well as we can: +### 3. Join the Roo Code Community -- More versatile "OpenAI Compatible" support -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Enhanced support for Ollama and LM Studio +- **Primary:** Join our [Discord](https://discord.gg/roocode) and DM **Hannes Rudolph (`hrudolph`)**. +- **Alternative:** Experienced contributors can engage directly via [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -### Model Support +## Finding & Planning Your Contribution -We want Roo to work as well on as many models as possible, including local models: +### Types of Contributions -- Local model support through custom system prompting and workflows -- Benchmarking evals and test cases +- **Bug Fixes:** Addressing code issues. +- **New Features:** Adding functionality. +- **Documentation:** Improving guides and clarity. -### System Support +### Issue-First Approach -We want Roo to run well on everyone's computer: +All contributions must begin with a GitHub Issue. -- Cross platform terminal integration -- Strong and consistent support for Mac, Windows, and Linux +- **Check existing issues**: Search [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Create an issue**: Use appropriate templates: + - **Bugs:** "Bug Report" template. + - **Features:** "Detailed Feature Proposal" template. Approval required before starting. +- **Claim issues**: Comment and await official assignment. -### Documentation +**PRs without approved issues may be closed.** -We want comprehensive, accessible documentation for all users and contributors: +### Deciding What to Work On -- Expanded user guides and tutorials -- Clear API documentation -- Better contributor guidance -- Multilingual documentation resources -- Interactive examples and code samples +- Check the [GitHub Project](https://github.com/orgs/RooVetGit/projects/1) for unassigned "Good First Issues." +- For docs, visit [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). -### Stability +### Reporting Bugs -We want to significantly decrease the number of bugs and increase automated testing: +- Check for existing reports first. +- Create new bugs using the ["Bug Report" template](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Security issues**: Report privately via [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -- Debug logging switch -- "Machine/Task Information" copy button for sending in with bug/support requests +## Development & Submission Process -### Internationalization +### Development Setup -We want Roo to speak everyone's language: +1. **Fork & Clone:** -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع - -We especially welcome contributions that advance our roadmap goals. If you're working on something that aligns with these pillars, please mention it in your PR description. - -## Development Setup - -1. **Clone** the repo: - -```sh -git clone https://github.com/RooVetGit/Roo-Code.git ``` - -2. **Install dependencies**: - -```sh -npm run install:all +git clone https://github.com/YOUR_USERNAME/Roo-Code.git ``` -3. **Start the webview (Vite/React app with HMR)**: +2. **Install Dependencies:** -```sh -npm run dev ``` - -4. **Debug**: - Press `F5` (or **Run** → **Start Debugging**) in VSCode to open a new session with Roo Code loaded. - -Changes to the webview will appear immediately. Changes to the core extension will require a restart of the extension host. - -Alternatively you can build a .vsix and install it directly in VSCode: - -```sh -npm run build -``` - -A `.vsix` file will appear in the `bin/` directory which can be installed with: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Writing and Submitting Code - -Anyone can contribute code to Roo Code, but we ask that you follow these guidelines to ensure your contributions can be smoothly integrated: - -1. **Keep Pull Requests Focused** - - - Limit PRs to a single feature or bug fix - - Split larger changes into smaller, related PRs - - Break changes into logical commits that can be reviewed independently - -2. **Code Quality** +3. **Debugging:** Open with VS Code (`F5`). - - All PRs must pass CI checks which include both linting and formatting - - Address any ESLint warnings or errors before submitting - - Respond to all feedback from Ellipsis, our automated code review tool - - Follow TypeScript best practices and maintain type safety +### Writing Code Guidelines -3. **Testing** +- One focused PR per feature or fix. +- Follow ESLint and TypeScript best practices. +- Write clear, descriptive commits referencing issues (e.g., `Fixes #123`). +- Provide thorough testing (`npm test`). +- Rebase onto the latest `main` branch before submission. - - Add tests for new features - - Run `npm test` to ensure all tests pass - - Update existing tests if your changes affect them - - Include both unit tests and integration tests where appropriate +### Submitting a Pull Request -4. **Commit Guidelines** +- Begin as a **Draft PR** if seeking early feedback. +- Clearly describe your changes following the Pull Request Template. +- Provide screenshots/videos for UI changes. +- Indicate if documentation updates are necessary. - - Write clear, descriptive commit messages - - Reference relevant issues in commits using #issue-number +### Pull Request Policy -5. **Before Submitting** +- Must reference pre-approved, assigned issues. +- PRs without adherence to the policy may be closed. +- PRs should pass CI tests, align with the roadmap, and have clear documentation. - - Rebase your branch on the latest main - - Ensure your branch builds successfully - - Double-check all tests are passing - - Review your changes for any debugging code or console logs +### Review Process -6. **Pull Request Description** - - Clearly describe what your changes do - - Include steps to test the changes - - List any breaking changes - - Add screenshots for UI changes +- **Daily Triage:** Quick checks by maintainers. +- **Weekly In-depth Review:** Comprehensive assessment. +- **Iterate promptly** based on feedback. -## Contribution Agreement +## Legal -By submitting a pull request, you agree that your contributions will be licensed under the same license as the project ([Apache 2.0](LICENSE)). +By contributing, you agree your contributions will be licensed under the Apache 2.0 License, consistent with Roo Code's licensing. diff --git a/cline_docs/bedrock/bedrock-cache-strategy-documentation.md b/docs/bedrock/bedrock-cache-strategy-documentation.md similarity index 100% rename from cline_docs/bedrock/bedrock-cache-strategy-documentation.md rename to docs/bedrock/bedrock-cache-strategy-documentation.md diff --git a/cline_docs/bedrock/model-identification.md b/docs/bedrock/model-identification.md similarity index 100% rename from cline_docs/bedrock/model-identification.md rename to docs/bedrock/model-identification.md diff --git a/cline_docs/settings.md b/docs/settings.md similarity index 100% rename from cline_docs/settings.md rename to docs/settings.md diff --git a/e2e/.vscode-test.mjs b/e2e/.vscode-test.mjs index ccc8b495ea9..c83f12c4bb6 100644 --- a/e2e/.vscode-test.mjs +++ b/e2e/.vscode-test.mjs @@ -2,18 +2,15 @@ * See: https://code.visualstudio.com/api/working-with-extensions/testing-extension */ -import { defineConfig } from '@vscode/test-cli'; +import { defineConfig } from "@vscode/test-cli" export default defineConfig({ - label: 'integrationTest', - files: 'out/suite/**/*.test.js', - workspaceFolder: '.', + label: "integrationTest", + files: "out/suite/**/*.test.js", + workspaceFolder: ".", mocha: { - ui: 'tdd', + ui: "tdd", timeout: 60000, }, - launchArgs: [ - '--enable-proposed-api=RooVeterinaryInc.roo-cline', - '--disable-extensions' - ] -}); + launchArgs: ["--enable-proposed-api=RooVeterinaryInc.roo-cline", "--disable-extensions"], +}) diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 278df120c28..e7f75013fdc 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -9,10 +9,10 @@ "version": "0.1.0", "devDependencies": { "@types/mocha": "^10.0.10", - "@vscode/test-cli": "^0.0.9", + "@vscode/test-cli": "^0.0.10", "@vscode/test-electron": "^2.4.0", "mocha": "^11.1.0", - "typescript": "^5.4.5" + "typescript": "5.8.3" } }, "node_modules/@bcoe/v8-coverage": { @@ -104,9 +104,9 @@ "license": "MIT" }, "node_modules/@vscode/test-cli": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.9.tgz", - "integrity": "sha512-vsl5/ueE3Jf0f6XzB0ECHHMsd5A0Yu6StElb8a+XsubZW7kHNAOw4Y3TSSuDzKEpLnJ92nbMy1Zl+KLGCE6NaA==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.10.tgz", + "integrity": "sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==", "dev": true, "license": "MIT", "dependencies": { @@ -318,16 +318,16 @@ } }, "node_modules/@vscode/test-electron": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", - "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", "dev": true, "license": "MIT", "dependencies": { "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.5", "jszip": "^3.10.1", - "ora": "^7.0.1", + "ora": "^8.1.0", "semver": "^7.6.2" }, "engines": { @@ -411,27 +411,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -445,33 +424,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -502,31 +454,6 @@ "dev": true, "license": "ISC" }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/c8": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", @@ -622,16 +549,16 @@ } }, "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^4.0.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -965,6 +892,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -1061,27 +1001,6 @@ "node": ">= 14" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -1374,14 +1293,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { @@ -1411,15 +1333,14 @@ } }, "node_modules/mocha": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", - "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.2.2.tgz", + "integrity": "sha512-VlSBxrPYHK4YNOEbFdkCxHQbZMoNzBkoPprqtZRW6311EUF/DlSxoycE2e/2NtRk4WKkIXzyrXDTrlikJMWgbw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", @@ -1430,6 +1351,7 @@ "log-symbols": "^4.1.0", "minimatch": "^5.1.6", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", @@ -1446,6 +1368,22 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/mocha/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -1459,6 +1397,20 @@ "node": ">=10" } }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -1503,40 +1455,40 @@ } }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ora": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", - "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1563,28 +1515,41 @@ "license": "MIT" }, "node_modules/ora/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ora/node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -1593,18 +1558,18 @@ } }, "node_modules/ora/node_modules/string-width": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", - "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1703,6 +1668,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1773,29 +1745,22 @@ } }, "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1870,16 +1835,13 @@ } }, "node_modules/stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "dev": true, "license": "MIT", - "dependencies": { - "bl": "^5.0.0" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2110,9 +2072,9 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/e2e/package.json b/e2e/package.json index c05e39b8760..2eed983aea2 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "lint": "ESLINT_USE_FLAT_CONFIG=false eslint src/**/*.ts", + "lint": "ESLINT_USE_FLAT_CONFIG=false eslint src/**/*.ts --max-warnings=0", "check-types": "tsc --noEmit", "test": "npm run build && npx dotenvx run -f .env.local -- node ./out/runTest.js", "ci": "npm run vscode-test && npm run test", @@ -13,9 +13,9 @@ "dependencies": {}, "devDependencies": { "@types/mocha": "^10.0.10", - "@vscode/test-cli": "^0.0.9", + "@vscode/test-cli": "^0.0.10", "@vscode/test-electron": "^2.4.0", "mocha": "^11.1.0", - "typescript": "^5.4.5" + "typescript": "5.8.3" } } diff --git a/evals/apps/web/package.json b/evals/apps/web/package.json index d52770bbb5d..d7b5ca6aed3 100644 --- a/evals/apps/web/package.json +++ b/evals/apps/web/package.json @@ -31,7 +31,7 @@ "clsx": "^2.1.1", "cmdk": "^1.1.0", "fuzzysort": "^3.1.0", - "lucide-react": "^0.479.0", + "lucide-react": "^0.510.0", "next": "15.2.2", "next-themes": "^0.4.6", "p-map": "^7.0.3", diff --git a/evals/config/eslint/package.json b/evals/config/eslint/package.json index 3b84b039de4..b706de1d95a 100644 --- a/evals/config/eslint/package.json +++ b/evals/config/eslint/package.json @@ -16,7 +16,6 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-turbo": "^2.4.4", "globals": "^16.0.0", - "typescript": "^5", "typescript-eslint": "^8.26.0" } } diff --git a/evals/package.json b/evals/package.json index 2e7c21d9770..92c4ab99cd1 100644 --- a/evals/package.json +++ b/evals/package.json @@ -20,7 +20,7 @@ "prettier": "^3.5.3", "tsx": "^4.19.4", "turbo": "^2.5.2", - "typescript": "^5.8.3", + "typescript": "5.8.3", "typescript-eslint": "^8.31.1" } } diff --git a/evals/packages/db/package.json b/evals/packages/db/package.json index ffc298ea01f..bbf3c1cfc79 100644 --- a/evals/packages/db/package.json +++ b/evals/packages/db/package.json @@ -21,8 +21,8 @@ }, "dependencies": { "@evals/types": "workspace:^", - "@libsql/client": "^0.14.0", - "drizzle-orm": "^0.40.0", + "@libsql/client": "^0.15.0", + "drizzle-orm": "^0.43.0", "drizzle-zod": "^0.7.0", "p-map": "^7.0.3", "zod": "^3.24.2" @@ -30,7 +30,7 @@ "devDependencies": { "@evals/eslint-config": "workspace:^", "@evals/typescript-config": "workspace:^", - "drizzle-kit": "^0.30.5", + "drizzle-kit": "^0.31.0", "execa": "^9.5.2", "vitest": "^3.0.9" } diff --git a/evals/packages/types/src/roo-code-defaults.ts b/evals/packages/types/src/roo-code-defaults.ts index 442510976bf..9c20f94146d 100644 --- a/evals/packages/types/src/roo-code-defaults.ts +++ b/evals/packages/types/src/roo-code-defaults.ts @@ -4,7 +4,7 @@ export const rooCodeDefaults: RooCodeSettings = { apiProvider: "openrouter", openRouterUseMiddleOutTransform: false, - lastShownAnnouncementId: "apr-30-2025-3-15", + lastShownAnnouncementId: "may-06-2025-3-16", pinnedApiConfigs: {}, @@ -42,7 +42,7 @@ export const rooCodeDefaults: RooCodeSettings = { terminalZshP10k: false, terminalZdotdir: true, terminalCompressProgressBar: true, - terminalShellIntegrationDisabled: true, + terminalShellIntegrationDisabled: false, diffEnabled: true, fuzzyMatchThreshold: 1, @@ -53,7 +53,7 @@ export const rooCodeDefaults: RooCodeSettings = { maxOpenTabsContext: 20, maxWorkspaceFiles: 200, showRooIgnoredFiles: true, - maxReadFileLine: 500, + maxReadFileLine: 500, // -1 to enable full file reading. language: "en", telemetrySetting: "enabled", diff --git a/evals/packages/types/src/roo-code.ts b/evals/packages/types/src/roo-code.ts index 4318603edc5..08dd58e6180 100644 --- a/evals/packages/types/src/roo-code.ts +++ b/evals/packages/types/src/roo-code.ts @@ -56,6 +56,7 @@ export const languages = [ "it", "ja", "ko", + "nl", "pl", "pt-BR", "ru", @@ -102,7 +103,6 @@ export const modelInfoSchema = z.object({ supportsImages: z.boolean().optional(), supportsComputerUse: z.boolean().optional(), supportsPromptCache: z.boolean(), - isPromptCacheOptional: z.boolean().optional(), inputPrice: z.number().optional(), outputPrice: z.number().optional(), cacheWritesPrice: z.number().optional(), @@ -128,18 +128,6 @@ export const modelInfoSchema = z.object({ export type ModelInfo = z.infer -/** - * ApiConfigMeta - */ - -export const apiConfigMetaSchema = z.object({ - id: z.string(), - name: z.string(), - apiProvider: providerNamesSchema.optional(), -}) - -export type ApiConfigMeta = z.infer - /** * HistoryItem */ @@ -310,7 +298,7 @@ export type CommandExecutionStatus = z.infer */ const experimentsSchema = z.object({ + autoCondenseContext: z.boolean(), powerSteering: z.boolean(), }) @@ -330,45 +319,75 @@ export type Experiments = z.infer type _AssertExperiments = AssertEqual>> /** - * ProviderSettings + * ProviderSettingsEntry */ -export const providerSettingsSchema = z.object({ +export const providerSettingsEntrySchema = z.object({ + id: z.string(), + name: z.string(), apiProvider: providerNamesSchema.optional(), - // Anthropic +}) + +export type ProviderSettingsEntry = z.infer + +/** + * ProviderSettings + */ + +const genericProviderSettingsSchema = z.object({ + includeMaxTokens: z.boolean().optional(), + reasoningEffort: reasoningEffortsSchema.optional(), + diffEnabled: z.boolean().optional(), + fuzzyMatchThreshold: z.number().optional(), + modelTemperature: z.number().nullish(), + rateLimitSeconds: z.number().optional(), + // Claude 3.7 Sonnet Thinking + modelMaxTokens: z.number().optional(), + modelMaxThinkingTokens: z.number().optional(), +}) + +const anthropicSchema = z.object({ apiModelId: z.string().optional(), apiKey: z.string().optional(), anthropicBaseUrl: z.string().optional(), anthropicUseAuthToken: z.boolean().optional(), - // Glama +}) + +const glamaSchema = z.object({ glamaModelId: z.string().optional(), glamaApiKey: z.string().optional(), - // OpenRouter +}) + +const openRouterSchema = z.object({ openRouterApiKey: z.string().optional(), openRouterModelId: z.string().optional(), openRouterBaseUrl: z.string().optional(), openRouterSpecificProvider: z.string().optional(), openRouterUseMiddleOutTransform: z.boolean().optional(), - // Amazon Bedrock +}) + +const bedrockSchema = z.object({ awsAccessKey: z.string().optional(), awsSecretKey: z.string().optional(), awsSessionToken: z.string().optional(), awsRegion: z.string().optional(), awsUseCrossRegionInference: z.boolean().optional(), awsUsePromptCache: z.boolean().optional(), - awspromptCacheId: z.string().optional(), awsProfile: z.string().optional(), awsUseProfile: z.boolean().optional(), awsCustomArn: z.string().optional(), - // Google Vertex +}) + +const vertexSchema = z.object({ vertexKeyFile: z.string().optional(), vertexJsonCredentials: z.string().optional(), vertexProjectId: z.string().optional(), vertexRegion: z.string().optional(), - // OpenAI +}) + +const openAiSchema = z.object({ openAiBaseUrl: z.string().optional(), openAiApiKey: z.string().optional(), - openAiHostHeader: z.string().optional(), openAiLegacyFormat: z.boolean().optional(), openAiR1FormatEnabled: z.boolean().optional(), openAiModelId: z.string().optional(), @@ -377,10 +396,16 @@ export const providerSettingsSchema = z.object({ azureApiVersion: z.string().optional(), openAiStreamingEnabled: z.boolean().optional(), enableReasoningEffort: z.boolean().optional(), - // Ollama + openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. + openAiHeaders: z.record(z.string(), z.string()).optional(), +}) + +const ollamaSchema = z.object({ ollamaModelId: z.string().optional(), ollamaBaseUrl: z.string().optional(), - // VS Code LM +}) + +const vsCodeLmSchema = z.object({ vsCodeLmModelSelector: z .object({ vendor: z.string().optional(), @@ -389,43 +414,48 @@ export const providerSettingsSchema = z.object({ id: z.string().optional(), }) .optional(), - // LM Studio +}) + +const lmStudioSchema = z.object({ lmStudioModelId: z.string().optional(), lmStudioBaseUrl: z.string().optional(), lmStudioDraftModelId: z.string().optional(), lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), - // Gemini +}) + +const geminiSchema = z.object({ geminiApiKey: z.string().optional(), googleGeminiBaseUrl: z.string().optional(), - // OpenAI Native +}) + +const openAiNativeSchema = z.object({ openAiNativeApiKey: z.string().optional(), openAiNativeBaseUrl: z.string().optional(), - // Mistral +}) + +const mistralSchema = z.object({ mistralApiKey: z.string().optional(), mistralCodestralUrl: z.string().optional(), - // DeepSeek +}) + +const deepSeekSchema = z.object({ deepSeekBaseUrl: z.string().optional(), deepSeekApiKey: z.string().optional(), - // Unbound +}) + +const unboundSchema = z.object({ unboundApiKey: z.string().optional(), unboundModelId: z.string().optional(), - // Requesty +}) + +const requestySchema = z.object({ requestyApiKey: z.string().optional(), requestyModelId: z.string().optional(), - // X.AI (Grok) - xaiApiKey: z.string().optional(), - // Claude 3.7 Sonnet Thinking - modelMaxTokens: z.number().optional(), - modelMaxThinkingTokens: z.number().optional(), - // Generic - includeMaxTokens: z.boolean().optional(), - reasoningEffort: reasoningEffortsSchema.optional(), - promptCachingEnabled: z.boolean().optional(), - diffEnabled: z.boolean().optional(), - fuzzyMatchThreshold: z.number().optional(), - modelTemperature: z.number().nullish(), - rateLimitSeconds: z.number().optional(), - // Fake AI +}) + +const humanRelaySchema = z.object({}) + +const fakeAiSchema = z.object({ fakeAi: z.unknown().optional(), // PearAI pearaiApiKey: z.string().optional(), @@ -437,6 +467,165 @@ export const providerSettingsSchema = z.object({ .optional(), }) +const xaiSchema = z.object({ + xaiApiKey: z.string().optional(), +}) + +const groqSchema = z.object({ + groqApiKey: z.string().optional(), +}) + +const chutesSchema = z.object({ + chutesApiKey: z.string().optional(), +}) + +const litellmSchema = z.object({ + litellmBaseUrl: z.string().optional(), + litellmApiKey: z.string().optional(), + litellmModelId: z.string().optional(), +}) + +const defaultSchema = z.object({ + apiProvider: z.undefined(), +}) + +export const providerSettingsSchemaDiscriminated = z + .discriminatedUnion("apiProvider", [ + anthropicSchema.merge( + z.object({ + apiProvider: z.literal("anthropic"), + }), + ), + glamaSchema.merge( + z.object({ + apiProvider: z.literal("glama"), + }), + ), + openRouterSchema.merge( + z.object({ + apiProvider: z.literal("openrouter"), + }), + ), + bedrockSchema.merge( + z.object({ + apiProvider: z.literal("bedrock"), + }), + ), + vertexSchema.merge( + z.object({ + apiProvider: z.literal("vertex"), + }), + ), + openAiSchema.merge( + z.object({ + apiProvider: z.literal("openai"), + }), + ), + ollamaSchema.merge( + z.object({ + apiProvider: z.literal("ollama"), + }), + ), + vsCodeLmSchema.merge( + z.object({ + apiProvider: z.literal("vscode-lm"), + }), + ), + lmStudioSchema.merge( + z.object({ + apiProvider: z.literal("lmstudio"), + }), + ), + geminiSchema.merge( + z.object({ + apiProvider: z.literal("gemini"), + }), + ), + openAiNativeSchema.merge( + z.object({ + apiProvider: z.literal("openai-native"), + }), + ), + mistralSchema.merge( + z.object({ + apiProvider: z.literal("mistral"), + }), + ), + deepSeekSchema.merge( + z.object({ + apiProvider: z.literal("deepseek"), + }), + ), + unboundSchema.merge( + z.object({ + apiProvider: z.literal("unbound"), + }), + ), + requestySchema.merge( + z.object({ + apiProvider: z.literal("requesty"), + }), + ), + humanRelaySchema.merge( + z.object({ + apiProvider: z.literal("human-relay"), + }), + ), + fakeAiSchema.merge( + z.object({ + apiProvider: z.literal("fake-ai"), + }), + ), + xaiSchema.merge( + z.object({ + apiProvider: z.literal("xai"), + }), + ), + groqSchema.merge( + z.object({ + apiProvider: z.literal("groq"), + }), + ), + chutesSchema.merge( + z.object({ + apiProvider: z.literal("chutes"), + }), + ), + litellmSchema.merge( + z.object({ + apiProvider: z.literal("litellm"), + }), + ), + defaultSchema, + ]) + .and(genericProviderSettingsSchema) + +export const providerSettingsSchema = z.object({ + apiProvider: providerNamesSchema.optional(), + ...anthropicSchema.shape, + ...glamaSchema.shape, + ...openRouterSchema.shape, + ...bedrockSchema.shape, + ...vertexSchema.shape, + ...openAiSchema.shape, + ...ollamaSchema.shape, + ...vsCodeLmSchema.shape, + ...lmStudioSchema.shape, + ...geminiSchema.shape, + ...openAiNativeSchema.shape, + ...mistralSchema.shape, + ...deepSeekSchema.shape, + ...unboundSchema.shape, + ...requestySchema.shape, + ...humanRelaySchema.shape, + ...fakeAiSchema.shape, + ...xaiSchema.shape, + ...groqSchema.shape, + ...chutesSchema.shape, + ...litellmSchema.shape, + ...genericProviderSettingsSchema.shape, +}) + export type ProviderSettings = z.infer type ProviderSettingsRecord = Record, undefined> @@ -464,7 +653,6 @@ const providerSettingsRecord: ProviderSettingsRecord = { awsRegion: undefined, awsUseCrossRegionInference: undefined, awsUsePromptCache: undefined, - awspromptCacheId: undefined, awsProfile: undefined, awsUseProfile: undefined, awsCustomArn: undefined, @@ -476,7 +664,6 @@ const providerSettingsRecord: ProviderSettingsRecord = { // OpenAI openAiBaseUrl: undefined, openAiApiKey: undefined, - openAiHostHeader: undefined, openAiLegacyFormat: undefined, openAiR1FormatEnabled: undefined, openAiModelId: undefined, @@ -485,6 +672,8 @@ const providerSettingsRecord: ProviderSettingsRecord = { azureApiVersion: undefined, openAiStreamingEnabled: undefined, enableReasoningEffort: undefined, + openAiHostHeader: undefined, // Keep temporarily for backward compatibility during migration + openAiHeaders: undefined, // Ollama ollamaModelId: undefined, ollamaBaseUrl: undefined, @@ -518,7 +707,6 @@ const providerSettingsRecord: ProviderSettingsRecord = { // Generic includeMaxTokens: undefined, reasoningEffort: undefined, - promptCachingEnabled: undefined, diffEnabled: undefined, fuzzyMatchThreshold: undefined, modelTemperature: undefined, @@ -530,6 +718,14 @@ const providerSettingsRecord: ProviderSettingsRecord = { pearaiAgentModels: undefined, // X.AI (Grok) xaiApiKey: undefined, + // Groq + groqApiKey: undefined, + // Chutes AI + chutesApiKey: undefined, + // LiteLLM + litellmBaseUrl: undefined, + litellmApiKey: undefined, + litellmModelId: undefined, } export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Keys[] @@ -540,7 +736,7 @@ export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Key export const globalSettingsSchema = z.object({ currentApiConfigName: z.string().optional(), - listApiConfigMeta: z.array(apiConfigMetaSchema).optional(), + listApiConfigMeta: z.array(providerSettingsEntrySchema).optional(), pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(), lastShownAnnouncementId: z.string().optional(), diff --git a/evals/pnpm-lock.yaml b/evals/pnpm-lock.yaml index b2acaab60d1..e1a787cc184 100644 --- a/evals/pnpm-lock.yaml +++ b/evals/pnpm-lock.yaml @@ -10,16 +10,16 @@ importers: devDependencies: '@dotenvx/dotenvx': specifier: ^1.41.0 - version: 1.41.0 + version: 1.44.0 '@eslint/js': specifier: ^9.25.1 - version: 9.25.1 + version: 9.26.0 eslint: specifier: ^9.25.1 - version: 9.25.1(jiti@2.4.2) + version: 9.26.0(jiti@2.4.2) globals: specifier: ^16.0.0 - version: 16.0.0 + version: 16.1.0 prettier: specifier: ^3.5.3 version: 3.5.3 @@ -28,13 +28,13 @@ importers: version: 4.19.4 turbo: specifier: ^2.5.2 - version: 2.5.2 + version: 2.5.3 typescript: - specifier: ^5.8.3 + specifier: 5.8.3 version: 5.8.3 typescript-eslint: specifier: ^8.31.1 - version: 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + version: 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) apps/cli: dependencies: @@ -142,8 +142,8 @@ importers: specifier: ^3.1.0 version: 3.1.0 lucide-react: - specifier: ^0.479.0 - version: 0.479.0(react@19.0.0) + specifier: ^0.510.0 + version: 0.510.0(react@19.0.0) next: specifier: 15.2.2 version: 15.2.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -231,16 +231,13 @@ importers: version: 5.2.0(eslint@9.22.0(jiti@2.4.2)) eslint-plugin-turbo: specifier: ^2.4.4 - version: 2.4.4(eslint@9.22.0(jiti@2.4.2))(turbo@2.5.2) + version: 2.4.4(eslint@9.22.0(jiti@2.4.2))(turbo@2.5.3) globals: specifier: ^16.0.0 version: 16.0.0 - typescript: - specifier: ^5 - version: 5.8.2 typescript-eslint: specifier: ^8.26.0 - version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3) config/typescript: {} @@ -250,14 +247,14 @@ importers: specifier: workspace:^ version: link:../types '@libsql/client': - specifier: ^0.14.0 - version: 0.14.0 + specifier: ^0.15.0 + version: 0.15.4 drizzle-orm: - specifier: ^0.40.0 - version: 0.40.1(@libsql/client@0.14.0)(gel@2.0.1) + specifier: ^0.43.0 + version: 0.43.1(@libsql/client@0.15.4)(gel@2.0.1) drizzle-zod: specifier: ^0.7.0 - version: 0.7.0(drizzle-orm@0.40.1(@libsql/client@0.14.0)(gel@2.0.1))(zod@3.24.2) + version: 0.7.0(drizzle-orm@0.43.1(@libsql/client@0.15.4)(gel@2.0.1))(zod@3.24.2) p-map: specifier: ^7.0.3 version: 7.0.3 @@ -272,8 +269,8 @@ importers: specifier: workspace:^ version: link:../../config/typescript drizzle-kit: - specifier: ^0.30.5 - version: 0.30.5 + specifier: ^0.31.0 + version: 0.31.1 execa: specifier: ^9.5.2 version: 9.5.2 @@ -346,8 +343,8 @@ packages: resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} engines: {node: '>=6.9.0'} - '@dotenvx/dotenvx@1.41.0': - resolution: {integrity: sha512-lFZOSKLM2/Jm7FXYUIvnciUhMsuEatyxCgau4lnjDD59LaSYiaNLjyjnUL/aYpH1+iaDhD37+mPOzH9kBZlUJQ==} + '@dotenvx/dotenvx@1.44.0': + resolution: {integrity: sha512-18Aa+7KP/L2Kj9lxmT4EJZnsCq/xGIHgzU26rdzsKMhjpeT3YY+qin/dNAnIaVHPZnee7kXpZL55M9htd30r7Q==} hasBin: true '@drizzle-team/brocli@0.10.2': @@ -370,12 +367,6 @@ packages: resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} deprecated: 'Merged into tsx: https://tsx.is' - '@esbuild/aix-ppc64@0.19.12': - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.1': resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} engines: {node: '>=18'} @@ -394,12 +385,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.19.12': - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.1': resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} engines: {node: '>=18'} @@ -418,12 +403,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.19.12': - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.1': resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} engines: {node: '>=18'} @@ -442,12 +421,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.19.12': - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.1': resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} engines: {node: '>=18'} @@ -466,12 +439,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.19.12': - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.1': resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} engines: {node: '>=18'} @@ -490,12 +457,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.19.12': - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.1': resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} engines: {node: '>=18'} @@ -514,12 +475,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.19.12': - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.1': resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} engines: {node: '>=18'} @@ -538,12 +493,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.19.12': - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.1': resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} engines: {node: '>=18'} @@ -562,12 +511,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.19.12': - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.1': resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} engines: {node: '>=18'} @@ -586,12 +529,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.19.12': - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.1': resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} engines: {node: '>=18'} @@ -610,12 +547,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.19.12': - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.1': resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} engines: {node: '>=18'} @@ -634,12 +565,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.19.12': - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.1': resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} engines: {node: '>=18'} @@ -658,12 +583,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.19.12': - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.1': resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} engines: {node: '>=18'} @@ -682,12 +601,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.19.12': - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.1': resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} engines: {node: '>=18'} @@ -706,12 +619,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.19.12': - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.1': resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} engines: {node: '>=18'} @@ -730,12 +637,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.19.12': - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.1': resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} engines: {node: '>=18'} @@ -754,12 +655,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.19.12': - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.1': resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} engines: {node: '>=18'} @@ -790,12 +685,6 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.19.12': - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.1': resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} engines: {node: '>=18'} @@ -826,12 +715,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.19.12': - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.1': resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} engines: {node: '>=18'} @@ -850,12 +733,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.19.12': - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.1': resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} engines: {node: '>=18'} @@ -874,12 +751,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.19.12': - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.1': resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} engines: {node: '>=18'} @@ -898,12 +769,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.19.12': - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.25.1': resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} engines: {node: '>=18'} @@ -922,12 +787,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.19.12': - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.1': resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} engines: {node: '>=18'} @@ -952,6 +811,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -968,8 +833,8 @@ packages: resolution: {integrity: sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.2.1': - resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==} + '@eslint/config-helpers@0.2.2': + resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.12.0': @@ -992,8 +857,8 @@ packages: resolution: {integrity: sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.25.1': - resolution: {integrity: sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==} + '@eslint/js@9.26.0': + resolution: {integrity: sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': @@ -1048,6 +913,10 @@ packages: resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} engines: {node: '>=18.18'} + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + '@img/sharp-darwin-arm64@0.33.5': resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1156,19 +1025,19 @@ packages: '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@libsql/client@0.14.0': - resolution: {integrity: sha512-/9HEKfn6fwXB5aTEEoMeFh4CtG0ZzbncBb1e++OCdVpgKZ/xyMsIVYXm0w7Pv4RUel803vE6LwniB3PqD72R0Q==} + '@libsql/client@0.15.4': + resolution: {integrity: sha512-m8a7giWlhLdfKVIZFd3UlBptWTS+H0toSOL09BxbqzBeFHwuVC+5ewyi4LMBxoy2TLNQGE4lO8cwpsTWmu695w==} - '@libsql/core@0.14.0': - resolution: {integrity: sha512-nhbuXf7GP3PSZgdCY2Ecj8vz187ptHlZQ0VRc751oB2C1W8jQUXKKklvt7t1LJiUTQBVJuadF628eUk+3cRi4Q==} + '@libsql/core@0.15.4': + resolution: {integrity: sha512-NMvh6xnn3vrcd7DNehj0HiJcRWB2a8hHhJUTkOBej3Pf3KB21HOmdOUjXxJ5pGbjWXh4ezQBmHtF5ozFhocXaA==} - '@libsql/darwin-arm64@0.4.7': - resolution: {integrity: sha512-yOL742IfWUlUevnI5PdnIT4fryY3LYTdLm56bnY0wXBw7dhFcnjuA7jrH3oSVz2mjZTHujxoITgAE7V6Z+eAbg==} + '@libsql/darwin-arm64@0.5.8': + resolution: {integrity: sha512-dJRfwCHAKOIgysMbB+PBo3ZmCVuGC02fH57kFEFlqbbUv6wnAZV5g7GErQIv4IlC4VPKAS4RL20fjLUgXE+0Xg==} cpu: [arm64] os: [darwin] - '@libsql/darwin-x64@0.4.7': - resolution: {integrity: sha512-ezc7V75+eoyyH07BO9tIyJdqXXcRfZMbKcLCeF8+qWK5nP8wWuMcfOVywecsXGRbT99zc5eNra4NEx6z5PkSsA==} + '@libsql/darwin-x64@0.5.8': + resolution: {integrity: sha512-ua5ngqJy9o4lyjYDzF8c69YbOAwP2TQXPhDURs8l97b09HHFh5/8gWRNor7vYRpsziwp8TC77DdQ0C84+gP5tg==} cpu: [x64] os: [darwin] @@ -1182,31 +1051,35 @@ packages: '@libsql/isomorphic-ws@0.1.5': resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==} - '@libsql/linux-arm64-gnu@0.4.7': - resolution: {integrity: sha512-WlX2VYB5diM4kFfNaYcyhw5y+UJAI3xcMkEUJZPtRDEIu85SsSFrQ+gvoKfcVh76B//ztSeEX2wl9yrjF7BBCA==} + '@libsql/linux-arm64-gnu@0.5.8': + resolution: {integrity: sha512-6HHZlPbMu+cmCJafg/dwOcWFMu07hTB5teMKU5ke66kqeWLRBnOs5/DnZGVz6q0k+Z4L4UTRbdrnCklR3GmvFg==} cpu: [arm64] os: [linux] - '@libsql/linux-arm64-musl@0.4.7': - resolution: {integrity: sha512-6kK9xAArVRlTCpWeqnNMCoXW1pe7WITI378n4NpvU5EJ0Ok3aNTIC2nRPRjhro90QcnmLL1jPcrVwO4WD1U0xw==} + '@libsql/linux-arm64-musl@0.5.8': + resolution: {integrity: sha512-QGhZadKk3gvrDHa63U7xQrsqET/43E6L7/15oh7I+SINl8meoZAJNTJNYfOUmPM2lPPfNDgr46v4p5ggo6su0A==} cpu: [arm64] os: [linux] - '@libsql/linux-x64-gnu@0.4.7': - resolution: {integrity: sha512-CMnNRCmlWQqqzlTw6NeaZXzLWI8bydaXDke63JTUCvu8R+fj/ENsLrVBtPDlxQ0wGsYdXGlrUCH8Qi9gJep0yQ==} + '@libsql/linux-x64-gnu@0.5.8': + resolution: {integrity: sha512-HUWxOvLE5W287O/vaHWFpZMqaaebEBZvcUqJJ/E+IcC9kmKc6GqDW+fJkfPfosrpGyPNbYDj0w9pIcck0l/oeA==} cpu: [x64] os: [linux] - '@libsql/linux-x64-musl@0.4.7': - resolution: {integrity: sha512-nI6tpS1t6WzGAt1Kx1n1HsvtBbZ+jHn0m7ogNNT6pQHZQj7AFFTIMeDQw/i/Nt5H38np1GVRNsFe99eSIMs9XA==} + '@libsql/linux-x64-musl@0.5.8': + resolution: {integrity: sha512-hfhkPwqzFroU01xUB7ZXFw3bP+jqcGolGLyhEkeh/Rsoune0ucm1KPrU2tqTBqQP4a7lL0nSL1A37nfjIO61Hw==} cpu: [x64] os: [linux] - '@libsql/win32-x64-msvc@0.4.7': - resolution: {integrity: sha512-7pJzOWzPm6oJUxml+PCDRzYQ4A1hTMHAciTAHfFK4fkbDZX33nWPVG7Y3vqdKtslcwAzwmrNDc6sXy2nwWnbiw==} + '@libsql/win32-x64-msvc@0.5.8': + resolution: {integrity: sha512-8hKczus0swLEvXu8N0znWdyFo5QzFnE9mnz7G/sb+eVn+zpPlT6ZdFHZdhQzev9C0to7kvYDj03qESTUIwDiqg==} cpu: [x64] os: [win32] + '@modelcontextprotocol/sdk@1.11.2': + resolution: {integrity: sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ==} + engines: {node: '>=18'} + '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} @@ -2202,8 +2075,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/eslint-plugin@8.31.1': - resolution: {integrity: sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==} + '@typescript-eslint/eslint-plugin@8.32.1': + resolution: {integrity: sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -2217,8 +2090,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.31.1': - resolution: {integrity: sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==} + '@typescript-eslint/parser@8.32.1': + resolution: {integrity: sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2228,8 +2101,8 @@ packages: resolution: {integrity: sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.31.1': - resolution: {integrity: sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==} + '@typescript-eslint/scope-manager@8.32.1': + resolution: {integrity: sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/type-utils@8.26.1': @@ -2239,8 +2112,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/type-utils@8.31.1': - resolution: {integrity: sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==} + '@typescript-eslint/type-utils@8.32.1': + resolution: {integrity: sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2250,8 +2123,8 @@ packages: resolution: {integrity: sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.31.1': - resolution: {integrity: sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==} + '@typescript-eslint/types@8.32.1': + resolution: {integrity: sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.26.1': @@ -2260,8 +2133,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/typescript-estree@8.31.1': - resolution: {integrity: sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==} + '@typescript-eslint/typescript-estree@8.32.1': + resolution: {integrity: sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' @@ -2273,8 +2146,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.31.1': - resolution: {integrity: sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==} + '@typescript-eslint/utils@8.32.1': + resolution: {integrity: sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2284,8 +2157,8 @@ packages: resolution: {integrity: sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.31.1': - resolution: {integrity: sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==} + '@typescript-eslint/visitor-keys@8.32.1': + resolution: {integrity: sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitest/expect@3.0.9': @@ -2320,6 +2193,10 @@ packages: '@xobotyi/scrollbar-width@1.9.5': resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2415,6 +2292,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -2432,6 +2313,10 @@ packages: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -2537,6 +2422,22 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} @@ -2547,6 +2448,10 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + cosmiconfig@7.0.1: resolution: {integrity: sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==} engines: {node: '>=10'} @@ -2594,6 +2499,15 @@ packages: supports-color: optional: true + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -2612,6 +2526,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} @@ -2635,12 +2553,12 @@ packages: resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} - drizzle-kit@0.30.5: - resolution: {integrity: sha512-l6dMSE100u7sDaTbLczibrQZjA35jLsHNqIV+jmhNVO3O8jzM6kywMOmV9uOz9ZVSCMPQhAZEFjL/qDPVrqpUA==} + drizzle-kit@0.31.1: + resolution: {integrity: sha512-PUjYKWtzOzPtdtQlTHQG3qfv4Y0XT8+Eas6UbxCmxTj7qgMf+39dDujf1BP1I+qqZtw9uzwTh8jYtkMuCq+B0Q==} hasBin: true - drizzle-orm@0.40.1: - resolution: {integrity: sha512-aPNhtiJiPfm3qxz1czrnIDkfvkSdKGXYeZkpG55NPTVI186LmK2fBLMi4dsHpPHlJrZeQ92D322YFPHADBALew==} + drizzle-orm@0.43.1: + resolution: {integrity: sha512-dUcDaZtE/zN4RV/xqGrVSMpnEczxd5cIaoDeor7Zst9wOe/HzC/7eAaulywWGYXdDEc9oBPMjayVEDg0ziTLJA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=4' @@ -2650,7 +2568,7 @@ packages: '@neondatabase/serverless': '>=0.10.0' '@op-engineering/op-sqlite': '>=2' '@opentelemetry/api': ^1.4.1 - '@planetscale/database': '>=1' + '@planetscale/database': '>=1.13' '@prisma/client': '*' '@tidbcloud/serverless': '*' '@types/better-sqlite3': '*' @@ -2749,6 +2667,9 @@ packages: resolution: {integrity: sha512-eJAgf9pdv214Hn98FlUzclRMYWF7WfoLlkS9nWMTm1qcCwn6Ad4EGD9lr9HXMBfSrZhYQujRE+p0adPRkctC6A==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + ejs@3.1.8: resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} engines: {node: '>=0.10.0'} @@ -2757,6 +2678,10 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + enhanced-resolve@5.18.1: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} @@ -2820,11 +2745,6 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.25.1: resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} engines: {node: '>=18'} @@ -2839,6 +2759,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -2897,8 +2820,8 @@ packages: jiti: optional: true - eslint@9.25.1: - resolution: {integrity: sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==} + eslint@9.26.0: + resolution: {integrity: sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -2930,6 +2853,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + event-pubsub@5.0.3: resolution: {integrity: sha512-2QiHxshejKgJrYMzSI9MEHrvhmzxBL+eLyiM5IiyjDBySkgwS2+tdtnO3gbx8pEisu/yOFCIhfCb63gCEu0yBQ==} engines: {node: '>=13.0.0'} @@ -2937,6 +2864,14 @@ packages: event-stream@3.3.4: resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + eventsource-parser@3.0.1: + resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -2949,6 +2884,16 @@ packages: resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==} engines: {node: '>=12.0.0'} + express-rate-limit@7.5.0: + resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + engines: {node: '>= 16'} + peerDependencies: + express: ^4.11 || 5 || ^5.0.0-beta.1 + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3002,6 +2947,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3030,6 +2979,14 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + from@0.1.7: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} @@ -3113,6 +3070,10 @@ packages: resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==} engines: {node: '>=18'} + globals@16.1.0: + resolution: {integrity: sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==} + engines: {node: '>=18'} + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -3162,6 +3123,10 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -3173,10 +3138,18 @@ packages: hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.4: + resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} + engines: {node: '>= 4'} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -3199,6 +3172,10 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -3277,6 +3254,9 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3398,8 +3378,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libsql@0.4.7: - resolution: {integrity: sha512-T9eIRCs6b0J1SHKYIvD8+KCJMcWZ900iZyxdnSCdqxN12Z1ijzT+jY5nrk72Jw4B0HGzms2NgpryArlJqvc3Lw==} + libsql@0.5.8: + resolution: {integrity: sha512-+OopMI1wM/NvAJTHf3O3+beHd1YfKLnSVsOGBl3/7UBDZ4ydVadkbBk5Hjjs9d3ALC5rBaftMY59AvwyC8MzPw==} cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] @@ -3537,8 +3517,8 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - lucide-react@0.479.0: - resolution: {integrity: sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ==} + lucide-react@0.510.0: + resolution: {integrity: sha512-p8SQRAMVh7NhsAIETokSqDrc5CHnDLbV29mMnzaXx+Vc/hnqQzwI2r0FMWCcoTXnbw2KEjy48xwpGdEL+ck06Q==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -3555,6 +3535,14 @@ packages: mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -3566,6 +3554,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -3603,6 +3599,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + next-themes@0.4.6: resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: @@ -3633,6 +3633,7 @@ packages: node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} @@ -3685,6 +3686,10 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -3736,6 +3741,10 @@ packages: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3755,6 +3764,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3780,6 +3793,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -3818,6 +3835,10 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + ps-tree@1.2.0: resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} engines: {node: '>= 0.10'} @@ -3827,9 +3848,21 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + react-dom@19.0.0: resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} peerDependencies: @@ -3943,6 +3976,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rtl-css-js@1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} @@ -3956,6 +3993,9 @@ packages: safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -3964,6 +4004,9 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.25.0: resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} @@ -3985,6 +4028,19 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -4001,6 +4057,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -4085,6 +4144,10 @@ packages: stacktrace-js@2.0.2: resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + std-env@3.8.1: resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==} @@ -4230,6 +4293,10 @@ packages: toggle-selection@1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + ts-api-utils@2.0.1: resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} engines: {node: '>=18.12'} @@ -4253,44 +4320,48 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - turbo-darwin-64@2.5.2: - resolution: {integrity: sha512-2aIl0Sx230nLk+Cg2qSVxvPOBWCZpwKNuAMKoROTvWKif6VMpkWWiR9XEPoz7sHeLmCOed4GYGMjL1bqAiIS/g==} + turbo-darwin-64@2.5.3: + resolution: {integrity: sha512-YSItEVBUIvAGPUDpAB9etEmSqZI3T6BHrkBkeSErvICXn3dfqXUfeLx35LfptLDEbrzFUdwYFNmt8QXOwe9yaw==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.5.2: - resolution: {integrity: sha512-MrFYhK/jYu8N6QlqZtqSHi3e4QVxlzqU3ANHTKn3/tThuwTLbNHEvzBPWSj5W7nZcM58dCqi6gYrfRz6bJZyAA==} + turbo-darwin-arm64@2.5.3: + resolution: {integrity: sha512-5PefrwHd42UiZX7YA9m1LPW6x9YJBDErXmsegCkVp+GjmWrADfEOxpFrGQNonH3ZMj77WZB2PVE5Aw3gA+IOhg==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.5.2: - resolution: {integrity: sha512-LxNqUE2HmAJQ/8deoLgMUDzKxd5bKxqH0UBogWa+DF+JcXhtze3UTMr6lEr0dEofdsEUYK1zg8FRjglmwlN5YA==} + turbo-linux-64@2.5.3: + resolution: {integrity: sha512-M9xigFgawn5ofTmRzvjjLj3Lqc05O8VHKuOlWNUlnHPUltFquyEeSkpQNkE/vpPdOR14AzxqHbhhxtfS4qvb1w==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.5.2: - resolution: {integrity: sha512-0MI1Ao1q8zhd+UUbIEsrM+yLq1BsrcJQRGZkxIsHFlGp7WQQH1oR3laBgfnUCNdCotCMD6w4moc9pUbXdOR3bg==} + turbo-linux-arm64@2.5.3: + resolution: {integrity: sha512-auJRbYZ8SGJVqvzTikpg1bsRAsiI9Tk0/SDkA5Xgg0GdiHDH/BOzv1ZjDE2mjmlrO/obr19Dw+39OlMhwLffrw==} cpu: [arm64] os: [linux] - turbo-windows-64@2.5.2: - resolution: {integrity: sha512-hOLcbgZzE5ttACHHyc1ajmWYq4zKT42IC3G6XqgiXxMbS+4eyVYTL+7UvCZBd3Kca1u4TLQdLQjeO76zyDJc2A==} + turbo-windows-64@2.5.3: + resolution: {integrity: sha512-arLQYohuHtIEKkmQSCU9vtrKUg+/1TTstWB9VYRSsz+khvg81eX6LYHtXJfH/dK7Ho6ck+JaEh5G+QrE1jEmCQ==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.5.2: - resolution: {integrity: sha512-fMU41ABhSLa18H8V3Z7BMCGynQ8x+wj9WyBMvWm1jeyRKgkvUYJsO2vkIsy8m0vrwnIeVXKOIn6eSe1ddlBVqw==} + turbo-windows-arm64@2.5.3: + resolution: {integrity: sha512-3JPn66HAynJ0gtr6H+hjY4VHpu1RPKcEwGATvGUTmLmYSYBQieVlnGDRMMoYN066YfyPqnNGCfhYbXfH92Cm0g==} cpu: [arm64] os: [win32] - turbo@2.5.2: - resolution: {integrity: sha512-Qo5lfuStr6LQh3sPQl7kIi243bGU4aHGDQJUf6ylAdGwks30jJFloc9NYHP7Y373+gGU9OS0faA4Mb5Sy8X9Xw==} + turbo@2.5.3: + resolution: {integrity: sha512-iHuaNcq5GZZnr3XDZNuu2LSyCzAOPwDuo5Qt+q64DfsTP1i3T2bKfxJhni2ZQxsvAoxRbuUK5QetJki4qc5aYA==} hasBin: true type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -4314,18 +4385,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - typescript-eslint@8.31.1: - resolution: {integrity: sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==} + typescript-eslint@8.32.1: + resolution: {integrity: sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - typescript@5.8.2: - resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} - engines: {node: '>=14.17'} - hasBin: true - typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -4342,6 +4408,10 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} @@ -4372,6 +4442,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vaul@1.1.2: resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} peerDependencies: @@ -4547,9 +4621,17 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} + zod-to-json-schema@3.24.5: + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + zod@3.24.2: resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod@3.24.4: + resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -4566,7 +4648,7 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 - '@dotenvx/dotenvx@1.41.0': + '@dotenvx/dotenvx@1.44.0': dependencies: commander: 11.1.0 dotenv: 16.5.0 @@ -4599,9 +4681,6 @@ snapshots: '@esbuild-kit/core-utils': 3.3.2 get-tsconfig: 4.10.0 - '@esbuild/aix-ppc64@0.19.12': - optional: true - '@esbuild/aix-ppc64@0.25.1': optional: true @@ -4611,9 +4690,6 @@ snapshots: '@esbuild/android-arm64@0.18.20': optional: true - '@esbuild/android-arm64@0.19.12': - optional: true - '@esbuild/android-arm64@0.25.1': optional: true @@ -4623,9 +4699,6 @@ snapshots: '@esbuild/android-arm@0.18.20': optional: true - '@esbuild/android-arm@0.19.12': - optional: true - '@esbuild/android-arm@0.25.1': optional: true @@ -4635,9 +4708,6 @@ snapshots: '@esbuild/android-x64@0.18.20': optional: true - '@esbuild/android-x64@0.19.12': - optional: true - '@esbuild/android-x64@0.25.1': optional: true @@ -4647,9 +4717,6 @@ snapshots: '@esbuild/darwin-arm64@0.18.20': optional: true - '@esbuild/darwin-arm64@0.19.12': - optional: true - '@esbuild/darwin-arm64@0.25.1': optional: true @@ -4659,9 +4726,6 @@ snapshots: '@esbuild/darwin-x64@0.18.20': optional: true - '@esbuild/darwin-x64@0.19.12': - optional: true - '@esbuild/darwin-x64@0.25.1': optional: true @@ -4671,9 +4735,6 @@ snapshots: '@esbuild/freebsd-arm64@0.18.20': optional: true - '@esbuild/freebsd-arm64@0.19.12': - optional: true - '@esbuild/freebsd-arm64@0.25.1': optional: true @@ -4683,9 +4744,6 @@ snapshots: '@esbuild/freebsd-x64@0.18.20': optional: true - '@esbuild/freebsd-x64@0.19.12': - optional: true - '@esbuild/freebsd-x64@0.25.1': optional: true @@ -4695,9 +4753,6 @@ snapshots: '@esbuild/linux-arm64@0.18.20': optional: true - '@esbuild/linux-arm64@0.19.12': - optional: true - '@esbuild/linux-arm64@0.25.1': optional: true @@ -4707,9 +4762,6 @@ snapshots: '@esbuild/linux-arm@0.18.20': optional: true - '@esbuild/linux-arm@0.19.12': - optional: true - '@esbuild/linux-arm@0.25.1': optional: true @@ -4719,9 +4771,6 @@ snapshots: '@esbuild/linux-ia32@0.18.20': optional: true - '@esbuild/linux-ia32@0.19.12': - optional: true - '@esbuild/linux-ia32@0.25.1': optional: true @@ -4731,9 +4780,6 @@ snapshots: '@esbuild/linux-loong64@0.18.20': optional: true - '@esbuild/linux-loong64@0.19.12': - optional: true - '@esbuild/linux-loong64@0.25.1': optional: true @@ -4743,9 +4789,6 @@ snapshots: '@esbuild/linux-mips64el@0.18.20': optional: true - '@esbuild/linux-mips64el@0.19.12': - optional: true - '@esbuild/linux-mips64el@0.25.1': optional: true @@ -4755,9 +4798,6 @@ snapshots: '@esbuild/linux-ppc64@0.18.20': optional: true - '@esbuild/linux-ppc64@0.19.12': - optional: true - '@esbuild/linux-ppc64@0.25.1': optional: true @@ -4767,9 +4807,6 @@ snapshots: '@esbuild/linux-riscv64@0.18.20': optional: true - '@esbuild/linux-riscv64@0.19.12': - optional: true - '@esbuild/linux-riscv64@0.25.1': optional: true @@ -4779,9 +4816,6 @@ snapshots: '@esbuild/linux-s390x@0.18.20': optional: true - '@esbuild/linux-s390x@0.19.12': - optional: true - '@esbuild/linux-s390x@0.25.1': optional: true @@ -4791,9 +4825,6 @@ snapshots: '@esbuild/linux-x64@0.18.20': optional: true - '@esbuild/linux-x64@0.19.12': - optional: true - '@esbuild/linux-x64@0.25.1': optional: true @@ -4809,9 +4840,6 @@ snapshots: '@esbuild/netbsd-x64@0.18.20': optional: true - '@esbuild/netbsd-x64@0.19.12': - optional: true - '@esbuild/netbsd-x64@0.25.1': optional: true @@ -4827,9 +4855,6 @@ snapshots: '@esbuild/openbsd-x64@0.18.20': optional: true - '@esbuild/openbsd-x64@0.19.12': - optional: true - '@esbuild/openbsd-x64@0.25.1': optional: true @@ -4839,9 +4864,6 @@ snapshots: '@esbuild/sunos-x64@0.18.20': optional: true - '@esbuild/sunos-x64@0.19.12': - optional: true - '@esbuild/sunos-x64@0.25.1': optional: true @@ -4851,9 +4873,6 @@ snapshots: '@esbuild/win32-arm64@0.18.20': optional: true - '@esbuild/win32-arm64@0.19.12': - optional: true - '@esbuild/win32-arm64@0.25.1': optional: true @@ -4863,9 +4882,6 @@ snapshots: '@esbuild/win32-ia32@0.18.20': optional: true - '@esbuild/win32-ia32@0.19.12': - optional: true - '@esbuild/win32-ia32@0.25.1': optional: true @@ -4875,9 +4891,6 @@ snapshots: '@esbuild/win32-x64@0.18.20': optional: true - '@esbuild/win32-x64@0.19.12': - optional: true - '@esbuild/win32-x64@0.25.1': optional: true @@ -4894,9 +4907,9 @@ snapshots: eslint: 9.22.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.6.1(eslint@9.25.1(jiti@2.4.2))': + '@eslint-community/eslint-utils@4.7.0(eslint@9.26.0(jiti@2.4.2))': dependencies: - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -4919,7 +4932,7 @@ snapshots: '@eslint/config-helpers@0.1.0': {} - '@eslint/config-helpers@0.2.1': {} + '@eslint/config-helpers@0.2.2': {} '@eslint/core@0.12.0': dependencies: @@ -4959,7 +4972,7 @@ snapshots: '@eslint/js@9.22.0': {} - '@eslint/js@9.25.1': {} + '@eslint/js@9.26.0': {} '@eslint/object-schema@2.1.6': {} @@ -5008,6 +5021,8 @@ snapshots: '@humanwhocodes/retry@0.4.2': {} + '@humanwhocodes/retry@0.4.3': {} + '@img/sharp-darwin-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.0.4 @@ -5085,25 +5100,25 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.0': {} - '@libsql/client@0.14.0': + '@libsql/client@0.15.4': dependencies: - '@libsql/core': 0.14.0 + '@libsql/core': 0.15.4 '@libsql/hrana-client': 0.7.0 js-base64: 3.7.7 - libsql: 0.4.7 + libsql: 0.5.8 promise-limit: 2.7.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@libsql/core@0.14.0': + '@libsql/core@0.15.4': dependencies: js-base64: 3.7.7 - '@libsql/darwin-arm64@0.4.7': + '@libsql/darwin-arm64@0.5.8': optional: true - '@libsql/darwin-x64@0.4.7': + '@libsql/darwin-x64@0.5.8': optional: true '@libsql/hrana-client@0.7.0': @@ -5126,21 +5141,36 @@ snapshots: - bufferutil - utf-8-validate - '@libsql/linux-arm64-gnu@0.4.7': + '@libsql/linux-arm64-gnu@0.5.8': optional: true - '@libsql/linux-arm64-musl@0.4.7': + '@libsql/linux-arm64-musl@0.5.8': optional: true - '@libsql/linux-x64-gnu@0.4.7': + '@libsql/linux-x64-gnu@0.5.8': optional: true - '@libsql/linux-x64-musl@0.4.7': + '@libsql/linux-x64-musl@0.5.8': optional: true - '@libsql/win32-x64-msvc@0.4.7': + '@libsql/win32-x64-msvc@0.5.8': optional: true + '@modelcontextprotocol/sdk@1.11.2': + dependencies: + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + express: 5.1.0 + express-rate-limit: 7.5.0(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.0 + zod: 3.24.4 + zod-to-json-schema: 3.24.5(zod@3.24.4) + transitivePeerDependencies: + - supports-color + '@neon-rs/load@0.0.4': {} '@next/env@15.2.2': {} @@ -5193,7 +5223,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@petamoriken/float16@3.9.2': {} + '@petamoriken/float16@3.9.2': + optional: true '@radix-ui/number@1.1.0': {} @@ -6016,60 +6047,60 @@ snapshots: dependencies: '@types/node': 20.17.24 - '@typescript-eslint/eslint-plugin@8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)': + '@typescript-eslint/eslint-plugin@8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.26.1 - '@typescript-eslint/type-utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) - '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + '@typescript-eslint/type-utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.26.1 eslint: 9.22.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 2.0.1(typescript@5.8.2) - typescript: 5.8.2 + ts-api-utils: 2.0.1(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/type-utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.31.1 - eslint: 9.25.1(jiti@2.4.2) + '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.1 + eslint: 9.26.0(jiti@2.4.2) graphemer: 1.4.0 - ignore: 5.3.2 + ignore: 7.0.4 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)': + '@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.26.1 '@typescript-eslint/types': 8.26.1 - '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.26.1 debug: 4.4.0 eslint: 9.22.0(jiti@2.4.2) - typescript: 5.8.2 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.1 debug: 4.4.0 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -6079,28 +6110,28 @@ snapshots: '@typescript-eslint/types': 8.26.1 '@typescript-eslint/visitor-keys': 8.26.1 - '@typescript-eslint/scope-manager@8.31.1': + '@typescript-eslint/scope-manager@8.32.1': dependencies: - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/visitor-keys': 8.32.1 - '@typescript-eslint/type-utils@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)': + '@typescript-eslint/type-utils@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.2) - '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3) debug: 4.4.0 eslint: 9.22.0(jiti@2.4.2) - ts-api-utils: 2.0.1(typescript@5.8.2) - typescript: 5.8.2 + ts-api-utils: 2.0.1(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) debug: 4.4.0 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -6108,9 +6139,9 @@ snapshots: '@typescript-eslint/types@8.26.1': {} - '@typescript-eslint/types@8.31.1': {} + '@typescript-eslint/types@8.32.1': {} - '@typescript-eslint/typescript-estree@8.26.1(typescript@5.8.2)': + '@typescript-eslint/typescript-estree@8.26.1(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 8.26.1 '@typescript-eslint/visitor-keys': 8.26.1 @@ -6119,43 +6150,43 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.1 - ts-api-utils: 2.0.1(typescript@5.8.2) - typescript: 5.8.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.31.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.32.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/visitor-keys': 8.32.1 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.1 + semver: 7.7.2 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)': + '@typescript-eslint/utils@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.6.1(eslint@9.22.0(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.26.1 '@typescript-eslint/types': 8.26.1 - '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.3) eslint: 9.22.0(jiti@2.4.2) - typescript: 5.8.2 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/utils@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@9.25.1(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - eslint: 9.25.1(jiti@2.4.2) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -6165,9 +6196,9 @@ snapshots: '@typescript-eslint/types': 8.26.1 eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@8.31.1': + '@typescript-eslint/visitor-keys@8.32.1': dependencies: - '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/types': 8.32.1 eslint-visitor-keys: 4.2.0 '@vitest/expect@3.0.9': @@ -6212,6 +6243,11 @@ snapshots: '@xobotyi/scrollbar-width@1.9.5': {} + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.14.1): dependencies: acorn: 8.14.1 @@ -6326,6 +6362,20 @@ snapshots: balanced-match@1.0.2: {} + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.0 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -6345,6 +6395,8 @@ snapshots: dependencies: streamsearch: 1.1.0 + bytes@3.1.2: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -6460,6 +6512,16 @@ snapshots: concat-map@0.0.1: {} + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 @@ -6476,6 +6538,11 @@ snapshots: core-util-is@1.0.3: {} + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cosmiconfig@7.0.1: dependencies: '@types/parse-json': 4.0.2 @@ -6531,6 +6598,11 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.1: + dependencies: + ms: 2.1.3 + optional: true + deep-eql@5.0.2: {} deep-is@0.1.4: {} @@ -6551,6 +6623,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + depd@2.0.0: {} + detect-libc@2.0.2: {} detect-libc@2.0.3: {} @@ -6565,24 +6639,23 @@ snapshots: dotenv@16.5.0: {} - drizzle-kit@0.30.5: + drizzle-kit@0.31.1: dependencies: '@drizzle-team/brocli': 0.10.2 '@esbuild-kit/esm-loader': 2.6.5 - esbuild: 0.19.12 - esbuild-register: 3.6.0(esbuild@0.19.12) - gel: 2.0.1 + esbuild: 0.25.3 + esbuild-register: 3.6.0(esbuild@0.25.3) transitivePeerDependencies: - supports-color - drizzle-orm@0.40.1(@libsql/client@0.14.0)(gel@2.0.1): + drizzle-orm@0.43.1(@libsql/client@0.15.4)(gel@2.0.1): optionalDependencies: - '@libsql/client': 0.14.0 + '@libsql/client': 0.15.4 gel: 2.0.1 - drizzle-zod@0.7.0(drizzle-orm@0.40.1(@libsql/client@0.14.0)(gel@2.0.1))(zod@3.24.2): + drizzle-zod@0.7.0(drizzle-orm@0.43.1(@libsql/client@0.15.4)(gel@2.0.1))(zod@3.24.2): dependencies: - drizzle-orm: 0.40.1(@libsql/client@0.14.0)(gel@2.0.1) + drizzle-orm: 0.43.1(@libsql/client@0.15.4)(gel@2.0.1) zod: 3.24.2 dunder-proto@1.0.1: @@ -6602,12 +6675,16 @@ snapshots: '@noble/curves': 1.9.0 '@noble/hashes': 1.8.0 + ee-first@1.1.1: {} + ejs@3.1.8: dependencies: jake: 10.9.2 emoji-regex@8.0.0: {} + encodeurl@2.0.0: {} + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 @@ -6617,7 +6694,8 @@ snapshots: dependencies: ansi-colors: 4.1.3 - env-paths@3.0.0: {} + env-paths@3.0.0: + optional: true error-ex@1.3.2: dependencies: @@ -6727,10 +6805,10 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild-register@3.6.0(esbuild@0.19.12): + esbuild-register@3.6.0(esbuild@0.25.3): dependencies: debug: 4.4.0 - esbuild: 0.19.12 + esbuild: 0.25.3 transitivePeerDependencies: - supports-color @@ -6759,32 +6837,6 @@ snapshots: '@esbuild/win32-ia32': 0.18.20 '@esbuild/win32-x64': 0.18.20 - esbuild@0.19.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - esbuild@0.25.1: optionalDependencies: '@esbuild/aix-ppc64': 0.25.1 @@ -6843,6 +6895,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@4.0.0: {} @@ -6879,11 +6933,11 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.4.4(eslint@9.22.0(jiti@2.4.2))(turbo@2.5.2): + eslint-plugin-turbo@2.4.4(eslint@9.22.0(jiti@2.4.2))(turbo@2.5.3): dependencies: dotenv: 16.0.3 eslint: 9.22.0(jiti@2.4.2) - turbo: 2.5.2 + turbo: 2.5.3 eslint-scope@8.3.0: dependencies: @@ -6936,19 +6990,20 @@ snapshots: transitivePeerDependencies: - supports-color - eslint@9.25.1(jiti@2.4.2): + eslint@9.26.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@9.25.1(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.20.0 - '@eslint/config-helpers': 0.2.1 + '@eslint/config-helpers': 0.2.2 '@eslint/core': 0.13.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.25.1 + '@eslint/js': 9.26.0 '@eslint/plugin-kit': 0.2.8 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.2 + '@humanwhocodes/retry': 0.4.3 + '@modelcontextprotocol/sdk': 1.11.2 '@types/estree': 1.0.7 '@types/json-schema': 7.0.15 ajv: 6.12.6 @@ -6973,6 +7028,7 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 + zod: 3.24.4 optionalDependencies: jiti: 2.4.2 transitivePeerDependencies: @@ -7000,6 +7056,8 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + event-pubsub@5.0.3: dependencies: copyfiles: 2.4.1 @@ -7015,6 +7073,12 @@ snapshots: stream-combiner: 0.0.4 through: 2.3.8 + eventsource-parser@3.0.1: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.1 + execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -7044,6 +7108,42 @@ snapshots: expect-type@1.2.0: {} + express-rate-limit@7.5.0(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -7099,6 +7199,17 @@ snapshots: dependencies: to-regex-range: 5.0.1 + finalhandler@2.1.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -7121,6 +7232,10 @@ snapshots: dependencies: fetch-blob: 3.2.0 + forwarded@0.2.0: {} + + fresh@2.0.0: {} + from@0.1.7: {} fs-jetpack@4.3.1: @@ -7151,13 +7266,14 @@ snapshots: gel@2.0.1: dependencies: '@petamoriken/float16': 3.9.2 - debug: 4.4.0 + debug: 4.4.1 env-paths: 3.0.0 - semver: 7.7.1 + semver: 7.7.2 shell-quote: 1.8.2 which: 4.0.0 transitivePeerDependencies: - supports-color + optional: true get-caller-file@2.0.5: {} @@ -7219,6 +7335,8 @@ snapshots: globals@16.0.0: {} + globals@16.1.0: {} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -7289,14 +7407,28 @@ snapshots: dependencies: function-bind: 1.1.2 + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + human-signals@2.1.0: {} human-signals@8.0.0: {} hyphenate-style-name@1.1.0: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.2: {} + ignore@7.0.4: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -7321,6 +7453,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + ipaddr.js@1.9.1: {} + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -7398,6 +7532,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-promise@4.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -7511,18 +7647,18 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libsql@0.4.7: + libsql@0.5.8: dependencies: '@neon-rs/load': 0.0.4 detect-libc: 2.0.2 optionalDependencies: - '@libsql/darwin-arm64': 0.4.7 - '@libsql/darwin-x64': 0.4.7 - '@libsql/linux-arm64-gnu': 0.4.7 - '@libsql/linux-arm64-musl': 0.4.7 - '@libsql/linux-x64-gnu': 0.4.7 - '@libsql/linux-x64-musl': 0.4.7 - '@libsql/win32-x64-msvc': 0.4.7 + '@libsql/darwin-arm64': 0.5.8 + '@libsql/darwin-x64': 0.5.8 + '@libsql/linux-arm64-gnu': 0.5.8 + '@libsql/linux-arm64-musl': 0.5.8 + '@libsql/linux-x64-gnu': 0.5.8 + '@libsql/linux-x64-musl': 0.5.8 + '@libsql/win32-x64-msvc': 0.5.8 lightningcss-darwin-arm64@1.29.2: optional: true @@ -7621,7 +7757,7 @@ snapshots: dependencies: yallist: 4.0.0 - lucide-react@0.479.0(react@19.0.0): + lucide-react@0.510.0(react@19.0.0): dependencies: react: 19.0.0 @@ -7635,6 +7771,10 @@ snapshots: mdn-data@2.0.14: {} + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -7644,6 +7784,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + mimic-fn@2.1.0: {} minimatch@3.1.2: @@ -7679,6 +7825,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.0.0: {} + next-themes@0.4.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: react: 19.0.0 @@ -7776,6 +7924,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -7838,6 +7990,8 @@ snapshots: parse-ms@4.0.0: {} + parseurl@1.3.3: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -7848,6 +8002,8 @@ snapshots: path-parse@1.0.7: {} + path-to-regexp@8.2.0: {} + path-type@4.0.0: {} pathe@2.0.3: {} @@ -7864,6 +8020,8 @@ snapshots: picomatch@4.0.2: {} + pkce-challenge@5.0.0: {} + pluralize@8.0.0: {} possible-typed-array-names@1.1.0: {} @@ -7898,14 +8056,32 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + ps-tree@1.2.0: dependencies: event-stream: 3.3.4 punycode@2.3.1: {} + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + queue-microtask@1.2.3: {} + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + react-dom@19.0.0(react@19.0.0): dependencies: react: 19.0.0 @@ -8060,6 +8236,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.38.0 fsevents: 2.3.3 + router@2.2.0: + dependencies: + debug: 4.4.0 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + rtl-css-js@1.16.1: dependencies: '@babel/runtime': 7.26.10 @@ -8078,6 +8264,8 @@ snapshots: safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -8089,6 +8277,8 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safer-buffer@2.1.2: {} + scheduler@0.25.0: {} screenfull@5.2.0: {} @@ -8101,6 +8291,33 @@ snapshots: semver@7.7.1: {} + semver@7.7.2: {} + + send@1.2.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -8125,6 +8342,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setprototypeof@1.2.0: {} + sharp@0.33.5: dependencies: color: 4.2.3 @@ -8158,7 +8377,8 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.2: {} + shell-quote@1.8.2: + optional: true side-channel-list@1.0.0: dependencies: @@ -8238,6 +8458,8 @@ snapshots: stack-generator: 2.0.10 stacktrace-gps: 3.1.2 + statuses@2.0.1: {} + std-env@3.8.1: {} stream-combiner@0.0.4: @@ -8372,9 +8594,11 @@ snapshots: toggle-selection@1.0.6: {} - ts-api-utils@2.0.1(typescript@5.8.2): + toidentifier@1.0.1: {} + + ts-api-utils@2.0.1(typescript@5.8.3): dependencies: - typescript: 5.8.2 + typescript: 5.8.3 ts-api-utils@2.1.0(typescript@5.8.3): dependencies: @@ -8391,37 +8615,43 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - turbo-darwin-64@2.5.2: + turbo-darwin-64@2.5.3: optional: true - turbo-darwin-arm64@2.5.2: + turbo-darwin-arm64@2.5.3: optional: true - turbo-linux-64@2.5.2: + turbo-linux-64@2.5.3: optional: true - turbo-linux-arm64@2.5.2: + turbo-linux-arm64@2.5.3: optional: true - turbo-windows-64@2.5.2: + turbo-windows-64@2.5.3: optional: true - turbo-windows-arm64@2.5.2: + turbo-windows-arm64@2.5.3: optional: true - turbo@2.5.2: + turbo@2.5.3: optionalDependencies: - turbo-darwin-64: 2.5.2 - turbo-darwin-arm64: 2.5.2 - turbo-linux-64: 2.5.2 - turbo-linux-arm64: 2.5.2 - turbo-windows-64: 2.5.2 - turbo-windows-arm64: 2.5.2 + turbo-darwin-64: 2.5.3 + turbo-darwin-arm64: 2.5.3 + turbo-linux-64: 2.5.3 + turbo-linux-arm64: 2.5.3 + turbo-windows-64: 2.5.3 + turbo-windows-arm64: 2.5.3 type-check@0.4.0: dependencies: prelude-ls: 1.2.1 + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -8455,28 +8685,26 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2): + typescript-eslint@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) - '@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) - '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + '@typescript-eslint/eslint-plugin': 8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.22.0(jiti@2.4.2) - typescript: 5.8.2 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - typescript-eslint@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3): + typescript-eslint@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.25.1(jiti@2.4.2) + '@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color - typescript@5.8.2: {} - typescript@5.8.3: {} unbox-primitive@1.1.0: @@ -8490,6 +8718,8 @@ snapshots: unicorn-magic@0.3.0: {} + unpipe@1.0.0: {} + untildify@4.0.0: {} uri-js@4.4.1: @@ -8513,6 +8743,8 @@ snapshots: util-deprecate@1.0.2: {} + vary@1.1.2: {} + vaul@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: '@radix-ui/react-dialog': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -8691,4 +8923,10 @@ snapshots: yoctocolors@2.1.1: {} + zod-to-json-schema@3.24.5(zod@3.24.4): + dependencies: + zod: 3.24.4 + zod@3.24.2: {} + + zod@3.24.4: {} diff --git a/evals/scripts/setup.sh b/evals/scripts/setup.sh index f58f80793e9..de3c05393a9 100755 --- a/evals/scripts/setup.sh +++ b/evals/scripts/setup.sh @@ -286,35 +286,49 @@ fi # To reset VSCode: # rm -rvf ~/.vscode && rm -rvf ~/Library/Application\ Support/Code -echo "🔌 Installing Visual Studio Code extensions..." +echo -n "🔌 Installing Visual Studio Code extensions... " code --install-extension golang.go &>/dev/null || exit 1 code --install-extension dbaeumer.vscode-eslint&>/dev/null || exit 1 code --install-extension redhat.java &>/dev/null || exit 1 code --install-extension ms-python.python&>/dev/null || exit 1 code --install-extension rust-lang.rust-analyzer &>/dev/null || exit 1 -code --install-extension rooveterinaryinc.roo-cline &>/dev/null || exit 1 + +if ! code --list-extensions 2>/dev/null | grep -q "rooveterinaryinc.roo-cline"; then + code --install-extension rooveterinaryinc.roo-cline &>/dev/null || exit 1 +fi + +echo "✅ Done" if [[ ! -d "../../evals" ]]; then - if gh auth status &>/dev/null; then - read -p "🔗 Would you like to be able to share eval results? (Y/n): " fork_evals + echo -n "🔗 Cloning evals repository... " - if [[ "$fork_evals" =~ ^[Yy]|^$ ]]; then - gh repo fork cte/evals --clone ../../evals || exit 1 - else - gh repo clone cte/evals ../../evals || exit 1 - fi + if gh auth status &>/dev/null; then + gh repo clone cte/evals ../../evals || exit 1 else git clone https://github.com/cte/evals.git ../../evals || exit 1 fi + + echo "✅ Done" +else + echo -n "🔄 Updating evals repository... " + + (cd ../../evals && \ + git checkout -f &>/dev/null && \ + git clean -f -d &>/dev/null && \ + git checkout main &>/dev/null && \ + git pull &>/dev/null) || { echo "❌ Failed to update evals repository."; exit 1; } + + echo "✅ Done" fi if [[ ! -s .env ]]; then cp .env.sample .env || exit 1 fi -echo "🗄️ Syncing Roo Code evals database..." +echo -n "🗄️ Syncing Roo Code evals database... " pnpm --filter @evals/db db:push &>/dev/null || exit 1 pnpm --filter @evals/db db:enable-wal &>/dev/null || exit 1 +echo "✅ Done" if ! grep -q "OPENROUTER_API_KEY" .env; then read -p "🔐 Enter your OpenRouter API key (sk-or-v1-...): " openrouter_api_key @@ -323,14 +337,11 @@ if ! grep -q "OPENROUTER_API_KEY" .env; then echo "OPENROUTER_API_KEY=$openrouter_api_key" >> .env || exit 1 fi -if [[ ! -s "../bin/roo-code-latest.vsix" ]]; then - build_extension -else - read -p "💻 Do you want to build a new version of the Roo Code extension? (y/N): " build_extension +current_version=$(code --list-extensions --show-versions 2>/dev/null | grep roo) +read -p "💻 Do you want to build a new version of the Roo Code extension? [currently $current_version] (y/N): " build_extension - if [[ "$build_extension" =~ ^[Yy]$ ]]; then - build_extension - fi +if [[ "$build_extension" =~ ^[Yy]$ ]]; then + build_extension fi echo -e "\n🚀 You're ready to rock and roll! \n" diff --git a/git b/git deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/jest.config.js b/jest.config.js index cd4944c5477..e05776918e9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -14,7 +14,6 @@ module.exports = { allowJs: true, }, diagnostics: false, - isolatedModules: true, }, ], }, diff --git a/locales/ca/CODE_OF_CONDUCT.md b/locales/ca/CODE_OF_CONDUCT.md index a433818592e..0d572628186 100644 --- a/locales/ca/CODE_OF_CONDUCT.md +++ b/locales/ca/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • Català • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Codi de Conducta del Pacte de Col·laboradors ## El nostre Compromís diff --git a/locales/ca/CONTRIBUTING.md b/locales/ca/CONTRIBUTING.md index 5f2beefc79e..41a227c497c 100644 --- a/locales/ca/CONTRIBUTING.md +++ b/locales/ca/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Contribuir a Roo Code - -Estem entusiasmats que estigueu interessats en contribuir a Roo Code. Ja sigui arreglant un error, afegint una funcionalitat o millorant la nostra documentació, cada contribució fa que Roo Code sigui més intel·ligent! Per mantenir la nostra comunitat vibrant i acollidora, tots els membres han de complir el nostre [Codi de Conducta](CODE_OF_CONDUCT.md). - -## Uniu-vos a la nostra comunitat - -Encoratgem fortament a tots els col·laboradors a unir-se a la nostra [comunitat de Discord](https://discord.gg/roocode)! Formar part del nostre servidor de Discord us ajuda a: - -- Obtenir ajuda i orientació en temps real sobre les vostres contribucions -- Connectar amb altres col·laboradors i membres de l'equip principal -- Mantenir-vos al dia sobre els desenvolupaments i prioritats del projecte -- Participar en discussions que configuren el futur de Roo Code -- Trobar oportunitats de col·laboració amb altres desenvolupadors +[English](../../CONTRIBUTING.md) • Català • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -## Informar d'errors o problemes +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -Els informes d'errors ajuden a millorar Roo Code per a tothom! Abans de crear un nou informe, si us plau [cerqueu entre els existents](https://github.com/RooVetGit/Roo-Code/issues) per evitar duplicats. Quan estigueu a punt per informar d'un error, dirigiu-vos a la nostra [pàgina d'incidències](https://github.com/RooVetGit/Roo-Code/issues/new/choose) on trobareu una plantilla per ajudar-vos a completar la informació rellevant. - -
- 🔐 Important: Si descobriu una vulnerabilitat de seguretat, utilitzeu l'eina de seguretat de Github per informar-ne privadament. -
+# Contribuir a Roo Code -## Decidir en què treballar +Roo Code és un projecte impulsat per la comunitat i valorem molt cada contribució. Per simplificar la col·laboració, treballem amb un enfoc [Issue-First](#enfoc-issue-first), que significa que tots els [Pull Requests (PRs)](#enviar-un-pull-request) han d'estar primer vinculats a una Issue de GitHub. Si us plau, llegeix aquesta guia amb atenció. -Buscant una bona primera contribució? Consulteu les incidències a la secció "Issue [Unassigned]" del nostre [Projecte de Github de Roo Code](https://github.com/orgs/RooVetGit/projects/1). Aquestes estan específicament seleccionades per a nous col·laboradors i àrees on ens encantaria rebre ajuda! +## Taula de continguts -També donem la benvinguda a contribucions a la nostra [documentació](https://docs.roocode.com/)! Ja sigui corregint errors tipogràfics, millorant guies existents o creant nou contingut educatiu - ens encantaria construir un repositori de recursos impulsat per la comunitat que ajudi a tothom a aprofitar al màxim Roo Code. Podeu fer clic a "Editar aquesta pàgina" a qualsevol pàgina per arribar ràpidament al lloc correcte a Github per editar el fitxer, o podeu anar directament a https://github.com/RooVetGit/Roo-Code-Docs. +- [Abans de contribuir](#abans-de-contribuir) +- [Trobar i planificar la teva contribució](#trobar-i-planificar-la-teva-contribució) +- [Procés de desenvolupament i enviament](#procés-de-desenvolupament-i-enviament) +- [Legal](#legal) -Si esteu planejant treballar en una funcionalitat més gran, si us plau creeu primer una [sol·licitud de funcionalitat](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) perquè puguem discutir si s'alinea amb la visió de Roo Code. També podeu consultar el nostre [Full de Ruta del Projecte](#full-de-ruta-del-projecte) a continuació per veure si la vostra idea s'ajusta a la nostra direcció estratègica. +## Abans de contribuir -## Full de Ruta del Projecte +### 1. Codi de conducta -Roo Code té un full de ruta de desenvolupament clar que guia les nostres prioritats i direcció futura. Entendre el nostre full de ruta us pot ajudar a: +Tots els col·laboradors han de complir el nostre [Codi de conducta](./CODE_OF_CONDUCT.md). -- Alinear les vostres contribucions amb els objectius del projecte -- Identificar àrees on la vostra experiència seria més valuosa -- Entendre el context darrere de certes decisions de disseny -- Trobar inspiració per a noves funcionalitats que donin suport a la nostra visió +### 2. Fulla de ruta del projecte -El nostre full de ruta actual se centra en sis pilars clau: +La nostra fulla de ruta orienta la direcció del projecte. Alinea les teves contribucions amb aquests objectius clau: -### Suport de Proveïdors +### Fiabilitat primer -Aspirem a donar suport a tants proveïdors com sigui possible: +- Garantir que l'edició de diferències i l'execució de comandes siguin consistentment fiables +- Reduir els punts de fricció que desanimen l'ús regular +- Garantir un funcionament fluid en tots els idiomes i plataformes +- Ampliar el suport robust per a una àmplia varietat de proveïdors i models d'IA -- Suport més versàtil per a "OpenAI Compatible" -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Suport millorat per a Ollama i LM Studio +### Experiència d'usuari millorada -### Suport de Models +- Simplificar la interfície d'usuari per a més claredat i intuïció +- Millorar contínuament el flux de treball per satisfer les altes expectatives dels desenvolupadors -Volem que Roo funcioni tan bé com sigui possible amb tants models com sigui possible, inclosos els models locals: +### Lideratge en rendiment dels agents -- Suport de models locals a través de prompts de sistema personalitzats i fluxos de treball -- Avaluacions de rendiment i casos de prova +- Establir punts de referència d'avaluació (evals) complets per mesurar la productivitat real +- Facilitar que tothom pugui executar i interpretar aquestes avaluacions fàcilment +- Proporcionar millores que demostrin increments clars en les puntuacions d'avaluació -### Suport de Sistemes +Esmenta la relació amb aquestes àrees als teus PRs. -Volem que Roo funcioni bé a l'ordinador de tothom: +### 3. Uneix-te a la comunitat Roo Code -- Integració de terminal multiplataforma -- Suport sòlid i consistent per a Mac, Windows i Linux +- **Principal:** Uneix-te al nostre [Discord](https://discord.gg/roocode) i envia un DM a **Hannes Rudolph (`hrudolph`)**. +- **Alternativa:** Els col·laboradors experimentats poden participar directament via [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -### Documentació +## Trobar i planificar la teva contribució -Volem documentació completa i accessible per a tots els usuaris i col·laboradors: +### Tipus de contribucions -- Guies d'usuari i tutorials ampliats -- Documentació clara de l'API -- Millor orientació per als col·laboradors -- Recursos de documentació multilingües -- Exemples interactius i mostres de codi +- **Correcció d'errors:** Solucionar problemes en el codi. +- **Noves funcionalitats:** Afegir noves capacitats. +- **Documentació:** Millorar guies i claredat. -### Estabilitat +### Enfoc Issue-First -Volem reduir significativament el nombre d'errors i augmentar les proves automatitzades: +Totes les contribucions han de començar amb una Issue de GitHub. -- Interruptor de registre de depuració -- Botó de còpia "Informació de Màquina/Tasca" per enviar amb sol·licituds d'error/suport +- **Revisar issues existents:** Cerca a [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Crear una issue:** Utilitza les plantilles adequades: + - **Errors:** Plantilla "Bug Report". + - **Funcionalitats:** Plantilla "Detailed Feature Proposal". Es requereix aprovació abans de començar. +- **Reclamar issues:** Comenta i espera l'assignació oficial. -### Internacionalització +**Els PRs sense issues aprovades poden ser tancats.** -Volem que Roo parli l'idioma de tothom: +### Decidir en què treballar -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +- Consulta el [Projecte GitHub](https://github.com/orgs/RooVetGit/projects/1) per trobar "Good First Issues" no assignades. +- Per a documentació, visita [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). -Donem especialment la benvinguda a contribucions que avancin els nostres objectius del full de ruta. Si esteu treballant en alguna cosa que s'alinea amb aquests pilars, si us plau mencioneu-ho a la descripció del vostre PR. +### Informar d'errors -## Configuració de desenvolupament +- Comprova primer els informes existents. +- Crea nous informes d'errors utilitzant la [plantilla "Bug Report"](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Vulnerabilitats de seguretat:** Informa de manera privada via [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -1. **Cloneu** el repositori: +## Procés de desenvolupament i enviament -```sh -git clone https://github.com/RooVetGit/Roo-Code.git -``` +### Configuració de desenvolupament -2. **Instal·leu les dependències**: +1. **Fork & Clona:** -```sh -npm run install:all ``` - -3. **Inicieu la vista web (aplicació Vite/React amb HMR)**: - -```sh -npm run dev +git clone https://github.com/EL_TEU_USUARI/Roo-Code.git ``` -4. **Depuració**: - Premeu `F5` (o **Execució** → **Inicia la depuració**) a VSCode per obrir una nova sessió amb Roo Code carregat. - -Els canvis a la vista web apareixeran immediatament. Els canvis a l'extensió principal requeriran reiniciar l'amfitrió de l'extensió. - -Alternativament, podeu crear un .vsix i instal·lar-lo directament a VSCode: +2. **Instal·la dependències:** -```sh -npm run build ``` - -Apareixerà un fitxer `.vsix` al directori `bin/` que es pot instal·lar amb: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Escriure i enviar codi - -Qualsevol persona pot contribuir amb codi a Roo Code, però us demanem que seguiu aquestes directrius per assegurar que les vostres contribucions puguin ser integrades sense problemes: - -1. **Mantingueu les Pull Requests enfocades** - - - Limiteu les PR a una sola funcionalitat o correcció d'error - - Dividiu els canvis més grans en PR més petites i relacionades - - Dividiu els canvis en commits lògics que puguin ser revisats independentment - -2. **Qualitat del codi** +3. **Depuració:** Obre amb VS Code (`F5`). - - Totes les PR han de passar les comprovacions de CI que inclouen tant anàlisi com formatació - - Solucioneu qualsevol advertència o error d'ESLint abans d'enviar - - Responeu a tots els comentaris d'Ellipsis, la nostra eina automatitzada de revisió de codi - - Seguiu les millors pràctiques de TypeScript i mantingueu la seguretat de tipus +### Guia per escriure codi -3. **Proves** +- Un PR centrat per funcionalitat o correcció. +- Segueix les millors pràctiques d'ESLint i TypeScript. +- Escriu missatges de commit clars i descriptius que facin referència a issues (ex: `Fixes #123`). +- Proporciona proves completes (`npm test`). +- Rebaseja a la branca `main` més recent abans d'enviar. - - Afegiu proves per a noves funcionalitats - - Executeu `npm test` per assegurar que totes les proves passin - - Actualitzeu les proves existents si els vostres canvis les afecten - - Incloeu tant proves unitàries com proves d'integració quan sigui apropiat +### Enviar un Pull Request -4. **Directrius de commits** +- Comença com a **PR en esborrany** si busques feedback primerenc. +- Descriu clarament els teus canvis seguint la Plantilla de Pull Request. +- Proporciona captures de pantalla/vídeos per a canvis d'UI. +- Indica si es necessiten actualitzacions de documentació. - - Escriviu missatges de commit clars i descriptius - - Feu referència a incidències rellevants als commits utilitzant #número-incidència +### Política de Pull Request -5. **Abans d'enviar** +- Ha de fer referència a issues preaprovades i assignades. +- Els PRs que no segueixen la política poden ser tancats. +- Els PRs han de passar els tests de CI, alinear-se amb la fulla de ruta i tenir documentació clara. - - Rebaseu la vostra branca sobre l'última main - - Assegureu-vos que la vostra branca es construeix amb èxit - - Comproveu doblement que totes les proves passen - - Reviseu els vostres canvis per qualsevol codi de depuració o registres de consola +### Procés de revisió -6. **Descripció de la Pull Request** - - Descriviu clarament què fan els vostres canvis - - Incloeu passos per provar els canvis - - Enumereu qualsevol canvi important - - Afegiu captures de pantalla per a canvis d'interfície d'usuari +- **Triatge diari:** Comprovacions ràpides pels mantenidors. +- **Revisió setmanal detallada:** Avaluació exhaustiva. +- **Itera ràpidament** en base al feedback. -## Acord de contribució +## Legal -En enviar una pull request, accepteu que les vostres contribucions estaran sota la mateixa llicència que el projecte ([Apache 2.0](../LICENSE)). +En enviar un pull request, acceptes que les teves contribucions es llicenciïn sota la Llicència Apache 2.0, d'acord amb la llicència de Roo Code. diff --git a/locales/ca/README.md b/locales/ca/README.md index 4bbcfb1e36e..42853a8b785 100644 --- a/locales/ca/README.md +++ b/locales/ca/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • Català • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • Català • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ Tant si busqueu un soci de programació flexible, un arquitecte de sistemes o rols especialitzats com un enginyer de control de qualitat o un gestor de producte, Roo Code us pot ajudar a construir programari de manera més eficient. -Consulteu el [CHANGELOG](../CHANGELOG.md) per a actualitzacions i correccions detallades. +Consulteu el [CHANGELOG](../../CHANGELOG.md) per a actualitzacions i correccions detallades. --- -## 🎉 Roo Code 3.15 Llançat +## 🎉 Roo Code 3.17 Llançat -Roo Code 3.15 aporta noves funcionalitats i millores basades en els vostres comentaris! +Roo Code 3.17 aporta noves i potents funcionalitats i millores basades en els vostres comentaris! -- **Memòria cau per a prompts a Vertex** - Vertex AI ara suporta memòria cau de prompts, millorant els temps de resposta i reduint els costos d'API. -- **Mecanisme alternatiu per al Terminal** - S'ha implementat un mecanisme alternatiu quan la integració de shell del terminal de VSCode falla, assegurant operacions de terminal més fiables. -- **Fragments de codi millorats** - S'ha millorat la renderització i interacció amb fragments de codi a la interfície de xat per a una millor llegibilitat i usabilitat. +- **Memòria cau implícita per a Gemini** - Les crides a l'API de Gemini ara es desen automàticament a la memòria cau, reduint els costos d'API. +- **Selecció de mode més intel·ligent** - Les definicions de mode ara poden incloure orientació sobre quan s'ha d'utilitzar cada mode, permetent una millor orquestració. +- **Condensació intel·ligent del context** - Resumeix de manera intel·ligent l'historial de converses quan el context s'omple en lloc de truncar-lo (activeu-ho a Configuració -> Experimental). --- @@ -178,32 +178,36 @@ Ens encanten les contribucions de la comunitat! Comenceu llegint el nostre [CONT Gràcies a tots els nostres col·laboradors que han ajudat a millorar Roo Code! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Llicència diff --git a/locales/de/CODE_OF_CONDUCT.md b/locales/de/CODE_OF_CONDUCT.md index d2fc00beb81..b1fa6e3128f 100644 --- a/locales/de/CODE_OF_CONDUCT.md +++ b/locales/de/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • Deutsch • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Verhaltenskodex für Mitwirkende ## Unser Versprechen diff --git a/locales/de/CONTRIBUTING.md b/locales/de/CONTRIBUTING.md index 1aa7a5b6c2a..df96ef42b4a 100644 --- a/locales/de/CONTRIBUTING.md +++ b/locales/de/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Beitrag zu Roo Code - -Wir freuen uns, dass du Interesse hast, zu Roo Code beizutragen. Ob du einen Fehler behebst, eine Funktion hinzufügst oder unsere Dokumentation verbesserst, jeder Beitrag macht Roo Code intelligenter! Um unsere Community lebendig und einladend zu halten, müssen sich alle Mitglieder an unseren [Verhaltenskodex](CODE_OF_CONDUCT.md) halten. - -## Treten Sie unserer Community bei - -Wir ermutigen alle Mitwirkenden nachdrücklich, unserer [Discord-Community](https://discord.gg/roocode) beizutreten! Teil unseres Discord-Servers zu sein, hilft dir: - -- Echtzeit-Hilfe und Anleitung für deine Beiträge zu erhalten -- Mit anderen Mitwirkenden und Kernteammitgliedern in Kontakt zu treten -- Über Projektentwicklungen und Prioritäten auf dem Laufenden zu bleiben -- An Diskussionen teilzunehmen, die die Zukunft von Roo Code gestalten -- Kooperationsmöglichkeiten mit anderen Entwicklern zu finden +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • Deutsch • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -## Fehler oder Probleme melden +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -Fehlerberichte helfen, Roo Code für alle besser zu machen! Bevor du ein neues Issue erstellst, bitte [suche in bestehenden Issues](https://github.com/RooVetGit/Roo-Code/issues), um Duplikate zu vermeiden. Wenn du bereit bist, einen Fehler zu melden, gehe zu unserer [Issues-Seite](https://github.com/RooVetGit/Roo-Code/issues/new/choose), wo du eine Vorlage findest, die dir beim Ausfüllen der relevanten Informationen hilft. - -
- 🔐 Wichtig: Wenn du eine Sicherheitslücke entdeckst, nutze bitte das Github-Sicherheitstool, um sie privat zu melden. -
+# Beitrag zu Roo Code -## Entscheiden, woran Sie arbeiten möchten +Roo Code ist ein Community-getriebenes Projekt, und wir schätzen jeden Beitrag sehr. Für eine reibungslose Zusammenarbeit arbeiten wir nach dem [Issue-First-Ansatz](#issue-first-ansatz), was bedeutet, dass alle [Pull Requests (PRs)](#einen-pull-request-einreichen) zuerst mit einem GitHub Issue verknüpft werden müssen. Bitte lies diesen Leitfaden sorgfältig durch. -Suchst du nach einem guten ersten Beitrag? Schau dir Issues im Abschnitt "Issue [Unassigned]" unseres [Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1) Github-Projekts an. Diese sind speziell für neue Mitwirkende und Bereiche ausgewählt, in denen wir Hilfe gebrauchen könnten! +## Inhaltsverzeichnis -Wir begrüßen auch Beiträge zu unserer [Dokumentation](https://docs.roocode.com/)! Ob du Tippfehler korrigierst, bestehende Anleitungen verbesserst oder neue Bildungsinhalte erstellst - wir würden gerne ein Community-geführtes Repository von Ressourcen aufbauen, das jedem hilft, das Beste aus Roo Code herauszuholen. Du kannst auf jeder Seite auf "Edit this page" klicken, um schnell zur richtigen Stelle in Github zu gelangen, um die Datei zu bearbeiten, oder du kannst direkt zu https://github.com/RooVetGit/Roo-Code-Docs gehen. +- [Bevor du beiträgst](#bevor-du-beiträgst) +- [Beitrag finden & planen](#beitrag-finden--planen) +- [Entwicklung & Einreichung](#entwicklung--einreichung) +- [Rechtliches](#rechtliches) -Wenn du an einer größeren Funktion arbeiten möchtest, erstelle bitte zuerst eine [Funktionsanfrage](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop), damit wir diskutieren können, ob sie mit der Vision von Roo Code übereinstimmt. Du kannst auch unseren [Projekt-Fahrplan](#projekt-fahrplan) unten überprüfen, um zu sehen, ob deine Idee mit unserer strategischen Ausrichtung übereinstimmt. +## Bevor du beiträgst -## Projekt-Fahrplan +### 1. Verhaltenskodex -Roo Code hat einen klaren Entwicklungsfahrplan, der unsere Prioritäten und zukünftige Richtung leitet. Das Verständnis unseres Fahrplans kann dir helfen: +Alle Mitwirkenden müssen sich an unseren [Verhaltenskodex](./CODE_OF_CONDUCT.md) halten. -- Deine Beiträge mit den Projektzielen abzustimmen -- Bereiche zu identifizieren, in denen deine Expertise am wertvollsten wäre -- Den Kontext hinter bestimmten Designentscheidungen zu verstehen -- Inspiration für neue Funktionen zu finden, die unsere Vision unterstützen +### 2. Projekt-Roadmap -Unser aktueller Fahrplan konzentriert sich auf sechs Schlüsselsäulen: +Unsere Roadmap gibt die Richtung des Projekts vor. Richte deine Beiträge an diesen Schlüsselzielen aus: -### Provider-Unterstützung +### Zuverlässigkeit an erster Stelle -Wir möchten so viele Provider wie möglich gut unterstützen: +- Sicherstellen, dass Diff-Bearbeitung und Befehlsausführung durchgängig zuverlässig sind. +- Reibungspunkte reduzieren, die von der regelmäßigen Nutzung abhalten. +- Reibungslosen Betrieb in allen Sprachen und auf allen Plattformen garantieren. +- Robuste Unterstützung für eine Vielzahl von KI-Anbietern und -Modellen ausbauen. -- Vielseitigere "OpenAI Compatible" Unterstützung -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Verbesserte Unterstützung für Ollama und LM Studio +### Verbesserte Benutzererfahrung -### Modell-Unterstützung +- Die Benutzeroberfläche für mehr Klarheit und Intuitivität optimieren. +- Den Workflow kontinuierlich verbessern, um den hohen Erwartungen gerecht zu werden, die Entwickler an täglich genutzte Tools stellen. -Wir wollen, dass Roo mit so vielen Modellen wie möglich gut funktioniert, einschließlich lokaler Modelle: +### Führend bei der Agentenleistung -- Lokale Modellunterstützung durch benutzerdefiniertes System-Prompting und Workflows -- Benchmark-Evaluierungen und Testfälle +- Umfassende Evaluierungsmaßstäbe (Evals) etablieren, um die Produktivität in der realen Welt zu messen. +- Es für jeden einfach machen, diese Evals durchzuführen und zu interpretieren. +- Verbesserungen liefern, die klare Steigerungen der Eval-Ergebnisse zeigen. -### System-Unterstützung +Erwähne die Ausrichtung an diesen Bereichen in deinen PRs. -Wir wollen, dass Roo auf jedem Computer gut läuft: +### 3. Werde Teil der Roo Code Community -- Plattformübergreifende Terminal-Integration -- Starke und konsistente Unterstützung für Mac, Windows und Linux +- **Hauptweg:** Tritt unserem [Discord](https://discord.gg/roocode) bei und schreibe eine DM an **Hannes Rudolph (`hrudolph`)**. +- **Alternative:** Erfahrene Mitwirkende können sich direkt über [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1) beteiligen. -### Dokumentation +## Beitrag finden & planen -Wir wollen umfassende, zugängliche Dokumentation für alle Benutzer und Mitwirkenden: +### Beitragsarten -- Erweiterte Benutzerhandbücher und Tutorials -- Klare API-Dokumentation -- Bessere Anleitung für Mitwirkende -- Mehrsprachige Dokumentationsressourcen -- Interaktive Beispiele und Codebeispiele +- **Bugfixes:** Fehler im Code beheben. +- **Neue Features:** Neue Funktionen hinzufügen. +- **Dokumentation:** Anleitungen verbessern und klarer gestalten. -### Stabilität +### Issue-First-Ansatz -Wir wollen die Anzahl der Fehler deutlich reduzieren und die automatisierte Testabdeckung erhöhen: +Alle Beiträge müssen mit einem GitHub Issue beginnen. -- Debug-Logging-Schalter -- "Maschinen-/Aufgabeninformationen" Kopier-Button zum Einsenden mit Fehler-/Support-Anfragen +- **Bestehende Issues prüfen**: Durchsuche die [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Issue erstellen**: Nutze die passenden Vorlagen: + - **Bugs:** "Bug Report"-Vorlage. + - **Features:** "Detailed Feature Proposal"-Vorlage. Vor dem Start ist eine Genehmigung erforderlich. +- **Issues beanspruchen**: Kommentiere und warte auf die offizielle Zuweisung. -### Internationalisierung +**PRs ohne genehmigte Issues können geschlossen werden.** -Wir wollen, dass Roo die Sprache aller spricht: +### Was soll ich machen? -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +- Schau im [GitHub Project](https://github.com/orgs/RooVetGit/projects/1) nach nicht zugewiesenen "Good First Issues". +- Für Dokumentation besuche das [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs) Repository. -Wir begrüßen besonders Beiträge, die unsere Fahrplanziele voranbringen. Wenn du an etwas arbeitest, das mit diesen Säulen übereinstimmt, erwähne es bitte in deiner PR-Beschreibung. +### Bugs melden -## Entwicklungs-Setup +- Prüfe zuerst, ob der Bug bereits gemeldet wurde. +- Erstelle neue Bug-Reports mit der ["Bug Report"-Vorlage](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Sicherheitslücken:** Melde diese privat über [Security Advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -1. **Klone** das Repository: +## Entwicklung & Einreichung -```sh -git clone https://github.com/RooVetGit/Roo-Code.git -``` +### Entwicklungs-Setup -2. **Installiere Abhängigkeiten**: +1. **Fork & Clone:** -```sh -npm run install:all ``` - -3. **Starte die Webansicht (Vite/React-App mit HMR)**: - -```sh -npm run dev +git clone https://github.com/DEIN_USERNAME/Roo-Code.git ``` -4. **Debugging**: - Drücke `F5` (oder **Ausführen** → **Debugging starten**) in VSCode, um eine neue Sitzung mit geladenem Roo Code zu öffnen. - -Änderungen an der Webansicht erscheinen sofort. Änderungen an der Kern-Erweiterung erfordern einen Neustart des Erweiterungs-Hosts. - -Alternativ kannst du eine .vsix-Datei erstellen und direkt in VSCode installieren: +2. **Abhängigkeiten installieren:** -```sh -npm run build ``` - -Eine `.vsix`-Datei erscheint im `bin/`-Verzeichnis, die mit folgendem Befehl installiert werden kann: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Code schreiben und einreichen - -Jeder kann Code zu Roo Code beitragen, aber wir bitten dich, diese Richtlinien zu befolgen, um sicherzustellen, dass deine Beiträge reibungslos integriert werden können: - -1. **Halten Sie Pull Requests fokussiert** - - - Beschränke PRs auf eine einzelne Funktion oder Fehlerbehebung - - Teile größere Änderungen in kleinere, zusammenhängende PRs auf - - Unterteile Änderungen in logische Commits, die unabhängig überprüft werden können - -2. **Codequalität** +3. **Debugging:** Öffne mit VS Code (`F5`). - - Alle PRs müssen CI-Prüfungen bestehen, die sowohl Linting als auch Formatierung umfassen - - Behebe alle ESLint-Warnungen oder -Fehler vor dem Einreichen - - Reagiere auf alle Rückmeldungen von Ellipsis, unserem automatisierten Code-Review-Tool - - Folge TypeScript-Best-Practices und halte die Typsicherheit aufrecht +### Code-Richtlinien -3. **Testen** +- Ein fokussierter PR pro Feature oder Fix. +- Folge den ESLint und TypeScript Best Practices. +- Schreibe klare, beschreibende Commits, die auf Issues verweisen (z.B. `Fixes #123`). +- Liefere gründliche Tests (`npm test`). +- Rebase auf den neuesten `main`-Branch vor dem Einreichen. - - Füge Tests für neue Funktionen hinzu - - Führe `npm test` aus, um sicherzustellen, dass alle Tests bestanden werden - - Aktualisiere bestehende Tests, wenn deine Änderungen diese beeinflussen - - Schließe sowohl Unit-Tests als auch Integrationstests ein, wo angemessen +### Einen Pull Request einreichen -4. **Commit-Richtlinien** +- Beginne als **Draft PR**, wenn du frühes Feedback suchst. +- Beschreibe deine Änderungen klar und folge der Pull Request Vorlage. +- Stelle Screenshots/Videos für UI-Änderungen bereit. +- Gib an, ob Dokumentationsaktualisierungen notwendig sind. - - Schreibe klare, beschreibende Commit-Nachrichten - - Verweise auf relevante Issues in Commits mit #issue-nummer +### Pull Request Richtlinie -5. **Vor dem Einreichen** +- Muss auf vorab genehmigte, zugewiesene Issues verweisen. +- PRs ohne Einhaltung der Richtlinie können geschlossen werden. +- PRs sollten CI-Tests bestehen, zur Roadmap passen und klare Dokumentation haben. - - Rebase deinen Branch auf den neuesten main-Branch - - Stelle sicher, dass dein Branch erfolgreich baut - - Überprüfe erneut, dass alle Tests bestanden werden - - Prüfe deine Änderungen auf Debug-Code oder Konsolenausgaben +### Review-Prozess -6. **Pull Request Beschreibung** - - Beschreibe klar, was deine Änderungen bewirken - - Füge Schritte zum Testen der Änderungen hinzu - - Liste alle Breaking Changes auf - - Füge Screenshots für UI-Änderungen hinzu +- **Tägliche Triage:** Schnelle Prüfungen durch Maintainer. +- **Wöchentliche Tiefenprüfung:** Umfassende Bewertung. +- **Zeitnah auf Feedback reagieren** und entsprechend iterieren. -## Beitragsvereinbarung +## Rechtliches -Durch das Einreichen eines Pull Requests stimmst du zu, dass deine Beiträge unter derselben Lizenz wie das Projekt ([Apache 2.0](../LICENSE)) lizenziert werden. +Mit deinem Beitrag erklärst du dich damit einverstanden, dass deine Beiträge unter der Apache 2.0 Lizenz lizenziert werden, konsistent mit der Lizenzierung von Roo Code. diff --git a/locales/de/README.md b/locales/de/README.md index 19ad5ebb6e1..348a06d3873 100644 --- a/locales/de/README.md +++ b/locales/de/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • Deutsch • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • Deutsch • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ Ob Sie einen flexiblen Coding-Partner, einen Systemarchitekten oder spezialisierte Rollen wie einen QA-Ingenieur oder Produktmanager suchen, Roo Code kann Ihnen helfen, Software effizienter zu entwickeln. -Sehen Sie sich das [CHANGELOG](../CHANGELOG.md) für detaillierte Updates und Fehlerbehebungen an. +Sehen Sie sich das [CHANGELOG](../../CHANGELOG.md) für detaillierte Updates und Fehlerbehebungen an. --- -## 🎉 Roo Code 3.15 veröffentlicht +## 🎉 Roo Code 3.17 veröffentlicht -Roo Code 3.15 bringt neue Funktionen und Verbesserungen basierend auf deinem Feedback! +Roo Code 3.17 bringt leistungsstarke neue Funktionen und Verbesserungen basierend auf deinem Feedback! -- **Prompt-Caching für Vertex** - Vertex AI unterstützt jetzt Prompt-Caching, was die Antwortzeiten verbessert und API-Kosten reduziert. -- **Terminal-Fallback** - Ein Fallback-Mechanismus wurde implementiert, der greift, wenn die VSCode-Terminal-Shell-Integration fehlschlägt, um zuverlässigere Terminal-Operationen zu gewährleisten. -- **Verbesserte Code-Snippets** - Verbesserte Darstellung und Interaktion mit Code-Snippets in der Chat-Oberfläche für bessere Lesbarkeit und Benutzerfreundlichkeit. +- **Implizites Caching für Gemini** - Gemini API-Aufrufe werden jetzt automatisch gecacht, was die API-Kosten reduziert. +- **Intelligentere Modusauswahl** - Modusdefinitionen können jetzt Hinweise enthalten, wann jeder Modus verwendet werden sollte, was eine bessere Orchestrierung ermöglicht. +- **Intelligente Kontextkondensierung** - Fasst den Gesprächsverlauf intelligent zusammen, wenn der Kontext voll ist, anstatt ihn abzuschneiden (aktiviere dies in Einstellungen -> Experimentell). --- @@ -178,32 +178,36 @@ Wir lieben Community-Beiträge! Beginnen Sie mit dem Lesen unserer [CONTRIBUTING Danke an alle unsere Mitwirkenden, die geholfen haben, Roo Code zu verbessern! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Lizenz diff --git a/locales/es/CODE_OF_CONDUCT.md b/locales/es/CODE_OF_CONDUCT.md index 9375965456f..e978484e35e 100644 --- a/locales/es/CODE_OF_CONDUCT.md +++ b/locales/es/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • Español • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Código de Conducta del Pacto de Colaboradores ## Nuestro Compromiso diff --git a/locales/es/CONTRIBUTING.md b/locales/es/CONTRIBUTING.md index 14cf3a96f36..545af5174e7 100644 --- a/locales/es/CONTRIBUTING.md +++ b/locales/es/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Contribuir a Roo Code - -Estamos encantados de que estés interesado en contribuir a Roo Code. Ya sea que estés arreglando un error, añadiendo una función o mejorando nuestra documentación, ¡cada contribución hace que Roo Code sea más inteligente! Para mantener nuestra comunidad vibrante y acogedora, todos los miembros deben adherirse a nuestro [Código de Conducta](CODE_OF_CONDUCT.md). - -## Únete a nuestra comunidad - -¡Animamos encarecidamente a todos los colaboradores a unirse a nuestra [comunidad de Discord](https://discord.gg/roocode)! Formar parte de nuestro servidor de Discord te ayuda a: - -- Obtener ayuda y orientación en tiempo real para tus contribuciones -- Conectar con otros colaboradores y miembros del equipo principal -- Mantenerte actualizado sobre los desarrollos y prioridades del proyecto -- Participar en discusiones que dan forma al futuro de Roo Code -- Encontrar oportunidades de colaboración con otros desarrolladores +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • Español • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -## Reportar errores o problemas +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -¡Los informes de errores ayudan a mejorar Roo Code para todos! Antes de crear un nuevo issue, por favor [busca entre los existentes](https://github.com/RooVetGit/Roo-Code/issues) para evitar duplicados. Cuando estés listo para reportar un error, dirígete a nuestra [página de issues](https://github.com/RooVetGit/Roo-Code/issues/new/choose) donde encontrarás una plantilla para ayudarte a completar la información relevante. - -
- 🔐 Importante: Si descubres una vulnerabilidad de seguridad, por favor utiliza la herramienta de seguridad de GitHub para reportarla de forma privada. -
+# Contribuir a Roo Code -## Decidir en qué trabajar +Roo Code es un proyecto impulsado por la comunidad, y valoramos profundamente cada contribución. Para agilizar la colaboración, operamos con un enfoque [Issue-First](#enfoque-issue-first), lo que significa que todos los [Pull Requests (PRs)](#enviar-un-pull-request) deben estar vinculados primero a un Issue de GitHub. Por favor, revisa esta guía cuidadosamente. -¿Buscas una buena primera contribución? Revisa los issues en la sección "Issue [Unassigned]" de nuestro [Proyecto GitHub de Roo Code](https://github.com/orgs/RooVetGit/projects/1). ¡Estos están específicamente seleccionados para nuevos colaboradores y áreas donde nos encantaría recibir ayuda! +## Tabla de Contenidos -¡También damos la bienvenida a contribuciones a nuestra [documentación](https://docs.roocode.com/)! Ya sea arreglando errores tipográficos, mejorando guías existentes o creando nuevo contenido educativo - nos encantaría construir un repositorio de recursos impulsado por la comunidad que ayude a todos a sacar el máximo provecho de Roo Code. Puedes hacer clic en "Edit this page" en cualquier página para llegar rápidamente al lugar correcto en Github para editar el archivo, o puedes ir directamente a https://github.com/RooVetGit/Roo-Code-Docs. +- [Antes de Contribuir](#antes-de-contribuir) +- [Encontrar y Planificar tu Contribución](#encontrar-y-planificar-tu-contribución) +- [Proceso de Desarrollo y Envío](#proceso-de-desarrollo-y-envío) +- [Legal](#legal) -Si estás planeando trabajar en una función más grande, por favor crea una [solicitud de función](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) primero para que podamos discutir si se alinea con la visión de Roo Code. También puedes consultar nuestra [Hoja de Ruta del Proyecto](#hoja-de-ruta-del-proyecto) a continuación para ver si tu idea encaja con nuestra dirección estratégica. +## Antes de Contribuir -## Hoja de Ruta del Proyecto +### 1. Código de Conducta -Roo Code tiene una hoja de ruta de desarrollo clara que guía nuestras prioridades y dirección futura. Entender nuestra hoja de ruta puede ayudarte a: +Todos los colaboradores deben adherirse a nuestro [Código de Conducta](./CODE_OF_CONDUCT.md). -- Alinear tus contribuciones con los objetivos del proyecto -- Identificar áreas donde tu experiencia sería más valiosa -- Entender el contexto detrás de ciertas decisiones de diseño -- Encontrar inspiración para nuevas funciones que apoyen nuestra visión +### 2. Hoja de Ruta del Proyecto -Nuestra hoja de ruta actual se centra en seis pilares clave: +Nuestra hoja de ruta guía la dirección del proyecto. Alinea tus contribuciones con estos objetivos clave: -### Soporte de Proveedores +### Confiabilidad Primero -Nuestro objetivo es dar soporte a tantos proveedores como sea posible: +- Garantizar que la edición de diferencias y la ejecución de comandos sean consistentemente confiables. +- Reducir los puntos de fricción que disuaden el uso regular. +- Garantizar un funcionamiento fluido en todos los idiomas y plataformas. +- Ampliar el soporte sólido para una amplia variedad de proveedores y modelos de IA. -- Soporte más versátil para "OpenAI Compatible" -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Soporte mejorado para Ollama y LM Studio +### Experiencia de Usuario Mejorada -### Soporte de Modelos +- Simplificar la interfaz de usuario para mayor claridad e intuitividad. +- Mejorar continuamente el flujo de trabajo para satisfacer las altas expectativas que los desarrolladores tienen para herramientas de uso diario. -Queremos que Roo funcione bien con tantos modelos como sea posible, incluidos los modelos locales: +### Liderazgo en Rendimiento de Agentes -- Soporte para modelos locales a través de system prompting personalizado y flujos de trabajo -- Evaluaciones de benchmarking y casos de prueba +- Establecer evaluaciones comparativas completas (evals) para medir la productividad en el mundo real. +- Facilitar que todos puedan ejecutar e interpretar estas evaluaciones fácilmente. +- Ofrecer mejoras que demuestren aumentos claros en las puntuaciones de evaluación. -### Soporte de Sistemas +Menciona la alineación con estas áreas en tus PRs. -Queremos que Roo funcione bien en el ordenador de todos: +### 3. Únete a la Comunidad Roo Code -- Integración de terminal multiplataforma -- Soporte sólido y consistente para Mac, Windows y Linux +- **Principal:** Únete a nuestro [Discord](https://discord.gg/roocode) y envía un DM a **Hannes Rudolph (`hrudolph`)**. +- **Alternativa:** Los colaboradores experimentados pueden participar directamente a través de [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -### Documentación +## Encontrar y Planificar tu Contribución -Queremos una documentación completa y accesible para todos los usuarios y colaboradores: +### Tipos de Contribuciones -- Guías de usuario y tutoriales ampliados -- Documentación clara de la API -- Mejor orientación para colaboradores -- Recursos de documentación multilingües -- Ejemplos interactivos y muestras de código +- **Corrección de errores:** Solucionar problemas en el código. +- **Nuevas funciones:** Añadir funcionalidades. +- **Documentación:** Mejorar guías y claridad. -### Estabilidad +### Enfoque Issue-First -Queremos disminuir significativamente el número de errores y aumentar las pruebas automatizadas: +Todas las contribuciones deben comenzar con un Issue de GitHub. -- Interruptor de registro de depuración -- Botón de copia de "Información de Máquina/Tarea" para enviar con solicitudes de soporte/errores +- **Revisar issues existentes**: Busca en [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Crear un issue**: Usa las plantillas apropiadas: + - **Errores:** Plantilla "Bug Report". + - **Funciones:** Plantilla "Detailed Feature Proposal". Se requiere aprobación antes de comenzar. +- **Reclamar issues**: Comenta y espera la asignación oficial. -### Internacionalización +**Los PRs sin issues aprobados pueden ser cerrados.** -Queremos que Roo hable el idioma de todos: +### Decidir en Qué Trabajar -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +- Revisa el [Proyecto GitHub](https://github.com/orgs/RooVetGit/projects/1) para "Good First Issues" no asignados. +- Para documentación, visita [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). -Damos especialmente la bienvenida a contribuciones que avancen nuestros objetivos de la hoja de ruta. Si estás trabajando en algo que se alinea con estos pilares, por favor menciónalo en la descripción de tu PR. +### Reportar Errores -## Configuración de desarrollo +- Primero verifica si ya existen reportes. +- Crea nuevos reportes de errores usando la [plantilla "Bug Report"](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Problemas de seguridad**: Reporta de forma privada a través de [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -1. **Clona** el repositorio: +## Proceso de Desarrollo y Envío -```sh -git clone https://github.com/RooVetGit/Roo-Code.git -``` +### Configuración de Desarrollo -2. **Instala dependencias**: +1. **Fork & Clona:** -```sh -npm run install:all ``` - -3. **Inicia la vista web (aplicación Vite/React con HMR)**: - -```sh -npm run dev +git clone https://github.com/TU_USUARIO/Roo-Code.git ``` -4. **Depuración**: - Presiona `F5` (o **Ejecutar** → **Iniciar depuración**) en VSCode para abrir una nueva sesión con Roo Code cargado. - -Los cambios en la vista web aparecerán inmediatamente. Los cambios en la extensión principal requerirán un reinicio del host de extensión. - -Alternativamente, puedes construir un archivo .vsix e instalarlo directamente en VSCode: +2. **Instalar Dependencias:** -```sh -npm run build ``` - -Un archivo `.vsix` aparecerá en el directorio `bin/` que puede ser instalado con: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Escribir y enviar código - -Cualquiera puede contribuir con código a Roo Code, pero te pedimos que sigas estas pautas para asegurar que tus contribuciones puedan integrarse sin problemas: - -1. **Mantén los Pull Requests enfocados** - - - Limita los PRs a una sola función o corrección de errores - - Divide los cambios más grandes en PRs más pequeños y relacionados - - Separa los cambios en commits lógicos que puedan revisarse independientemente - -2. **Calidad del código** +3. **Depuración:** Abre con VS Code (`F5`). - - Todos los PRs deben pasar las comprobaciones de CI que incluyen tanto linting como formateo - - Soluciona cualquier advertencia o error de ESLint antes de enviar - - Responde a todos los comentarios de Ellipsis, nuestra herramienta automatizada de revisión de código - - Sigue las mejores prácticas de TypeScript y mantén la seguridad de tipos +### Guía para Escribir Código -3. **Pruebas** +- Un PR enfocado por función o corrección. +- Sigue las mejores prácticas de ESLint y TypeScript. +- Escribe commits claros y descriptivos que referencien issues (ej., `Fixes #123`). +- Proporciona pruebas exhaustivas (`npm test`). +- Rebase sobre la última rama `main` antes de enviar. - - Añade pruebas para nuevas funciones - - Ejecuta `npm test` para asegurar que todas las pruebas pasen - - Actualiza las pruebas existentes si tus cambios les afectan - - Incluye tanto pruebas unitarias como de integración cuando sea apropiado +### Enviar un Pull Request -4. **Directrices para commits** +- Comienza como **PR en Borrador** si buscas feedback temprano. +- Describe claramente tus cambios siguiendo la Plantilla de Pull Request. +- Proporciona capturas de pantalla/videos para cambios en la UI. +- Indica si son necesarias actualizaciones de documentación. - - Escribe mensajes de commit claros y descriptivos - - Haz referencia a los issues relevantes en los commits usando #número-de-issue +### Política de Pull Request -5. **Antes de enviar** +- Debe referenciar issues preaprobados y asignados. +- Los PRs que no cumplan con la política pueden ser cerrados. +- Los PRs deben pasar las pruebas de CI, alinearse con la hoja de ruta y tener documentación clara. - - Haz rebase de tu rama sobre la última main - - Asegúrate de que tu rama se construye correctamente - - Comprueba que todas las pruebas están pasando - - Revisa tus cambios para detectar código de depuración o logs de consola +### Proceso de Revisión -6. **Descripción del Pull Request** - - Describe claramente lo que hacen tus cambios - - Incluye pasos para probar los cambios - - Enumera cualquier cambio que rompa la compatibilidad - - Añade capturas de pantalla para cambios en la interfaz de usuario +- **Triage Diario:** Revisiones rápidas por parte de los mantenedores. +- **Revisión Semanal en Profundidad:** Evaluación integral. +- **Itera rápidamente** basándote en el feedback. -## Acuerdo de contribución +## Legal -Al enviar un pull request, aceptas que tus contribuciones serán licenciadas bajo la misma licencia que el proyecto ([Apache 2.0](../LICENSE)). +Al contribuir, aceptas que tus contribuciones serán licenciadas bajo la Licencia Apache 2.0, consistente con la licencia de Roo Code. diff --git a/locales/es/README.md b/locales/es/README.md index a5d8df2f8e2..c25925687c7 100644 --- a/locales/es/README.md +++ b/locales/es/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • Español • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • Español • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ Ya sea que busques un socio de programación flexible, un arquitecto de sistemas o roles especializados como ingeniero de control de calidad o gestor de productos, Roo Code puede ayudarte a construir software de manera más eficiente. -Consulta el [CHANGELOG](../CHANGELOG.md) para ver actualizaciones detalladas y correcciones. +Consulta el [CHANGELOG](../../CHANGELOG.md) para ver actualizaciones detalladas y correcciones. --- -## 🎉 Roo Code 3.15 Lanzado +## 🎉 Roo Code 3.17 Lanzado -¡Roo Code 3.15 trae nuevas funcionalidades y mejoras basadas en tus comentarios! +¡Roo Code 3.17 trae potentes nuevas funcionalidades y mejoras basadas en tus comentarios! -- **Caché para prompts en Vertex** - Vertex AI ahora admite caché de prompts, mejorando los tiempos de respuesta y reduciendo los costos de API. -- **Mecanismo de respaldo para terminal** - Se implementó un mecanismo de respaldo cuando la integración de shell de terminal de VSCode falla, asegurando operaciones de terminal más confiables. -- **Fragmentos de código mejorados** - Renderizado e interacción mejorados de fragmentos de código en la interfaz de chat para mejor legibilidad y usabilidad. +- **Caché implícito para Gemini** - Las llamadas a la API de Gemini ahora se almacenan automáticamente en caché, reduciendo los costos de API. +- **Selección de modo más inteligente** - Las definiciones de modo ahora pueden incluir orientación sobre cuándo debe usarse cada modo, permitiendo una mejor orquestación. +- **Condensación inteligente de contexto** - Resume de forma inteligente el historial de conversación cuando el contexto se llena en lugar de truncarlo (actívalo en Configuración -> Experimental). --- @@ -178,32 +178,36 @@ Usamos [changesets](https://github.com/changesets/changesets) para versionar y p ¡Gracias a todos nuestros colaboradores que han ayudado a mejorar Roo Code! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Licencia diff --git a/locales/fr/CODE_OF_CONDUCT.md b/locales/fr/CODE_OF_CONDUCT.md index bba5efa6e6e..b872617481d 100644 --- a/locales/fr/CODE_OF_CONDUCT.md +++ b/locales/fr/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • Français • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Code de Conduite des Contributeurs ## Notre Engagement diff --git a/locales/fr/CONTRIBUTING.md b/locales/fr/CONTRIBUTING.md index fdb4796a272..6036c4a891c 100644 --- a/locales/fr/CONTRIBUTING.md +++ b/locales/fr/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Contribuer à Roo Code - -Nous sommes ravis que vous soyez intéressé à contribuer à Roo Code. Que vous corrigiez un bug, ajoutiez une fonctionnalité ou amélioriez notre documentation, chaque contribution rend Roo Code plus intelligent ! Pour maintenir notre communauté dynamique et accueillante, tous les membres doivent adhérer à notre [Code de Conduite](CODE_OF_CONDUCT.md). - -## Rejoindre Notre Communauté - -Nous encourageons fortement tous les contributeurs à rejoindre notre [communauté Discord](https://discord.gg/roocode) ! Faire partie de notre serveur Discord vous aide à : - -- Obtenir de l'aide et des conseils en temps réel sur vos contributions -- Vous connecter avec d'autres contributeurs et membres de l'équipe principale -- Rester informé des développements et priorités du projet -- Participer aux discussions qui façonnent l'avenir de Roo Code -- Trouver des opportunités de collaboration avec d'autres développeurs +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • Français • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -## Signaler des Bugs ou des Problèmes +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -Les rapports de bugs aident à améliorer Roo Code pour tout le monde ! Avant de créer un nouveau problème, veuillez [rechercher parmi les existants](https://github.com/RooVetGit/Roo-Code/issues) pour éviter les doublons. Lorsque vous êtes prêt à signaler un bug, rendez-vous sur notre [page d'issues](https://github.com/RooVetGit/Roo-Code/issues/new/choose) où vous trouverez un modèle pour vous aider à remplir les informations pertinentes. - -
- 🔐 Important : Si vous découvrez une vulnérabilité de sécurité, veuillez utiliser l'outil de sécurité Github pour la signaler en privé. -
+# Contribuer à Roo Code -## Décider Sur Quoi Travailler +Roo Code est un projet porté par la communauté, et chaque contribution compte beaucoup pour nous. Pour fluidifier la collaboration, nous fonctionnons selon une [approche Issue-First](#approche-issue-first), ce qui signifie que toutes les [Pull Requests (PRs)](#soumettre-une-pull-request) doivent d'abord être liées à un ticket GitHub. Lis attentivement ce guide. -Vous cherchez une bonne première contribution ? Consultez les issues dans la section "Issue [Unassigned]" de notre [Projet Github Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1). Celles-ci sont spécifiquement sélectionnées pour les nouveaux contributeurs et les domaines où nous aimerions recevoir de l'aide ! +## Table des matières -Nous accueillons également les contributions à notre [documentation](https://docs.roocode.com/) ! Qu'il s'agisse de corriger des fautes de frappe, d'améliorer les guides existants ou de créer du nouveau contenu éducatif - nous aimerions construire un référentiel de ressources guidé par la communauté qui aide chacun à tirer le meilleur parti de Roo Code. Vous pouvez cliquer sur "Edit this page" sur n'importe quelle page pour accéder rapidement au bon endroit dans Github pour éditer le fichier, ou vous pouvez plonger directement dans https://github.com/RooVetGit/Roo-Code-Docs. +- [Avant de contribuer](#avant-de-contribuer) +- [Trouver et planifier ta contribution](#trouver-et-planifier-ta-contribution) +- [Processus de développement et de soumission](#processus-de-développement-et-de-soumission) +- [Légal](#légal) -Si vous prévoyez de travailler sur une fonctionnalité plus importante, veuillez d'abord créer une [demande de fonctionnalité](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) afin que nous puissions discuter si elle s'aligne avec la vision de Roo Code. Vous pouvez également consulter notre [Feuille de route du projet](#feuille-de-route-du-projet) ci-dessous pour voir si votre idée s'inscrit dans notre orientation stratégique. +## Avant de contribuer -## Feuille de route du projet +### 1. Code de conduite -Roo Code dispose d'une feuille de route de développement claire qui guide nos priorités et notre orientation future. Comprendre notre feuille de route peut vous aider à : +Tous les contributeurs doivent respecter notre [Code de conduite](./CODE_OF_CONDUCT.md). -- Aligner vos contributions avec les objectifs du projet -- Identifier les domaines où votre expertise serait la plus précieuse -- Comprendre le contexte derrière certaines décisions de conception -- Trouver de l'inspiration pour de nouvelles fonctionnalités qui soutiennent notre vision +### 2. Feuille de route du projet -Notre feuille de route actuelle se concentre sur six piliers clés : +Notre feuille de route guide la direction du projet. Aligne tes contributions avec ces objectifs clés : -### Support des fournisseurs +### Fiabilité avant tout -Nous visons à prendre en charge autant de fournisseurs que possible : +- Garantir que l'édition des différences et l'exécution des commandes soient toujours fiables. +- Réduire les points de friction qui découragent l'utilisation régulière. +- Assurer un fonctionnement fluide dans toutes les langues et sur toutes les plateformes. +- Étendre le support robuste pour une grande variété de fournisseurs et de modèles d'IA. -- Support plus polyvalent pour "OpenAI Compatible" -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Support amélioré pour Ollama et LM Studio +### Expérience utilisateur améliorée -### Support des modèles +- Simplifier l'interface utilisateur pour plus de clarté et d'intuitivité. +- Améliorer continuellement le flux de travail pour répondre aux attentes élevées des développeurs. -Nous voulons que Roo fonctionne aussi bien que possible avec autant de modèles que possible, y compris les modèles locaux : +### Leadership en performance des agents -- Support des modèles locaux via des prompts système personnalisés et des flux de travail -- Évaluations de benchmarking et cas de test +- Établir des référentiels d'évaluation (evals) complets pour mesurer la productivité réelle. +- Permettre à chacun d'exécuter et d'interpréter facilement ces évaluations. +- Fournir des améliorations qui démontrent des augmentations claires dans les scores d'évaluation. -### Support des systèmes +Mentionne l'alignement avec ces domaines dans tes PRs. -Nous voulons que Roo fonctionne bien sur l'ordinateur de chacun : +### 3. Rejoindre la communauté Roo Code -- Intégration de terminal multiplateforme -- Support solide et cohérent pour Mac, Windows et Linux +- **Principal :** Rejoins notre [Discord](https://discord.gg/roocode) et envoie un DM à **Hannes Rudolph (`hrudolph`)**. +- **Alternative :** Les contributeurs expérimentés peuvent participer directement via [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -### Documentation +## Trouver et planifier ta contribution -Nous voulons une documentation complète et accessible pour tous les utilisateurs et contributeurs : +### Types de contributions -- Guides utilisateur et tutoriels étendus -- Documentation API claire -- Meilleure orientation pour les contributeurs -- Ressources de documentation multilingues -- Exemples interactifs et échantillons de code +- **Corrections de bugs :** Résoudre des problèmes dans le code. +- **Nouvelles fonctionnalités :** Ajouter de nouvelles fonctions. +- **Documentation :** Améliorer les guides et la clarté. -### Stabilité +### Approche Issue-First -Nous voulons réduire considérablement le nombre de bugs et augmenter les tests automatisés : +Toutes les contributions doivent commencer par un ticket GitHub. -- Interrupteur de journalisation de débogage -- Bouton de copie "Informations machine/tâche" pour l'envoi avec les demandes de support/bug +- **Vérifier les tickets existants :** Cherche dans les [Issues GitHub](https://github.com/RooVetGit/Roo-Code/issues). +- **Créer un ticket :** Utilise les modèles appropriés : + - **Bugs :** Modèle "Bug Report". + - **Fonctionnalités :** Modèle "Detailed Feature Proposal". Approbation requise avant de commencer. +- **Réclamer des tickets :** Commente et attends l'assignation officielle. -### Internationalisation +**Les PRs sans tickets approuvés peuvent être fermées.** -Nous voulons que Roo parle la langue de tous : +### Décider sur quoi travailler -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +- Consulte le [Projet GitHub](https://github.com/orgs/RooVetGit/projects/1) pour les "Good First Issues" non assignés. +- Pour la documentation, visite [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). -Nous accueillons particulièrement les contributions qui font progresser nos objectifs de feuille de route. Si vous travaillez sur quelque chose qui s'aligne avec ces piliers, veuillez le mentionner dans la description de votre PR. +### Signaler des bugs -## Configuration de Développement +- Vérifie d'abord les rapports existants. +- Crée de nouveaux rapports de bugs avec le [modèle "Bug Report"](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Failles de sécurité :** Signale-les en privé via [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -1. **Clonez** le dépôt : +## Processus de développement et de soumission -```sh -git clone https://github.com/RooVetGit/Roo-Code.git -``` +### Configuration du développement -2. **Installez les dépendances** : +1. **Fork & Clone :** -```sh -npm run install:all ``` - -3. **Démarrez la vue web (application Vite/React avec HMR)** : - -```sh -npm run dev +git clone https://github.com/TON_UTILISATEUR/Roo-Code.git ``` -4. **Débogage** : - Appuyez sur `F5` (ou **Exécuter** → **Démarrer le débogage**) dans VSCode pour ouvrir une nouvelle session avec Roo Code chargé. - -Les modifications apportées à la vue web apparaîtront immédiatement. Les modifications apportées à l'extension principale nécessiteront un redémarrage de l'hôte d'extension. - -Vous pouvez également créer un fichier .vsix et l'installer directement dans VSCode : +2. **Installer les dépendances :** -```sh -npm run build ``` - -Un fichier `.vsix` apparaîtra dans le répertoire `bin/` qui peut être installé avec : - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Écrire et Soumettre du Code - -Tout le monde peut contribuer avec du code à Roo Code, mais nous vous demandons de suivre ces directives pour vous assurer que vos contributions puissent être intégrées en douceur : - -1. **Gardez les Pull Requests Ciblées** - - - Limitez les PRs à une seule fonctionnalité ou correction de bug - - Divisez les changements plus importants en PRs plus petites et liées - - Divisez les changements en commits logiques qui peuvent être examinés indépendamment - -2. **Qualité du Code** +3. **Débogage :** Ouvre avec VS Code (`F5`). - - Toutes les PRs doivent passer les vérifications CI qui incluent à la fois le linting et le formatage - - Résolvez toutes les alertes ou erreurs ESLint avant de soumettre - - Répondez à tous les retours d'Ellipsis, notre outil automatisé de revue de code - - Suivez les meilleures pratiques TypeScript et maintenez la sécurité des types +### Guide d'écriture du code -3. **Tests** +- Une PR ciblée par fonctionnalité ou correction. +- Suis les bonnes pratiques ESLint et TypeScript. +- Écris des commits clairs et descriptifs référençant les tickets (ex : `Fixes #123`). +- Fournis des tests complets (`npm test`). +- Rebase sur la dernière branche `main` avant de soumettre. - - Ajoutez des tests pour les nouvelles fonctionnalités - - Exécutez `npm test` pour vous assurer que tous les tests passent - - Mettez à jour les tests existants si vos changements les affectent - - Incluez à la fois des tests unitaires et d'intégration lorsque c'est approprié +### Soumettre une Pull Request -4. **Directives pour les Commits** +- Commence par un **brouillon de PR** si tu cherches un feedback précoce. +- Décris clairement tes changements en suivant le modèle de Pull Request. +- Fournis des captures d'écran/vidéos pour les changements d'interface. +- Indique si des mises à jour de documentation sont nécessaires. - - Écrivez des messages de commit clairs et descriptifs - - Référencez les issues pertinentes dans les commits en utilisant #numéro-issue +### Politique de Pull Request -5. **Avant de Soumettre** +- Doit référencer des tickets pré-approuvés et assignés. +- Les PRs ne respectant pas cette politique peuvent être fermées. +- Les PRs doivent passer les tests CI, s'aligner avec la feuille de route et avoir une documentation claire. - - Rebasez votre branche sur la dernière main - - Assurez-vous que votre branche se construit avec succès - - Vérifiez à nouveau que tous les tests passent - - Revoyez vos changements pour détecter tout code de débogage ou logs de console +### Processus de revue -6. **Description du Pull Request** - - Décrivez clairement ce que font vos changements - - Incluez des étapes pour tester les changements - - Listez tous les changements incompatibles - - Ajoutez des captures d'écran pour les changements d'interface utilisateur +- **Triage quotidien :** Vérifications rapides par les mainteneurs. +- **Revue hebdomadaire approfondie :** Évaluation complète. +- **Itère rapidement** sur la base du feedback. -## Accord de Contribution +## Légal -En soumettant une pull request, vous acceptez que vos contributions soient sous licence selon la même licence que le projet ([Apache 2.0](../LICENSE)). +En contribuant, tu acceptes que tes contributions soient sous licence Apache 2.0, conformément à la licence de Roo Code. diff --git a/locales/fr/README.md b/locales/fr/README.md index bb5f3862a76..f55e3f47f33 100644 --- a/locales/fr/README.md +++ b/locales/fr/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • Français • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • Français • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ Que vous recherchiez un partenaire de codage flexible, un architecte système, ou des rôles spécialisés comme un ingénieur QA ou un chef de produit, Roo Code peut vous aider à développer des logiciels plus efficacement. -Consultez le [CHANGELOG](../CHANGELOG.md) pour des mises à jour détaillées et des corrections. +Consultez le [CHANGELOG](../../CHANGELOG.md) pour des mises à jour détaillées et des corrections. --- -## 🎉 Roo Code 3.15 est sorti +## 🎉 Roo Code 3.17 est sorti -Roo Code 3.15 apporte de nouvelles fonctionnalités et améliorations basées sur vos commentaires ! +Roo Code 3.17 apporte de nouvelles fonctionnalités puissantes et des améliorations basées sur vos commentaires ! -- **Cache pour les prompts dans Vertex** - Vertex AI prend maintenant en charge le cache des prompts, améliorant les temps de réponse et réduisant les coûts d'API. -- **Mécanisme de secours pour le terminal** - Implémentation d'un mécanisme de secours lorsque l'intégration du shell du terminal VSCode échoue, garantissant des opérations de terminal plus fiables. -- **Fragments de code améliorés** - Rendu et interaction améliorés des fragments de code dans l'interface de chat pour une meilleure lisibilité et facilité d'utilisation. +- **Mise en cache implicite pour Gemini** - Les appels API Gemini sont désormais automatiquement mis en cache, réduisant les coûts d'API. +- **Sélection de mode plus intelligente** - Les définitions de mode peuvent maintenant inclure des indications sur quand chaque mode doit être utilisé, permettant une meilleure orchestration. +- **Condensation intelligente du contexte** - Résume intelligemment l'historique des conversations lorsque le contexte est plein au lieu de le tronquer (activez-le dans Paramètres -> Expérimental). --- @@ -178,32 +178,36 @@ Nous adorons les contributions de la communauté ! Commencez par lire notre [CON Merci à tous nos contributeurs qui ont aidé à améliorer Roo Code ! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Licence diff --git a/locales/hi/CODE_OF_CONDUCT.md b/locales/hi/CODE_OF_CONDUCT.md index 4f1529c5911..9d22f43944a 100644 --- a/locales/hi/CODE_OF_CONDUCT.md +++ b/locales/hi/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • हिंदी • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # योगदानकर्ता संधि आचार संहिता ## हमारी प्रतिज्ञा diff --git a/locales/hi/CONTRIBUTING.md b/locales/hi/CONTRIBUTING.md index 9f388c295f8..2ec62365b0c 100644 --- a/locales/hi/CONTRIBUTING.md +++ b/locales/hi/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Roo Code में योगदान देना +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • हिंदी • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -हम खुश हैं कि आप Roo Code में योगदान देने में रुचि रखते हैं। चाहे आप एक बग ठीक कर रहे हों, एक फीचर जोड़ रहे हों, या हमारे दस्तावेज़ों को सुधार रहे हों, हर योगदान Roo Code को अधिक स्मार्ट बनाता है! हमारे समुदाय को जीवंत और स्वागतयोग्य बनाए रखने के लिए, सभी सदस्यों को हमारे [आचार संहिता](CODE_OF_CONDUCT.md) का पालन करना चाहिए। +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -## हमारे समुदाय में शामिल हों +# Roo Code में योगदान करें -हम सभी योगदानकर्ताओं को हमारे [Discord समुदाय](https://discord.gg/roocode) में शामिल होने के लिए दृढ़ता से प्रोत्साहित करते हैं! हमारे Discord सर्वर का हिस्सा होने से आपको मदद मिलती है: +Roo Code एक समुदाय-आधारित प्रोजेक्ट है और हम हर योगदान को बहुत महत्व देते हैं। सहयोग को सरल बनाने के लिए, हम [Issue-First](#issue-first-एप्रोच) पद्धति अपनाते हैं, जिसका अर्थ है कि सभी [Pull Requests (PRs)](#pull-request-सबमिट-करना) को पहले GitHub Issue से जोड़ना आवश्यक है। कृपया इस गाइड को ध्यान से पढ़ें। -- अपने योगदान पर रीयल-टाइम मदद और मार्गदर्शन प्राप्त करें -- अन्य योगदानकर्ताओं और कोर टीम के सदस्यों से जुड़ें -- प्रोजेक्ट के विकास और प्राथमिकताओं से अपडेट रहें -- ऐसी चर्चाओं में भाग लें जो Roo Code के भविष्य को आकार देती हैं -- अन्य डेवलपर्स के साथ सहयोग के अवसर खोजें +## विषय सूची -## बग या समस्याओं की रिपोर्ट करना +- [योगदान करने से पहले](#योगदान-करने-से-पहले) +- [अपना योगदान ढूंढना और योजना बनाना](#अपना-योगदान-ढूंढना-और-योजना-बनाना) +- [विकास और सबमिशन प्रक्रिया](#विकास-और-सबमिशन-प्रक्रिया) +- [कानूनी](#कानूनी) -बग रिपोर्ट हर किसी के लिए Roo Code को बेहतर बनाने में मदद करती हैं! नई समस्या बनाने से पहले, कृपया डुप्लिकेट से बचने के लिए [मौजूदा समस्याओं की खोज करें](https://github.com/RooVetGit/Roo-Code/issues)। जब आप बग की रिपोर्ट करने के लिए तैयार हों, तो हमारे [इश्यूज पेज](https://github.com/RooVetGit/Roo-Code/issues/new/choose) पर जाएं जहां आपको प्रासंगिक जानकारी भरने में मदद करने के लिए एक टेम्पलेट मिलेगा। +## योगदान करने से पहले -
- 🔐 महत्वपूर्ण: यदि आप कोई सुरक्षा कमजोरी खोजते हैं, तो कृपया इसे निजी तौर पर रिपोर्ट करने के लिए Github सुरक्षा उपकरण का उपयोग करें। -
+### 1. आचार संहिता -## किस पर काम करना है यह तय करना +सभी योगदानकर्ताओं को हमारी [आचार संहिता](./CODE_OF_CONDUCT.md) का पालन करना चाहिए। -पहले योगदान के लिए एक अच्छा अवसर खोज रहे हैं? हमारे [Roo Code इश्यूज](https://github.com/orgs/RooVetGit/projects/1) Github प्रोजेक्ट के "Issue [Unassigned]" सेक्शन में इश्यूज देखें। ये विशेष रूप से नए योगदानकर्ताओं के लिए और ऐसे क्षेत्रों के लिए क्यूरेट किए गए हैं जहां हमें कुछ मदद की जरूरत होगी! +### 2. प्रोजेक्ट रोडमैप -हम अपने [दस्तावेज़ीकरण](https://docs.roocode.com/) में योगदान का भी स्वागत करते हैं! चाहे वह टाइपो ठीक करना हो, मौजूदा गाइड को सुधारना हो, या नई शैक्षिक सामग्री बनाना हो - हम संसाधनों का एक समुदाय-संचालित भंडार बनाना चाहते हैं जो हर किसी को Roo Code का अधिकतम उपयोग करने में मदद करे। आप फ़ाइल को संपादित करने के लिए किसी भी पृष्ठ पर "Edit this page" पर क्लिक कर सकते हैं या सीधे https://github.com/RooVetGit/Roo-Code-Docs में जा सकते हैं। +हमारा रोडमैप प्रोजेक्ट की दिशा तय करता है। अपने योगदान को इन प्रमुख लक्ष्यों के साथ संरेखित करें: -यदि आप एक बड़ी विशेषता पर काम करने की योजना बना रहे हैं, तो कृपया पहले एक [फीचर अनुरोध](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) बनाएं ताकि हम चर्चा कर सकें कि क्या यह Roo Code के दृष्टिकोण के अनुरूप है। आप नीचे दिए गए हमारे [प्रोजेक्ट रोडमैप](#प्रोजेक्ट-रोडमैप) को भी देख सकते हैं यह जानने के लिए कि क्या आपका विचार हमारी रणनीतिक दिशा के अनुरूप है। +### विश्वसनीयता पहले -## प्रोजेक्ट रोडमैप +- सुनिश्चित करें कि diff एडिटिंग और कमांड एक्जीक्यूशन लगातार विश्वसनीय हों +- नियमित उपयोग को हतोत्साहित करने वाले फ्रिक्शन पॉइंट्स को कम करें +- सभी भाषाओं और प्लेटफॉर्म्स पर सुचारू संचालन की गारंटी दें +- विभिन्न AI प्रदाताओं और मॉडल्स के लिए मजबूत समर्थन का विस्तार करें -Roo Code का एक स्पष्ट विकास रोडमैप है जो हमारी प्राथमिकताओं और भविष्य की दिशा का मार्गदर्शन करता है। हमारे रोडमैप को समझने से आपको मदद मिल सकती है: +### बेहतर उपयोगकर्ता अनुभव -- अपने योगदान को प्रोजेक्ट के लक्ष्यों के साथ संरेखित करना -- ऐसे क्षेत्रों की पहचान करना जहां आपकी विशेषज्ञता सबसे मूल्यवान होगी -- कुछ डिज़ाइन निर्णयों के पीछे के संदर्भ को समझना -- नई विशेषताओं के लिए प्रेरणा पाना जो हमारे दृष्टिकोण का समर्थन करती हैं +- स्पष्टता और सहजता के लिए UI/UX को सरल बनाएं +- डेवलपर्स के उच्च अपेक्षाओं को पूरा करने के लिए वर्कफ़्लो में निरंतर सुधार करें -हमारा वर्तमान रोडमैप छह प्रमुख स्तंभों पर केंद्रित है: +### एजेंट प्रदर्शन में अग्रणी -### प्रोवाइडर सपोर्ट +- वास्तविक दुनिया की उत्पादकता को मापने के लिए व्यापक मूल्यांकन बेंचमार्क (evals) स्थापित करें +- हर किसी के लिए इन मूल्यांकनों को आसानी से चलाना और समझना संभव बनाएं +- ऐसे सुधार लाएं जो मूल्यांकन स्कोर में स्पष्ट वृद्धि दिखाएं -हम जितने संभव हो सके उतने प्रोवाइडर्स को सपोर्ट करना चाहते हैं: +अपने PR में इन क्षेत्रों से संबंधित कार्य का उल्लेख करें। -- "OpenAI Compatible" के लिए अधिक बहुमुखी समर्थन -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Ollama और LM Studio के लिए बेहतर समर्थन +### 3. Roo Code कम्युनिटी से जुड़ें -### मॉडल सपोर्ट +- **मुख्य तरीका:** हमारे [Discord](https://discord.gg/roocode) से जुड़ें और **Hannes Rudolph (`hrudolph`)** को DM भेजें। +- **विकल्प:** अनुभवी योगदानकर्ता [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1) के माध्यम से सीधे भाग ले सकते हैं। -हम चाहते हैं कि Roo जितना संभव हो उतने मॉडल पर अच्छी तरह से काम करे, जिसमें लोकल मॉडल भी शामिल हैं: +## अपना योगदान ढूंढना और योजना बनाना -- कस्टम सिस्टम प्रॉम्प्टिंग और वर्कफ़्लोज़ के माध्यम से लोकल मॉडल सपोर्ट -- बेंचमार्किंग एवैल्युएशन और टेस्ट केस +### योगदान के प्रकार -### सिस्टम सपोर्ट +- **बग फिक्स:** कोड की समस्याओं को हल करना। +- **नई विशेषताएं:** नई कार्यक्षमता जोड़ना। +- **डॉक्युमेंटेशन:** गाइड सुधारना और स्पष्टता बढ़ाना। -हम चाहते हैं कि Roo हर किसी के कंप्यूटर पर अच्छी तरह से चले: +### Issue-First एप्रोच -- क्रॉस प्लेटफॉर्म टर्मिनल इंटीग्रेशन -- Mac, Windows और Linux के लिए मजबूत और सुसंगत समर्थन +हर योगदान GitHub Issue से शुरू होना चाहिए। -### डॉक्युमेंटेशन +- **मौजूदा Issues देखें:** [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues) में खोजें। +- **Issue बनाएं:** उपयुक्त टेम्पलेट का उपयोग करें: + - **बग:** "Bug Report" टेम्पलेट। + - **फीचर्स:** "Detailed Feature Proposal" टेम्पलेट। शुरू करने से पहले अनुमोदन आवश्यक है। +- **Issue क्लेम करें:** कमेंट करें और आधिकारिक असाइनमेंट का इंतजार करें। -हम सभी उपयोगकर्ताओं और योगदानकर्ताओं के लिए व्यापक, सुलभ दस्तावेज़ीकरण चाहते हैं: +**अनुमोदित Issue के बिना PR बंद किए जा सकते हैं।** -- विस्तारित उपयोगकर्ता गाइड और ट्यूटोरियल -- स्पष्ट API दस्तावेज़ीकरण -- योगदानकर्ताओं के लिए बेहतर मार्गदर्शन -- बहुभाषी दस्तावेज़ीकरण संसाधन -- इंटरैक्टिव उदाहरण और कोड सैंपल +### क्या काम करें चुनना -### स्थिरता +- [GitHub प्रोजेक्ट](https://github.com/orgs/RooVetGit/projects/1) में असाइन न किए गए "Good First Issues" देखें। +- डॉक्युमेंटेशन के लिए, [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs) देखें। -हम बग की संख्या को काफी कम करना और स्वचालित परीक्षण को बढ़ाना चाहते हैं: +### बग या समस्या रिपोर्ट करना -- डीबग लॉगिंग स्विच -- बग/सपोर्ट अनुरोधों के साथ भेजने के लिए "मशीन/टास्क इन्फॉर्मेशन" कॉपी बटन +- पहले मौजूदा रिपोर्ट देखें। +- ["Bug Report" टेम्पलेट](https://github.com/RooVetGit/Roo-Code/issues/new/choose) का उपयोग करके नए बग रिपोर्ट बनाएं। +- **सुरक्षा कमजोरियां:** [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new) के माध्यम से निजी तौर पर रिपोर्ट करें। -### अंतर्राष्ट्रीयकरण +## विकास और सबमिशन प्रक्रिया -हम चाहते हैं कि Roo हर किसी की भाषा बोले: +### विकास सेटअप -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +1. **Fork & Clone:** -हम विशेष रूप से उन योगदानों का स्वागत करते हैं जो हमारे रोडमैप लक्ष्यों को आगे बढ़ाते हैं। यदि आप कुछ ऐसा कर रहे हैं जो इन स्तंभों के अनुरूप है, तो कृपया अपने PR विवरण में इसका उल्लेख करें। - -## डेवलपमेंट सेटअप - -1. रिपो **क्लोन** करें: - -```sh -git clone https://github.com/RooVetGit/Roo-Code.git ``` - -2. **डिपेंडेंसीज इंस्टॉल** करें: - -```sh -npm run install:all -``` - -3. **वेबव्यू शुरू करें (Vite/React ऐप HMR के साथ)**: - -```sh -npm run dev +git clone https://github.com/आपका_यूज़रनेम/Roo-Code.git ``` -4. **डिबग**: - VSCode में `F5` दबाएं (या **Run** → **Start Debugging**) Roo Code लोड के साथ एक नया सेशन खोलने के लिए। +2. **डिपेंडेंसी इंस्टॉल करें:** -वेबव्यू में परिवर्तन तुरंत दिखाई देंगे। कोर एक्सटेंशन में परिवर्तनों के लिए एक्सटेंशन होस्ट को रीस्टार्ट करने की आवश्यकता होगी। - -वैकल्पिक रूप से आप .vsix बना सकते हैं और इसे सीधे VSCode में इंस्टॉल कर सकते हैं: - -```sh -npm run build ``` - -`bin/` डायरेक्टरी में एक `.vsix` फ़ाइल दिखाई देगी जिसे इस कमांड से इंस्टॉल किया जा सकता है: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## कोड लिखना और सबमिट करना - -कोई भी Roo Code में कोड का योगदान दे सकता है, लेकिन हम आपसे अनुरोध करते हैं कि आप इन दिशानिर्देशों का पालन करें ताकि आपके योगदान को सुचारू रूप से एकीकृत किया जा सके: - -1. **पुल रिक्वेस्ट को फोकस्ड रखें** - - - PR को एक ही फीचर या बग फिक्स तक सीमित रखें - - बड़े परिवर्तनों को छोटी, संबंधित PR में विभाजित करें - - परिवर्तनों को तार्किक कमिट्स में तोड़ें जिन्हें स्वतंत्र रूप से समीक्षा की जा सके - -2. **कोड क्वालिटी** +3. **डिबगिंग:** VS Code में `F5` दबाएं। - - सभी PR को CI चेक पास करना चाहिए जिसमें लिंटिंग और फॉर्मेटिंग दोनों शामिल हैं - - सबमिट करने से पहले किसी भी ESLint चेतावनी या त्रुटि को संबोधित करें - - Ellipsis, हमारे स्वचालित कोड समीक्षा टूल से सभी फीडबैक का जवाब दें - - TypeScript के बेस्ट प्रैक्टिस का पालन करें और टाइप सुरक्षा बनाए रखें +### कोड लिखने के दिशा-निर्देश -3. **टेस्टिंग** +- प्रति फीचर या फिक्स एक फोकस्ड PR। +- ESLint और TypeScript बेस्ट प्रैक्टिस का पालन करें। +- स्पष्ट, वर्णनात्मक कमिट मैसेज लिखें जो Issues को रेफर करें (जैसे `Fixes #123`)। +- पूर्ण टेस्टिंग प्रदान करें (`npm test`)। +- सबमिट करने से पहले अपनी ब्रांच को नवीनतम `main` पर रीबेस करें। - - नई विशेषताओं के लिए टेस्ट जोड़ें - - यह सुनिश्चित करने के लिए `npm test` चलाएं कि सभी टेस्ट पास हों - - यदि आपके परिवर्तन उन्हें प्रभावित करते हैं तो मौजूदा टेस्ट अपडेट करें - - जहां उपयुक्त हो, यूनिट टेस्ट और इंटीग्रेशन टेस्ट दोनों शामिल करें +### Pull Request सबमिट करना -4. **कमिट दिशानिर्देश** +- अगर आप शुरुआती फीडबैक चाहते हैं तो **ड्राफ्ट PR** से शुरू करें। +- Pull Request टेम्पलेट का पालन करते हुए अपने परिवर्तनों का स्पष्ट वर्णन करें। +- UI परिवर्तनों के लिए स्क्रीनशॉट/वीडियो प्रदान करें। +- बताएं कि क्या डॉक्युमेंटेशन अपडेट आवश्यक हैं। - - स्पष्ट, वर्णनात्मक कमिट संदेश लिखें - - #issue-number का उपयोग करके कमिट्स में प्रासंगिक मुद्दों का संदर्भ दें +### Pull Request नीति -5. **सबमिट करने से पहले** +- पूर्व-अनुमोदित और असाइन किए गए Issues का संदर्भ देना चाहिए। +- नीति का पालन न करने वाले PR बंद किए जा सकते हैं। +- PR को CI टेस्ट पास करना चाहिए, रोडमैप से मेल खाना चाहिए, और स्पष्ट डॉक्युमेंटेशन होनी चाहिए। - - अपनी ब्रांच को लेटेस्ट मेन पर रीबेस करें - - सुनिश्चित करें कि आपकी ब्रांच सफलतापूर्वक बिल्ड होती है - - डबल-चेक करें कि सभी टेस्ट पास हो रहे हैं - - अपने परिवर्तनों की समीक्षा करें किसी भी डिबगिंग कोड या कंसोल लॉग के लिए +### समीक्षा प्रक्रिया -6. **पुल रिक्वेस्ट विवरण** - - स्पष्ट रूप से बताएं कि आपके परिवर्तन क्या करते हैं - - परिवर्तनों का परीक्षण करने के लिए चरण शामिल करें - - किसी भी ब्रेकिंग चेंज की सूची बनाएं - - UI परिवर्तनों के लिए स्क्रीनशॉट जोड़ें +- **दैनिक ट्रायज:** मेंटेनर्स द्वारा त्वरित जांच। +- **साप्ताहिक गहन समीक्षा:** व्यापक मूल्यांकन। +- **फीडबैक के आधार पर तेजी से सुधार** करें। -## योगदान समझौता +## कानूनी -पुल रिक्वेस्ट सबमिट करके, आप सहमत होते हैं कि आपके योगदान को प्रोजेक्ट के समान लाइसेंस ([Apache 2.0](../LICENSE)) के तहत लाइसेंस दिया जाएगा। +Pull Request सबमिट करके, आप सहमत होते हैं कि आपके योगदान Roo Code के लाइसेंसिंग के अनुरूप Apache 2.0 लाइसेंस के तहत लाइसेंस किए जाएंगे। diff --git a/locales/hi/README.md b/locales/hi/README.md index 2d14f74ef90..d9313f05f67 100644 --- a/locales/hi/README.md +++ b/locales/hi/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • हिन्दी • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • हिन्दी • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ चाहे आप एक लचीला कोडिंग पार्टनर, सिस्टम आर्किटेक्ट, या क्यूए इंजीनियर या प्रोडक्ट मैनेजर जैसी विशेष भूमिकाओं की तलाश कर रहे हों, Roo Code आपको अधिक कुशलता से सॉफ्टवेयर बनाने में मदद कर सकता है। -विस्तृत अपडेट और फिक्स के लिए [CHANGELOG](../CHANGELOG.md) देखें। +विस्तृत अपडेट और फिक्स के लिए [CHANGELOG](../../CHANGELOG.md) देखें। --- -## 🎉 Roo Code 3.15 जारी +## 🎉 Roo Code 3.17 जारी -Roo Code 3.15 आपकी प्रतिक्रियाओं के आधार पर नई सुविधाएँ और सुधार लाता है! +Roo Code 3.17 आपकी प्रतिक्रियाओं के आधार पर शक्तिशाली नई सुविधाएँ और सुधार लाता है! -- **Vertex के लिए प्रॉम्प्ट कैशिंग** - Vertex AI अब प्रॉम्प्ट कैशिंग का समर्थन करता है, जिससे प्रतिक्रिया समय में सुधार और API लागत में कमी आती है। -- **टर्मिनल फॉलबैक** - VSCode टर्मिनल शेल एकीकरण विफल होने पर एक फॉलबैक तंत्र लागू किया गया है, जिससे अधिक विश्वसनीय टर्मिनल संचालन सुनिश्चित होता है। -- **बेहतर कोड स्निपेट्स** - चैट इंटरफेस में कोड स्निपेट्स की रेंडरिंग और इंटरैक्शन को बेहतर पठनीयता और उपयोगिता के लिए बढ़ाया गया है। +- **Gemini के लिए स्वचालित कैशिंग** - Gemini API कॉल अब स्वचालित रूप से कैश किए जाते हैं, जिससे API लागत कम होती है। +- **स्मार्ट मोड चयन** - मोड परिभाषाओं में अब यह निर्देश शामिल किया जा सकता है कि प्रत्येक मोड कब उपयोग किया जाना चाहिए, जिससे बेहतर ऑर्केस्ट्रेशन संभव होता है। +- **बुद्धिमान कॉन्टेक्स्ट कंडेंसिंग** - जब कॉन्टेक्स्ट भर जाता है, तो वार्तालाप इतिहास को काटने के बजाय बुद्धिमानी से सारांशित करता है (सेटिंग्स -> एक्सपेरिमेंटल में सक्षम करें)। --- @@ -178,32 +178,36 @@ code --install-extension bin/roo-cline-.vsix Roo Code को बेहतर बनाने में मदद करने वाले हमारे सभी योगदानकर्ताओं को धन्यवाद! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## लाइसेंस diff --git a/locales/it/CODE_OF_CONDUCT.md b/locales/it/CODE_OF_CONDUCT.md index b58e011b37d..7c6f5754e07 100644 --- a/locales/it/CODE_OF_CONDUCT.md +++ b/locales/it/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • Italiano • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Codice di Condotta del Patto del Contributore ## Il Nostro Impegno diff --git a/locales/it/CONTRIBUTING.md b/locales/it/CONTRIBUTING.md index 9e6ca151113..66f4143f5fa 100644 --- a/locales/it/CONTRIBUTING.md +++ b/locales/it/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Contribuire a Roo Code - -Siamo entusiasti che tu sia interessato a contribuire a Roo Code. Che tu stia correggendo un bug, aggiungendo una funzionalità o migliorando la nostra documentazione, ogni contributo rende Roo Code più intelligente! Per mantenere la nostra comunità vivace e accogliente, tutti i membri devono aderire al nostro [Codice di Condotta](CODE_OF_CONDUCT.md). - -## Unisciti alla Nostra Comunità - -Incoraggiamo fortemente tutti i contributori a unirsi alla nostra [comunità Discord](https://discord.gg/roocode)! Far parte del nostro server Discord ti aiuta a: - -- Ottenere aiuto e guida in tempo reale sui tuoi contributi -- Connetterti con altri contributori e membri del team principale -- Rimanere aggiornato sugli sviluppi e le priorità del progetto -- Partecipare a discussioni che modellano il futuro di Roo Code -- Trovare opportunità di collaborazione con altri sviluppatori +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • Italiano • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -## Segnalare Bug o Problemi +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -Le segnalazioni di bug aiutano a migliorare Roo Code per tutti! Prima di creare un nuovo problema, per favore [cerca tra quelli esistenti](https://github.com/RooVetGit/Roo-Code/issues) per evitare duplicati. Quando sei pronto a segnalare un bug, vai alla nostra [pagina dei problemi](https://github.com/RooVetGit/Roo-Code/issues/new/choose) dove troverai un modello per aiutarti a compilare le informazioni rilevanti. - -
- 🔐 Importante: Se scopri una vulnerabilità di sicurezza, utilizza lo strumento di sicurezza Github per segnalarla privatamente. -
+# Contribuire a Roo Code -## Decidere Su Cosa Lavorare +Roo Code è un progetto guidato dalla community e apprezziamo molto ogni contributo. Per semplificare la collaborazione, operiamo secondo un approccio [Issue-First](#approccio-issue-first), il che significa che tutte le [Pull Request (PR)](#inviare-una-pull-request) devono prima essere collegate a una Issue GitHub. Ti preghiamo di leggere attentamente questa guida. -Cerchi un buon primo contributo? Controlla i problemi nella sezione "Issue [Unassigned]" del nostro [Progetto Github di Roo Code](https://github.com/orgs/RooVetGit/projects/1). Questi sono specificamente selezionati per nuovi contributori e aree in cui ci piacerebbe avere un po' di aiuto! +## Indice -Accogliamo anche contributi alla nostra [documentazione](https://docs.roocode.com/)! Che si tratti di correggere errori di battitura, migliorare guide esistenti o creare nuovi contenuti educativi - ci piacerebbe costruire un repository di risorse guidato dalla comunità che aiuti tutti a ottenere il massimo da Roo Code. Puoi cliccare su "Edit this page" su qualsiasi pagina per arrivare rapidamente al punto giusto in Github per modificare il file, oppure puoi andare direttamente a https://github.com/RooVetGit/Roo-Code-Docs. +- [Prima di contribuire](#prima-di-contribuire) +- [Trovare e pianificare il tuo contributo](#trovare-e-pianificare-il-tuo-contributo) +- [Processo di sviluppo e invio](#processo-di-sviluppo-e-invio) +- [Legale](#legale) -Se stai pianificando di lavorare su una funzionalità più grande, per favore crea prima una [richiesta di funzionalità](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) così possiamo discutere se si allinea con la visione di Roo Code. Puoi anche consultare la nostra [Roadmap del Progetto](#roadmap-del-progetto) qui sotto per vedere se la tua idea si adatta alla nostra direzione strategica. +## Prima di contribuire -## Roadmap del Progetto +### 1. Codice di condotta -Roo Code ha una chiara roadmap di sviluppo che guida le nostre priorità e la direzione futura. Comprendere la nostra roadmap può aiutarti a: +Tutti i collaboratori devono rispettare il nostro [Codice di condotta](./CODE_OF_CONDUCT.md). -- Allineare i tuoi contributi con gli obiettivi del progetto -- Identificare aree in cui la tua esperienza sarebbe più preziosa -- Comprendere il contesto dietro certe decisioni di design -- Trovare ispirazione per nuove funzionalità che supportino la nostra visione +### 2. Roadmap del progetto -La nostra roadmap attuale si concentra su sei pilastri chiave: +La nostra roadmap guida la direzione del progetto. Allinea i tuoi contributi con questi obiettivi chiave: -### Supporto Provider +### Affidabilità prima di tutto -Miriamo a supportare quanti più provider possibile: +- Garantire che l'editing delle differenze e l'esecuzione dei comandi siano costantemente affidabili +- Ridurre i punti di attrito che scoraggiano l'uso regolare +- Garantire un funzionamento fluido in tutte le lingue e su tutte le piattaforme +- Ampliare il supporto robusto per una vasta gamma di provider e modelli di IA -- Supporto più versatile per "OpenAI Compatible" -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Supporto migliorato per Ollama e LM Studio +### Esperienza utente migliorata -### Supporto Modelli +- Semplificare l'interfaccia utente per maggiore chiarezza e intuitività +- Migliorare continuamente il flusso di lavoro per soddisfare le elevate aspettative degli sviluppatori -Vogliamo che Roo funzioni al meglio su quanti più modelli possibile, inclusi i modelli locali: +### Leadership nelle prestazioni degli agenti -- Supporto per modelli locali attraverso prompt di sistema personalizzati e flussi di lavoro -- Valutazioni di benchmark e casi di test +- Stabilire parametri di valutazione completi (evals) per misurare la produttività nel mondo reale +- Rendere facile per tutti eseguire e interpretare queste valutazioni +- Fornire miglioramenti che dimostrino chiari aumenti nei punteggi di valutazione -### Supporto Sistemi +Menziona l'allineamento con queste aree nelle tue PR. -Vogliamo che Roo funzioni bene sul computer di tutti: +### 3. Unisciti alla community Roo Code -- Integrazione del terminale multipiattaforma -- Supporto forte e coerente per Mac, Windows e Linux +- **Principale:** Unisciti al nostro [Discord](https://discord.gg/roocode) e invia un DM a **Hannes Rudolph (`hrudolph`)**. +- **Alternativa:** I collaboratori esperti possono partecipare direttamente tramite [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -### Documentazione +## Trovare e pianificare il tuo contributo -Vogliamo una documentazione completa e accessibile per tutti gli utenti e contributori: +### Tipi di contributi -- Guide utente e tutorial ampliati -- Documentazione API chiara -- Migliore orientamento per i contributori -- Risorse di documentazione multilingue -- Esempi interattivi e campioni di codice +- **Correzione bug:** Risolvere problemi nel codice. +- **Nuove funzionalità:** Aggiungere nuove funzionalità. +- **Documentazione:** Migliorare guide e chiarezza. -### Stabilità +### Approccio Issue-First -Vogliamo ridurre significativamente il numero di bug e aumentare i test automatizzati: +Tutti i contributi devono iniziare con una Issue GitHub. -- Interruttore di registrazione debug -- Pulsante di copia "Informazioni Macchina/Attività" per l'invio con richieste di supporto/bug +- **Verificare le issue esistenti:** Cerca su [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Creare una issue:** Usa i template appropriati: + - **Bug:** Template "Bug Report". + - **Funzionalità:** Template "Detailed Feature Proposal". Approvazione richiesta prima di iniziare. +- **Reclamare issue:** Commenta e attendi l'assegnazione ufficiale. -### Internazionalizzazione +**Le PR senza issue approvate potrebbero essere chiuse.** -Vogliamo che Roo parli la lingua di tutti: +### Decidere su cosa lavorare -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +- Controlla il [Progetto GitHub](https://github.com/orgs/RooVetGit/projects/1) per "Good First Issues" non assegnate. +- Per la documentazione, visita [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). -Accogliamo particolarmente i contributi che fanno progredire gli obiettivi della nostra roadmap. Se stai lavorando su qualcosa che si allinea con questi pilastri, per favore menzionalo nella descrizione della tua PR. +### Segnalare bug -## Configurazione per lo Sviluppo +- Controlla prima i report esistenti. +- Crea nuovi report di bug usando il [template "Bug Report"](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Problemi di sicurezza:** Segnala privatamente tramite [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -1. **Clona** il repository: +## Processo di sviluppo e invio -```sh -git clone https://github.com/RooVetGit/Roo-Code.git -``` +### Configurazione dello sviluppo -2. **Installa le dipendenze**: +1. **Fork & Clona:** -```sh -npm run install:all ``` - -3. **Avvia la webview (app Vite/React con HMR)**: - -```sh -npm run dev +git clone https://github.com/TUO_USERNAME/Roo-Code.git ``` -4. **Debug**: - Premi `F5` (o **Run** → **Start Debugging**) in VSCode per aprire una nuova sessione con Roo Code caricato. - -Le modifiche alla webview appariranno immediatamente. Le modifiche all'estensione principale richiederanno un riavvio dell'host dell'estensione. - -In alternativa puoi creare un file .vsix e installarlo direttamente in VSCode: +2. **Installa le dipendenze:** -```sh -npm run build ``` - -Un file `.vsix` apparirà nella directory `bin/` che può essere installato con: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Scrivere e Inviare Codice - -Chiunque può contribuire con codice a Roo Code, ma ti chiediamo di seguire queste linee guida per assicurare che i tuoi contributi possano essere integrati senza problemi: - -1. **Mantieni le Pull Request Focalizzate** - - - Limita le PR a una singola funzionalità o correzione di bug - - Suddividi i cambiamenti più grandi in PR più piccole e correlate - - Suddividi i cambiamenti in commit logici che possono essere revisionati indipendentemente - -2. **Qualità del Codice** +3. **Debug:** Apri con VS Code (`F5`). - - Tutte le PR devono passare i controlli CI che includono sia linting che formattazione - - Risolvi qualsiasi avviso o errore di ESLint prima di inviare - - Rispondi a tutti i feedback da Ellipsis, il nostro strumento automatico di revisione del codice - - Segui le migliori pratiche di TypeScript e mantieni la sicurezza dei tipi +### Linee guida per scrivere codice -3. **Testing** +- Una PR focalizzata per funzionalità o correzione. +- Segui le best practice di ESLint e TypeScript. +- Scrivi commit chiari e descrittivi che fanno riferimento alle issue (es. `Fixes #123`). +- Fornisci test approfonditi (`npm test`). +- Fai rebase sul branch `main` più recente prima dell'invio. - - Aggiungi test per le nuove funzionalità - - Esegui `npm test` per assicurarti che tutti i test passino - - Aggiorna i test esistenti se le tue modifiche li influenzano - - Includi sia test unitari che test di integrazione dove appropriato +### Inviare una Pull Request -4. **Linee Guida per i Commit** +- Inizia come **PR in bozza** se cerchi feedback anticipato. +- Descrivi chiaramente le tue modifiche seguendo il Template di Pull Request. +- Fornisci screenshot/video per modifiche UI. +- Indica se sono necessari aggiornamenti alla documentazione. - - Scrivi messaggi di commit chiari e descrittivi - - Fai riferimento ai problemi rilevanti nei commit usando #numero-problema +### Politica di Pull Request -5. **Prima di Inviare** +- Deve fare riferimento a issue pre-approvate e assegnate. +- Le PR che non rispettano la politica potrebbero essere chiuse. +- Le PR dovrebbero superare i test CI, allinearsi con la roadmap e avere documentazione chiara. - - Fai il rebase del tuo branch sull'ultimo main - - Assicurati che il tuo branch si costruisca con successo - - Ricontrolla che tutti i test stiano passando - - Rivedi le tue modifiche per qualsiasi codice di debug o log della console +### Processo di revisione -6. **Descrizione della Pull Request** - - Descrivi chiaramente cosa fanno le tue modifiche - - Includi passaggi per testare le modifiche - - Elenca eventuali breaking changes - - Aggiungi screenshot per modifiche UI +- **Triage quotidiano:** Controlli rapidi da parte dei maintainer. +- **Revisione settimanale approfondita:** Valutazione completa. +- **Itera rapidamente** in base al feedback. -## Accordo di Contribuzione +## Legale -Inviando una pull request, accetti che i tuoi contributi saranno concessi in licenza con la stessa licenza del progetto ([Apache 2.0](../LICENSE)). +Inviando una pull request, accetti che i tuoi contributi siano concessi in licenza sotto la Licenza Apache 2.0, in linea con la licenza di Roo Code. diff --git a/locales/it/README.md b/locales/it/README.md index dbf6fb5e886..a6c8387da26 100644 --- a/locales/it/README.md +++ b/locales/it/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • Italiano • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • Italiano • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ Che tu stia cercando un partner di codifica flessibile, un architetto di sistema o ruoli specializzati come un ingegnere QA o un product manager, Roo Code può aiutarti a costruire software in modo più efficiente. -Consulta il [CHANGELOG](../CHANGELOG.md) per aggiornamenti dettagliati e correzioni. +Consulta il [CHANGELOG](../../CHANGELOG.md) per aggiornamenti dettagliati e correzioni. --- -## 🎉 Roo Code 3.15 Rilasciato +## 🎉 Roo Code 3.17 Rilasciato -Roo Code 3.15 porta nuove funzionalità e miglioramenti basati sui tuoi feedback! +Roo Code 3.17 porta potenti nuove funzionalità e miglioramenti basati sui tuoi feedback! -- **Cache per i prompt in Vertex** - Vertex AI ora supporta la cache dei prompt, migliorando i tempi di risposta e riducendo i costi API. -- **Fallback del Terminale** - Implementato un meccanismo di fallback quando l'integrazione della shell del terminale VSCode fallisce, garantendo operazioni del terminale più affidabili. -- **Snippet di Codice Migliorati** - Rendering e interazione migliorati degli snippet di codice nell'interfaccia di chat per una migliore leggibilità e usabilità. +- **Caching implicito per Gemini** - Le chiamate API Gemini vengono ora memorizzate automaticamente nella cache, riducendo i costi API. +- **Selezione delle modalità più intelligente** - Le definizioni delle modalità possono ora includere indicazioni su quando utilizzare ciascuna modalità, consentendo una migliore orchestrazione. +- **Condensazione intelligente del contesto** - Riassume intelligentemente la cronologia delle conversazioni quando il contesto si riempie invece di troncarla (attivabile in Impostazioni -> Sperimentale). --- @@ -178,32 +178,36 @@ Amiamo i contributi della community! Inizia leggendo il nostro [CONTRIBUTING.md] Grazie a tutti i nostri contributori che hanno aiutato a migliorare Roo Code! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Licenza diff --git a/locales/ja/CODE_OF_CONDUCT.md b/locales/ja/CODE_OF_CONDUCT.md index 9dbb9b776dc..fb7dd9b11a6 100644 --- a/locales/ja/CODE_OF_CONDUCT.md +++ b/locales/ja/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +日本語 • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # コントリビューター行動規範 ## 私たちの誓約 diff --git a/locales/ja/CONTRIBUTING.md b/locales/ja/CONTRIBUTING.md index 42be40e33d2..2fd4a436768 100644 --- a/locales/ja/CONTRIBUTING.md +++ b/locales/ja/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Roo Codeへの貢献 +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -Roo Codeへの貢献に興味を持っていただき、ありがとうございます。バグの修正、機能の追加、またはドキュメントの改善など、すべての貢献がRoo Codeをよりスマートにします!コミュニティを活気に満ちた歓迎的なものに保つため、すべてのメンバーは[行動規範](CODE_OF_CONDUCT.md)を順守する必要があります。 +日本語 • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -## コミュニティに参加する +# Roo Code への貢献 -すべての貢献者に[Discordコミュニティ](https://discord.gg/roocode)への参加を強く推奨します!Discordサーバーに参加することで以下のメリットがあります: +Roo Code はコミュニティ主導のプロジェクトであり、すべての貢献を大切にしています。協力をスムーズにするため、[Issue-First](#issue-first-アプローチ)方式を採用しています。これはすべての[Pull Request (PR)](#pull-request-の提出)がまずGitHub Issueに紐付けられる必要があることを意味します。このガイドをよく読んでください。 -- 貢献に関するリアルタイムのヘルプとガイダンスを得られる -- 他の貢献者やコアチームメンバーとつながれる -- プロジェクトの開発と優先事項について最新情報を得られる -- Roo Codeの将来を形作るディスカッションに参加できる -- 他の開発者とのコラボレーションの機会を見つけられる +## 目次 -## バグや問題の報告 +- [貢献する前に](#貢献する前に) +- [貢献内容の発見と計画](#貢献内容の発見と計画) +- [開発と提出のプロセス](#開発と提出のプロセス) +- [法的事項](#法的事項) -バグレポートはRoo Codeをより良くするのに役立ちます!新しい課題を作成する前に、重複を避けるために[既存の課題を検索](https://github.com/RooVetGit/Roo-Code/issues)してください。バグを報告する準備ができたら、関連情報の入力を手助けするテンプレートが用意されている[課題ページ](https://github.com/RooVetGit/Roo-Code/issues/new/choose)にアクセスしてください。 +## 貢献する前に -
- 🔐 重要: セキュリティ脆弱性を発見した場合は、Githubセキュリティツールを使用して非公開で報告してください。 -
+### 1. 行動規範 -## 取り組む内容の決定 +すべてのコントリビューターは[行動規範](./CODE_OF_CONDUCT.md)を守る必要があります。 -良い最初の貢献を探していますか?[Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1) Githubプロジェクトの「Issue [Unassigned]」セクションの課題をチェックしてください。これらは新しい貢献者や私たちが助けを必要としている領域のために特別に選ばれています! +### 2. プロジェクトロードマップ -また、[ドキュメント](https://docs.roocode.com/)への貢献も歓迎します!タイプミスの修正、既存ガイドの改善、または新しい教育コンテンツの作成など、Roo Codeを最大限に活用するためのコミュニティ主導のリソースリポジトリの構築を目指しています。任意のページで「Edit this page」をクリックすると、ファイルを編集するためのGithubの適切な場所にすぐに移動できます。または、https://github.com/RooVetGit/Roo-Code-Docs に直接アクセスすることもできます。 +ロードマップはプロジェクトの方向性を示します。貢献をこれらの主要目標に沿わせてください: -より大きな機能に取り組む予定がある場合は、Roo Codeのビジョンに合致するかどうかを議論するために、まず[機能リクエスト](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)を作成してください。また、アイデアが私たちの戦略的方向性に合っているかどうかを確認するために、下記の[プロジェクトロードマップ](#プロジェクトロードマップ)をチェックすることもできます。 +### 信頼性優先 -## プロジェクトロードマップ +- diff編集とコマンド実行が常に信頼できることを保証 +- 定期的な使用を妨げる摩擦ポイントの削減 +- すべての言語環境とプラットフォームでのスムーズな動作を保証 +- 様々なAIプロバイダーとモデルへの堅牢なサポートを拡大 -Roo Codeには、私たちの優先事項と将来の方向性を導く明確な開発ロードマップがあります。私たちのロードマップを理解することで、以下のような助けになります: +### ユーザー体験の強化 -- あなたの貢献をプロジェクトの目標に合わせる -- あなたの専門知識が最も価値がある領域を特定する -- 特定のデザイン決定の背景を理解する -- 私たちのビジョンをサポートする新機能のインスピレーションを得る +- 明確さと直感性のためのUI/UXの合理化 +- 開発者が日常的に使用するツールに求める高い期待に応えるためのワークフローの継続的改善 -現在のロードマップは、6つの主要な柱に焦点を当てています: +### エージェントパフォーマンスの先導 -### プロバイダーサポート +- 実際の生産性を測定する包括的な評価基準(evals)の確立 +- 誰もが簡単にこれらの評価を実行して解釈できるようにする +- 評価スコアの明確な向上を示す改善を提供 -できるだけ多くのプロバイダーをサポートすることを目指しています: +PRでこれらの領域との関連性に言及してください。 -- より汎用的な「OpenAI互換」サポート -- xAI、Microsoft Azure AI、Alibaba Cloud Qwen、IBM Watsonx、Together AI、DeepInfra、Fireworks AI、Cohere、Perplexity AI、FriendliAI、Replicate -- OllamaとLM Studioの強化されたサポート +### 3. Roo Code コミュニティに参加する -### モデルサポート +- **主な方法:** [Discord](https://discord.gg/roocode)に参加し、**Hannes Rudolph (`hrudolph`)**にDMを送る。 +- **代替手段:** 経験豊富なコントリビューターは[GitHub Projects](https://github.com/orgs/RooVetGit/projects/1)を通じて直接参加できます。 -ローカルモデルを含め、できるだけ多くのモデルでRooが良好に動作することを望んでいます: +## 貢献内容の発見と計画 -- カスタムシステムプロンプティングとワークフローを通じたローカルモデルサポート -- ベンチマーク評価とテストケース +### 貢献の種類 -### システムサポート +- **バグ修正:** コードの問題を解決。 +- **新機能:** 機能を追加。 +- **ドキュメント:** ガイドを改善し明確にする。 -Rooが誰のコンピュータでも良好に動作することを望んでいます: +### Issue-First アプローチ -- クロスプラットフォームターミナル統合 -- Mac、Windows、Linuxの強力で一貫したサポート +すべての貢献はGitHub Issueから始めてください。 -### ドキュメンテーション +- **既存Issueの確認:** [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues)を検索。 +- **Issueの作成:** 適切なテンプレートを使用: + - **バグ:** 「Bug Report」テンプレート。 + - **機能:** 「Detailed Feature Proposal」テンプレート。開始前に承認が必要。 +- **Issue担当表明:** コメントし、正式な割り当てを待つ。 -すべてのユーザーと貢献者のための包括的でアクセスしやすいドキュメントを望んでいます: +**承認されたIssueに紐付けられていないPRは閉じられる可能性があります。** -- 拡張されたユーザーガイドとチュートリアル -- 明確なAPIドキュメント -- 貢献者のためのより良いガイダンス -- 多言語ドキュメントリソース -- インタラクティブな例とコードサンプル +### 何に取り組むか決める -### 安定性 +- 未割り当ての「Good First Issues」を[GitHub Project](https://github.com/orgs/RooVetGit/projects/1)でチェック。 +- ドキュメント関連は[Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs)を参照。 -バグの数を大幅に減らし、自動テストを増やすことを望んでいます: +### バグの報告 -- デバッグロギングスイッチ -- バグ/サポートリクエストと一緒に送信するための「マシン/タスク情報」コピーボタン +- まず既存の報告がないか確認。 +- 新しいバグは[「Bug Report」テンプレート](https://github.com/RooVetGit/Roo-Code/issues/new/choose)で報告。 +- **セキュリティ問題:** [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new)を通じて非公開で報告。 -### 国際化 +## 開発と提出のプロセス -Rooが誰の言語も話すことを望んでいます: +### 開発環境のセットアップ -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +1. **Fork & Clone:** -私たちは特に、ロードマップの目標を前進させる貢献を歓迎します。これらの柱に沿った何かに取り組んでいる場合は、PRの説明でそれについて言及してください。 - -## 開発のセットアップ - -1. リポジトリを**クローン**します: - -```sh -git clone https://github.com/RooVetGit/Roo-Code.git ``` - -2. **依存関係をインストール**します: - -```sh -npm run install:all -``` - -3. **ウェブビュー(Vite/ReactアプリとHMR)を起動**します: - -```sh -npm run dev +git clone https://github.com/あなたのユーザー名/Roo-Code.git ``` -4. **デバッグ**: - VSCodeで`F5`キー(または**実行**→**デバッグの開始**)を押すと、Roo Codeがロードされた新しいセッションが開きます。 +2. **依存関係のインストール:** -ウェブビューへの変更はすぐに反映されます。コア拡張機能への変更は、拡張機能ホストの再起動が必要です。 - -または、.vsixファイルをビルドしてVSCodeに直接インストールすることもできます: - -```sh -npm run build ``` - -`bin/`ディレクトリに`.vsix`ファイルが作成され、以下のコマンドでインストールできます: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## コードの作成と提出 - -誰でもRoo Codeにコードを貢献できますが、貢献がスムーズに統合されるように以下のガイドラインに従ってください: - -1. **プルリクエストを焦点を絞ったものにする** - - - PRを単一の機能またはバグ修正に限定する - - より大きな変更を小さく関連したPRに分割する - - 変更を独立してレビューできる論理的なコミットに分ける - -2. **コード品質** +3. **デバッグ:** VS Codeで`F5`を押して開く。 - - すべてのPRはlintingとフォーマットの両方を含むCIチェックに合格する必要がある - - 提出前にESLintの警告やエラーを解決する - - 自動コードレビューツールであるEllipsisからのすべてのフィードバックに対応する - - TypeScriptのベストプラクティスに従い、型の安全性を維持する +### コーディングガイドライン -3. **テスト** +- 1つの機能または修正ごとに1つのPR。 +- ESLintとTypeScriptのベストプラクティスに従う。 +- 関連Issueを参照する明確なコミットメッセージを書く(例:`Fixes #123`)。 +- 十分なテストを提供(`npm test`)。 +- 提出前に最新の`main`ブランチにリベース。 - - 新機能にはテストを追加する - - `npm test`を実行してすべてのテストが合格することを確認する - - 変更が影響する既存のテストを更新する - - 適切な場合は単体テストと統合テストの両方を含める +### Pull Request の提出 -4. **コミットガイドライン** +- 早期フィードバックを求める場合は**ドラフトPR**から始める。 +- Pull Requestテンプレートに従って変更を明確に説明。 +- UI変更のスクリーンショット/動画を提供。 +- ドキュメント更新が必要かどうかを示す。 - - 明確で説明的なコミットメッセージを書く - - #issue-number を使用してコミットで関連する課題を参照する +### Pull Request ポリシー -5. **提出前に** +- 承認・割り当て済みIssueを参照する必要がある。 +- ポリシーに従わないPRは閉じられる可能性がある。 +- PRはCIテストに合格し、ロードマップに沿い、明確なドキュメントを持つべき。 - - 最新のmainブランチに対してあなたのブランチをリベースする - - あなたのブランチが正常にビルドされることを確認する - - すべてのテストが合格していることを再確認する - - デバッグコードやコンソールログがないか変更を見直す +### レビュープロセス -6. **プルリクエストの説明** - - 変更内容を明確に説明する - - 変更をテストするための手順を含める - - 破壊的変更がある場合はリストアップする - - UI変更の場合はスクリーンショットを追加する +- **日次トリアージ:** メンテナーによる迅速なチェック。 +- **週次詳細レビュー:** 包括的な評価。 +- **フィードバックに基づいて迅速に改善**。 -## 貢献同意 +## 法的事項 -プルリクエストを提出することにより、あなたの貢献がプロジェクトと同じライセンス([Apache 2.0](../LICENSE))の下でライセンスされることに同意したものとみなします。 +Pull Requestを提出することで、あなたの貢献がRoo Codeと同じApache 2.0ライセンスの下で提供されることに同意したことになります。 diff --git a/locales/ja/README.md b/locales/ja/README.md index 3700bc271a5..46ffcd02a52 100644 --- a/locales/ja/README.md +++ b/locales/ja/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ 柔軟なコーディングパートナー、システムアーキテクト、QAエンジニアやプロダクトマネージャーなどの専門的な役割を求めているかどうかにかかわらず、Roo Codeはより効率的にソフトウェアを構築するのを手助けします。 -詳細な更新と修正については[CHANGELOG](../CHANGELOG.md)をご覧ください。 +詳細な更新と修正については[CHANGELOG](../../CHANGELOG.md)をご覧ください。 --- -## 🎉 Roo Code 3.15リリース +## 🎉 Roo Code 3.17リリース -Roo Code 3.15はユーザーのフィードバックに基づく新機能と改善を提供します! +Roo Code 3.17はユーザーのフィードバックに基づく強力な新機能と改善を提供します! -- **Vertex向けプロンプトキャッシング** - Vertex AIがプロンプトキャッシングをサポートするようになり、応答時間の改善とAPIコストの削減を実現しました -- **ターミナルフォールバック** - VSCodeターミナルシェル統合が失敗した場合のフォールバックメカニズムを実装し、より信頼性の高いターミナル操作を確保しました -- **コードスニペットの改善** - チャットインターフェースでのコードスニペットのレンダリングと操作性を向上させ、読みやすさと使いやすさを改善しました +- **Geminiの暗黙的キャッシング** - Gemini APIコールが自動的にキャッシュされるようになり、APIコストが削減されます +- **よりスマートなモード選択** - モード定義に各モードがいつ使用されるべきかの指針を含めることができるようになり、より良いオーケストレーションが可能になりました +- **インテリジェントなコンテキスト凝縮** - コンテキストが一杯になったとき、切り捨てる代わりに会話履歴をインテリジェントに要約します(設定→実験的機能で有効化) --- @@ -178,32 +178,36 @@ code --install-extension bin/roo-cline-.vsix Roo Codeの改善に貢献してくれたすべての貢献者に感謝します! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## ライセンス diff --git a/locales/ko/CODE_OF_CONDUCT.md b/locales/ko/CODE_OF_CONDUCT.md index cb3bdfc4914..4b52ef8f279 100644 --- a/locales/ko/CODE_OF_CONDUCT.md +++ b/locales/ko/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • 한국어 • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # 기여자 서약 행동 강령 ## 우리의 약속 diff --git a/locales/ko/CONTRIBUTING.md b/locales/ko/CONTRIBUTING.md index 342fcbc4512..ef216182da8 100644 --- a/locales/ko/CONTRIBUTING.md +++ b/locales/ko/CONTRIBUTING.md @@ -1,174 +1,129 @@ -# Roo Code에 기여하기 +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -Roo Code에 기여하는 데 관심을 가져주셔서 기쁩니다. 버그를 수정하든, 기능을 추가하든, 문서를 개선하든, 모든 기여는 Roo Code를 더 스마트하게 만듭니다! 우리 커뮤니티를 활기차고 친절하게 유지하기 위해, 모든 구성원은 우리의 [행동 강령](CODE_OF_CONDUCT.md)을 준수해야 합니다. +[日本語](../ja/CONTRIBUTING.md) • 한국어 • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -## 우리 커뮤니티에 참여하세요 +# Roo Code 기여 가이드 -모든 기여자가 우리의 [Discord 커뮤니티](https://discord.gg/roocode)에 참여할 것을 강력히 권장합니다! Discord 서버의 일원이 되면 다음과 같은 도움을 받을 수 있습니다: +Roo Code는 커뮤니티 주도의 프로젝트이며, 모든 기여를 소중하게 생각합니다. 협업을 간소화하기 위해 [Issue-First](#issue-first-접근법) 원칙을 적용하고 있으며, 이는 모든 [Pull Request (PR)](#pull-request-제출)가 먼저 GitHub Issue와 연결되어야 함을 의미합니다. 이 가이드를 주의 깊게 검토해 주세요. -- 기여에 대한 실시간 도움과 지침 얻기 -- 다른 기여자 및 핵심 팀원과 연결 -- 프로젝트 개발 및 우선순위에 대한 최신 정보 유지 -- Roo Code의 미래를 형성하는 토론에 참여 -- 다른 개발자와의 협업 기회 찾기 +## 목차 -## 버그 또는 이슈 보고하기 +- [기여 전 준비](#기여-전-준비) +- [기여 내용 찾기 및 계획 세우기](#기여-내용-찾기-및-계획-세우기) +- [개발 및 제출 프로세스](#개발-및-제출-프로세스) +- [법적 안내](#법적-안내) -버그 보고는 모두를 위해 Roo Code를 더 좋게 만드는 데 도움이 됩니다! 새 이슈를 만들기 전에, 중복을 피하기 위해 [기존 이슈 검색](https://github.com/RooVetGit/Roo-Code/issues)을 해주세요. 버그를 보고할 준비가 되면, 관련 정보를 작성하는 데 도움이 되는 템플릿이 있는 [이슈 페이지](https://github.com/RooVetGit/Roo-Code/issues/new/choose)로 이동하세요. +## 기여 전 준비 -
- 🔐 중요: 보안 취약점을 발견한 경우, 비공개로 보고하기 위해 Github 보안 도구를 사용하세요. -
+### 1. 행동 강령 -## 작업할 내용 결정하기 +모든 기여자는 [행동 강령](./CODE_OF_CONDUCT.md)을 준수해야 합니다. -첫 기여를 위한 좋은 시작점을 찾고 계신가요? 우리의 [Roo Code 이슈](https://github.com/orgs/RooVetGit/projects/1) Github 프로젝트의 "Issue [Unassigned]" 섹션에서 이슈를 확인하세요. 이러한 이슈들은 새로운 기여자와 우리가 도움을 필요로 하는 영역을 위해 특별히 선별되었습니다! +### 2. 프로젝트 로드맵 -우리는 [문서](https://docs.roocode.com/)에 대한 기여도 환영합니다! 오타 수정, 기존 가이드 개선 또는 새로운 교육 콘텐츠 생성 등 - 모든 사람이 Roo Code를 최대한 활용할 수 있도록 도와주는 커뮤니티 기반 리소스 저장소를 구축하고 싶습니다. 모든 -페이지에서 "Edit this page"를 클릭하여 파일을 편집할 수 있는 Github의 적절한 위치로 빠르게 이동하거나, https://github.com/RooVetGit/Roo-Code-Docs에 직접 접근할 수 있습니다. +로드맵은 프로젝트 방향을 안내합니다. 기여를 다음 핵심 목표에 맞추세요: -더 큰 기능 작업을 계획하고 있다면, Roo Code의 비전과 일치하는지 논의할 수 있도록 먼저 [기능 요청](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop)을 생성해주세요. 또한 아이디어가 우리의 전략적 방향과 일치하는지 확인하기 위해 아래의 [프로젝트 로드맵](#프로젝트-로드맵)을 확인할 수도 있습니다. +### 신뢰성 우선 -## 프로젝트 로드맵 +- diff 편집과 명령 실행의 일관된 신뢰성 보장 +- 정기적 사용을 방해하는 마찰점 감소 +- 모든 언어 환경과 플랫폼에서의 원활한 작동 보장 +- 다양한 AI 제공업체 및 모델에 대한 강력한 지원 확대 -Roo Code는 우리의 우선순위와 미래 방향을 안내하는 명확한 개발 로드맵을 가지고 있습니다. 우리의 로드맵을 이해하면 다음과 같은 도움을 받을 수 있습니다: +### 향상된 사용자 경험 -- 프로젝트 목표에 맞게 기여 조정 -- 당신의 전문 지식이 가장 가치 있는 영역 식별 -- 특정 디자인 결정 배경 이해 -- 우리의 비전을 지원하는 새로운 기능에 대한 영감 찾기 +- 명확성과 직관성을 위한 UI/UX 간소화 +- 개발자들이 일상적으로 사용하는 도구에 기대하는 높은 기준을 충족하기 위한 지속적인 워크플로우 개선 -현재 로드맵은 여섯 가지 주요 기둥에 초점을 맞추고 있습니다: +### 에이전트 성능 선도 -### 제공업체 지원 +- 실제 생산성을 측정하는 포괄적인 평가 기준(evals) 수립 +- 누구나 이러한 평가를 쉽게 실행하고 해석할 수 있도록 지원 +- 평가 점수의 명확한 향상을 보여주는 개선 제공 -가능한 한 많은 제공업체를 지원하는 것을 목표로 합니다: +PR에서 이러한 영역과의 연관성을 언급하세요. -- 더 다재다능한 "OpenAI 호환" 지원 -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Ollama와 LM Studio에 대한 향상된 지원 +### 3. Roo Code 커뮤니티 참여 -### 모델 지원 +- **주요 방법:** [Discord](https://discord.gg/roocode)에 가입하고 **Hannes Rudolph (`hrudolph`)**에게 DM을 보내세요. +- **대안:** 경험 많은 기여자는 [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1)를 통해 직접 참여할 수 있습니다. -로컬 모델을 포함하여 가능한 한 많은 모델에서 Roo가 잘 작동하기를 원합니다: +## 기여 내용 찾기 및 계획 세우기 -- 사용자 정의 시스템 프롬프팅 및 워크플로우를 통한 로컬 모델 지원 -- 벤치마킹 평가 및 테스트 케이스 +### 기여 유형 -### 시스템 지원 +- **버그 수정:** 코드 문제 해결. +- **새 기능:** 기능 추가. +- **문서화:** 가이드 개선 및 명확성 향상. -Roo가 모든 사람의 컴퓨터에서 잘 작동하기를 원합니다: +### Issue-First 접근법 -- 크로스 플랫폼 터미널 통합 -- Mac, Windows 및 Linux에 대한 강력하고 일관된 지원 +모든 기여는 GitHub Issue에서 시작해야 합니다. -### 문서화 +- **기존 Issue 확인:** [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues)를 검색하세요. +- **Issue 생성:** 적절한 템플릿 사용: + - **버그:** "Bug Report" 템플릿. + - **기능:** "Detailed Feature Proposal" 템플릿. 시작 전 승인 필요. +- **Issue 담당:** 댓글을 달고 공식 할당을 기다리세요. -모든 사용자와 기여자를 위한 포괄적이고 접근 가능한 문서를 원합니다: +**승인된 Issue 없는 PR은 닫힐 수 있습니다.** -- 확장된 사용자 가이드 및 튜토리얼 -- 명확한 API 문서 -- 기여자를 위한 더 나은 가이드 -- 다국어 문서 리소스 -- 대화형 예제 및 코드 샘플 +### 작업 선택하기 -### 안정성 +- 할당되지 않은 "Good First Issues"를 [GitHub 프로젝트](https://github.com/orgs/RooVetGit/projects/1)에서 확인하세요. +- 문서 관련은 [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs)를 참조하세요. -버그 수를 크게 줄이고 자동화된 테스트를 증가시키고자 합니다: +### 버그 신고 -- 디버그 로깅 스위치 -- 버그/지원 요청과 함께 보낼 수 있는 "기기/작업 정보" 복사 버튼 +- 먼저 기존 신고를 확인하세요. +- ["Bug Report" 템플릿](https://github.com/RooVetGit/Roo-Code/issues/new/choose)을 사용하여 새 버그를 신고하세요. +- **보안 문제:** [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new)를 통해 비공개로 신고하세요. -### 국제화 +## 개발 및 제출 프로세스 -Roo가 모든 사람의 언어를 말하기를 원합니다: +### 개발 환경 설정 -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +1. **Fork & Clone:** -우리는 특히 로드맵 목표를 발전시키는 기여를 환영합니다. 이러한 기둥에 맞는 작업을 하고 있다면, PR 설명에서 이를 언급해 주세요. - -## 개발 설정 - -1. 저장소 **클론**: - -```sh -git clone https://github.com/RooVetGit/Roo-Code.git ``` - -2. **의존성 설치**: - -```sh -npm run install:all -``` - -3. **웹뷰 시작(HMR이 있는 Vite/React 앱)**: - -```sh -npm run dev +git clone https://github.com/당신의_아이디/Roo-Code.git ``` -4. **디버깅**: - VSCode에서 `F5`를 누르거나(**실행** → **디버깅 시작**) Roo Code가 로드된 새 세션을 엽니다. +2. **의존성 설치:** -웹뷰의 변경 사항은 즉시 나타납니다. 코어 확장에 대한 변경 사항은 확장 호스트를 다시 시작해야 합니다. - -또는 .vsix를 빌드하고 VSCode에 직접 설치할 수 있습니다: - -```sh -npm run build ``` - -`bin/` 디렉토리에 `.vsix` 파일이 나타나며 다음 명령으로 설치할 수 있습니다: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## 코드 작성 및 제출 - -누구나 Roo Code에 코드를 기여할 수 있지만, 기여가 원활하게 통합될 수 있도록 다음 지침을 따라주시기 바랍니다: - -1. **Pull Request 집중** - - - PR을 단일 기능 또는 버그 수정으로 제한 - - 더 큰 변경사항을 더 작고 관련된 PR로 분할 - - 독립적으로 검토할 수 있는 논리적인 커밋으로 변경사항 분할 - -2. **코드 품질** +3. **디버깅:** VS Code에서 `F5`를 눌러 실행하세요. - - 모든 PR은 린팅 및 포맷팅을 포함한 CI 검사를 통과해야 함 - - 제출하기 전에 모든 ESLint 경고나 오류 해결 - - Ellipsis, 자동화된 코드 리뷰 도구의 모든 피드백에 응답 - - TypeScript 모범 사례를 따르고 타입 안전성 유지 +### 코드 작성 가이드라인 -3. **테스팅** +- 하나의 기능 또는 수정당 하나의 집중된 PR. +- ESLint와 TypeScript 모범 사례를 따르세요. +- Issue를 참조하는 명확한 커밋 메시지를 작성하세요(예: `Fixes #123`). +- 철저한 테스트를 제공하세요(`npm test`). +- 제출 전 최신 `main` 브랜치에 리베이스하세요. - - 새로운 기능에 대한 테스트 추가 - - 모든 테스트가 통과하는지 확인하기 위해 `npm test` 실행 - - 변경사항이 영향을 미치는 경우 기존 테스트 업데이트 - - 적절한 경우 단위 테스트와 통합 테스트 모두 포함 +### Pull Request 제출 -4. **커밋 가이드라인** +- 초기 피드백을 원한다면 **드래프트 PR**로 시작하세요. +- Pull Request 템플릿에 따라 변경 사항을 명확히 설명하세요. +- UI 변경에 대한 스크린샷/동영상을 제공하세요. +- 문서 업데이트가 필요한지 표시하세요. - - 명확하고 설명적인 커밋 메시지 작성 - - #이슈-번호를 사용하여 커밋에서 관련 이슈 참조 +### Pull Request 정책 -5. **제출 전** +- 사전 승인 및 할당된 Issue를 참조해야 합니다. +- 정책을 준수하지 않는 PR은 닫힐 수 있습니다. +- PR은 CI 테스트를 통과하고, 로드맵에 부합하며, 명확한 문서를 갖추어야 합니다. - - 최신 main에 브랜치 리베이스 - - 브랜치가 성공적으로 빌드되는지 확인 - - 모든 테스트가 통과하는지 다시 확인 - - 디버깅 코드나 콘솔 로그가 있는지 변경사항 검토 +### 리뷰 프로세스 -6. **Pull Request 설명** - - 변경사항이 무엇을 하는지 명확하게 설명 - - 변경사항을 테스트하는 단계 포함 - - 모든 주요 변경사항 나열 - - UI 변경사항에 대한 스크린샷 추가 +- **일일 분류:** 메인테이너의 빠른 검토. +- **주간 심층 리뷰:** 종합적인 평가. +- **피드백에 따라 신속히 반복**하세요. -## 기여 동의 +## 법적 안내 -Pull request를 제출함으로써, 귀하의 기여는 프로젝트와 동일한 라이선스([Apache 2.0](../LICENSE))에 따라 라이선스가 부여된다는 데 동의합니다. +기여함으로써, 귀하의 기여가 Roo Code의 라이선스와 일치하는 Apache 2.0 라이선스 하에 제공됨에 동의합니다. diff --git a/locales/ko/README.md b/locales/ko/README.md index 29e2358178c..e4e94c4d1b6 100644 --- a/locales/ko/README.md +++ b/locales/ko/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ 유연한 코딩 파트너, 시스템 아키텍트, QA 엔지니어나 제품 관리자와 같은 전문화된 역할을 찾고 있든, Roo Code는 더 효율적으로 소프트웨어를 구축하는 데 도움이 될 수 있습니다. -상세한 업데이트 및 수정 사항은 [CHANGELOG](../CHANGELOG.md)를 확인하세요. +상세한 업데이트 및 수정 사항은 [CHANGELOG](../../CHANGELOG.md)를 확인하세요. --- -## 🎉 Roo Code 3.15 출시 +## 🎉 Roo Code 3.17 출시 -Roo Code 3.15가 사용자 피드백을 바탕으로 새로운 기능과 개선 사항을 제공합니다! +Roo Code 3.17이 사용자 피드백을 바탕으로 강력한 새로운 기능과 개선 사항을 제공합니다! -- **Vertex용 프롬프트 캐싱** - Vertex AI에서 이제 프롬프트 캐싱을 지원하여 응답 시간을 개선하고 API 비용을 절감합니다. -- **터미널 폴백 메커니즘** - VSCode 터미널 쉘 통합이 실패할 때 작동하는 폴백 메커니즘을 구현하여 더 안정적인 터미널 작업을 보장합니다. -- **개선된 코드 스니펫** - 채팅 인터페이스에서 코드 스니펫의 렌더링과 상호작용을 개선하여 가독성과 사용성을 향상시켰습니다. +- **Gemini용 암시적 캐싱** - Gemini API 호출이 이제 자동으로 캐시되어 API 비용이 절감됩니다. +- **더 스마트한 모드 선택** - 모드 정의에 각 모드가 언제 사용되어야 하는지에 대한 지침을 포함할 수 있어 더 나은 오케스트레이션이 가능해졌습니다. +- **지능형 컨텍스트 응축** - 컨텍스트가 가득 찼을 때 잘라내는 대신 대화 기록을 지능적으로 요약합니다(설정 -> 실험적 기능에서 활성화). --- @@ -178,32 +178,36 @@ code --install-extension bin/roo-cline-.vsix Roo Code를 더 좋게 만드는 데 도움을 준 모든 기여자에게 감사드립니다! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## 라이선스 diff --git a/locales/nl/CODE_OF_CONDUCT.md b/locales/nl/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..2c9ba7d3f5f --- /dev/null +++ b/locales/nl/CODE_OF_CONDUCT.md @@ -0,0 +1,52 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • Nederlands • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + +# Contributor Covenant Gedragscode + +## Onze Belofte + +In het belang van het bevorderen van een open en gastvrije omgeving, beloven wij als bijdragers en beheerders om deelname aan ons project en onze community een ervaring zonder intimidatie te maken voor iedereen, ongeacht leeftijd, lichaamsgrootte, handicap, etniciteit, geslachtskenmerken, genderidentiteit en -expressie, ervaringsniveau, opleiding, sociaal-economische status, nationaliteit, uiterlijk, ras, religie of seksuele identiteit en oriëntatie. + +## Onze Standaarden + +Voorbeelden van gedrag dat bijdraagt aan het creëren van een positieve omgeving zijn onder andere: + +- Gebruik van gastvrije en inclusieve taal +- Respect tonen voor verschillende standpunten en ervaringen +- Constructieve kritiek gracieus accepteren +- Focus op wat het beste is voor de community +- Empathie tonen voor andere communityleden + +Voorbeelden van onacceptabel gedrag van deelnemers zijn onder andere: + +- Het gebruik van seksueel getinte taal of beelden en ongewenste seksuele aandacht of toenadering +- Trollgedrag, beledigende/denigrerende opmerkingen en persoonlijke of politieke aanvallen +- Openbare of privé-intimidatie +- Het publiceren van andermans privé-informatie, zoals een fysiek of elektronisch adres, zonder uitdrukkelijke toestemming +- Ander gedrag dat redelijkerwijs als ongepast kan worden beschouwd in een professionele omgeving + +## Onze Verantwoordelijkheden + +Projectbeheerders zijn verantwoordelijk voor het verduidelijken van de normen voor acceptabel gedrag en worden geacht passende en eerlijke corrigerende maatregelen te nemen in reactie op gevallen van onacceptabel gedrag. + +Projectbeheerders hebben het recht en de verantwoordelijkheid om opmerkingen, commits, code, wiki-bewerkingen, issues en andere bijdragen die niet in overeenstemming zijn met deze Gedragscode te verwijderen, te bewerken of te weigeren, of om elke bijdrager tijdelijk of permanent te verbannen voor ander gedrag dat zij ongepast, bedreigend, beledigend of schadelijk achten. + +## Toepassingsgebied + +Deze Gedragscode is van toepassing binnen projectruimtes en in openbare ruimtes wanneer een individu het project of de community vertegenwoordigt. Voorbeelden van vertegenwoordiging van een project of community zijn het gebruik van een officieel project-e-mailadres, posten via een officieel socialmedia-account of optreden als een aangestelde vertegenwoordiger op een online of offline evenement. De definitie van vertegenwoordiging van een project kan verder worden gespecificeerd door projectbeheerders. + +## Handhaving + +Voorvallen van beledigend, intimiderend of anderszins onacceptabel gedrag kunnen worden gemeld door contact op te nemen met het projectteam via support@roocode.com. Alle klachten zullen worden beoordeeld en onderzocht en zullen resulteren in een reactie die passend wordt geacht voor de omstandigheden. Het projectteam is verplicht vertrouwelijkheid te bewaren met betrekking tot de melder van het incident. Verdere details over specifiek handhavingsbeleid kunnen afzonderlijk worden gepubliceerd. + +Projectbeheerders die de Gedragscode niet te goeder trouw volgen of handhaven, kunnen tijdelijke of permanente gevolgen ondervinden zoals bepaald door andere leden van het leiderschap van het project. + +## Toeschrijving + +Deze Gedragscode is gebaseerd op [Cline's versie][cline_coc] van de [Contributor Covenant][homepage], versie 1.4, beschikbaar op https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[cline_coc]: https://github.com/cline/cline/blob/main/CODE_OF_CONDUCT.md +[homepage]: https://www.contributor-covenant.org + +Voor antwoorden op veelgestelde vragen over deze gedragscode, zie https://www.contributor-covenant.org/faq diff --git a/locales/nl/CONTRIBUTING.md b/locales/nl/CONTRIBUTING.md new file mode 100644 index 00000000000..46cc80fa304 --- /dev/null +++ b/locales/nl/CONTRIBUTING.md @@ -0,0 +1,129 @@ +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • Nederlands • [Русский](../ru/CONTRIBUTING.md) + +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) + +# Bijdragen aan Roo Code + +Roo Code is een door de community gedreven project en we waarderen elke bijdrage enorm. Om de samenwerking te stroomlijnen, werken we volgens een [Issue-First](#issue-first-aanpak) principe, wat betekent dat alle [Pull Requests (PR's)](#een-pull-request-indienen) eerst gekoppeld moeten worden aan een GitHub Issue. Lees deze gids zorgvuldig door. + +## Inhoudsopgave + +- [Voordat je bijdraagt](#voordat-je-bijdraagt) +- [Je bijdrage vinden & plannen](#je-bijdrage-vinden--plannen) +- [Ontwikkelings- & indieningsproces](#ontwikkelings--indieningsproces) +- [Juridisch](#juridisch) + +## Voordat je bijdraagt + +### 1. Gedragscode + +Alle bijdragers moeten zich houden aan onze [Gedragscode](./CODE_OF_CONDUCT.md). + +### 2. De project-roadmap + +Onze roadmap bepaalt de richting van het project. Stem je bijdragen af op deze kernpunten: + +### Betrouwbaarheid eerst + +- Zorgen dat diff-bewerking en opdrachtuitvoering consistent betrouwbaar zijn +- Verminderen van wrijvingspunten die regelmatig gebruik ontmoedigen +- Garanderen van soepele werking in alle talen en op alle platforms +- Uitbreiden van robuuste ondersteuning voor een breed scala aan AI-providers en -modellen + +### Verbeterde gebruikerservaring + +- Vereenvoudigen van de gebruikersinterface voor meer duidelijkheid en intuïtiviteit +- Continu verbeteren van de workflow om te voldoen aan de hoge verwachtingen van ontwikkelaars + +### Voorop lopen in agent-prestaties + +- Opstellen van uitgebreide evaluatiebenchmarks (evals) om productiviteit in de echte wereld te meten +- Het voor iedereen gemakkelijk maken om deze evaluaties uit te voeren en te interpreteren +- Verbeteringen leveren die duidelijke stijgingen in evaluatiescores aantonen + +Vermeld de afstemming met deze gebieden in je PR's. + +### 3. Word lid van de Roo Code-community + +- **Hoofdmethode:** Word lid van onze [Discord](https://discord.gg/roocode) en stuur een DM naar **Hannes Rudolph (`hrudolph`)**. +- **Alternatief:** Ervaren bijdragers kunnen direct meedoen via [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). + +## Je bijdrage vinden & plannen + +### Soorten bijdragen + +- **Bugfixes:** Problemen in code oplossen. +- **Nieuwe functies:** Functionaliteit toevoegen. +- **Documentatie:** Handleidingen verbeteren en verduidelijken. + +### Issue-First-aanpak + +Elke bijdrage moet beginnen met een GitHub Issue. + +- **Bestaande issues controleren:** Zoek in [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Issue aanmaken:** Gebruik de juiste templates: + - **Bugs:** "Bug Report"-template. + - **Functies:** "Detailed Feature Proposal"-template. Goedkeuring vereist voor je begint. +- **Issues claimen:** Reageer en wacht op officiële toewijzing. + +**PR's zonder goedgekeurde issues kunnen worden gesloten.** + +### Bepalen waar je aan werkt + +- Bekijk het [GitHub Project](https://github.com/orgs/RooVetGit/projects/1) voor niet-toegewezen "Good First Issues". +- Voor documentatie, bezoek [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). + +### Bugs of problemen melden + +- Controleer eerst of er al meldingen zijn. +- Maak nieuwe bugmeldingen met de ["Bug Report"-template](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Beveiligingsproblemen:** Meld privé via [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). + +## Ontwikkelings- & indieningsproces + +### Ontwikkelomgeving instellen + +1. **Fork & Clone:** + +``` +git clone https://github.com/JOUW_GEBRUIKERSNAAM/Roo-Code.git +``` + +2. **Installeer afhankelijkheden:** + +``` +npm run install:all +``` + +3. **Debuggen:** Open met VS Code (`F5`). + +### Richtlijnen voor het schrijven van code + +- Eén gerichte PR per functie of fix. +- Volg ESLint en TypeScript best practices. +- Schrijf duidelijke, beschrijvende commits die verwijzen naar issues (bijv. `Fixes #123`). +- Zorg voor grondige tests (`npm test`). +- Rebase op de nieuwste `main`-branch vóór indiening. + +### Een Pull Request indienen + +- Begin als **concept-PR** als je vroege feedback zoekt. +- Beschrijf je wijzigingen duidelijk volgens de Pull Request Template. +- Voeg screenshots/video's toe voor UI-wijzigingen. +- Geef aan of documentatie-updates nodig zijn. + +### Pull Request beleid + +- Moet verwijzen naar vooraf goedgekeurde en toegewezen issues. +- PR's die niet aan het beleid voldoen, kunnen worden gesloten. +- PR's moeten CI-tests doorstaan, aansluiten bij de roadmap en duidelijke documentatie hebben. + +### Reviewproces + +- **Dagelijkse triage:** Snelle controles door maintainers. +- **Wekelijkse diepgaande review:** Uitgebreide beoordeling. +- **Snel itereren** op basis van feedback. + +## Juridisch + +Door een pull request in te dienen, ga je ermee akkoord dat je bijdragen worden gelicenseerd onder de Apache 2.0-licentie, in overeenstemming met de licentie van Roo Code. diff --git a/locales/nl/README.md b/locales/nl/README.md new file mode 100644 index 00000000000..d172499037a --- /dev/null +++ b/locales/nl/README.md @@ -0,0 +1,220 @@ +
+ + +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • Nederlands • [Русский](../../locales/ru/README.md) + + + + +[日本語](../../locales/ja/README.md) • [한국어](../../locales/ko/README.md) • [Polski](../../locales/pl/README.md) • [Português (BR)](../../locales/pt-BR/README.md) • [Türkçe](../../locales/tr/README.md) • [Tiếng Việt](../../locales/vi/README.md) • [简体中文](../../locales/zh-CN/README.md) • [繁體中文](../../locales/zh-TW/README.md) + + +
+
+
+

Roo Code (voorheen Roo Cline)

+

+ +

+

Verbind met ontwikkelaars, draag ideeën bij en blijf op de hoogte met de nieuwste AI-gestuurde coderingstools.

+ + Join Discord + Join Reddit +
+
+
+ +
+ +Download op VS Marketplace +Feature Requests +Beoordeel & Review +Documentatie + +
+ +**Roo Code** is een AI-gestuurde **autonome codeeragent** die in je editor leeft. Het kan: + +- Communiceren in natuurlijke taal +- Bestanden direct in je werkruimte lezen en schrijven +- Terminalcommando's uitvoeren +- Browseracties automatiseren +- Integreren met elke OpenAI-compatibele of aangepaste API/model +- Zijn "persoonlijkheid" en mogelijkheden aanpassen via **Aangepaste Modi** + +Of je nu op zoek bent naar een flexibele codeerpartner, een systeemarchitect, of gespecialiseerde rollen zoals QA-engineer of productmanager, Roo Code helpt je efficiënter software te bouwen. + +Bekijk de [CHANGELOG](../../CHANGELOG.md) voor gedetailleerde updates en fixes. + +--- + +## 🎉 Roo Code 3.17 Uitgebracht + +Roo Code 3.17 brengt krachtige nieuwe functies en verbeteringen op basis van jullie feedback! + +- **Impliciete caching voor Gemini** - Gemini API-aanroepen worden nu automatisch gecachet, waardoor API-kosten worden verlaagd. +- **Slimmere modeselectie** - Modedefinities kunnen nu richtlijnen bevatten over wanneer elke modus moet worden gebruikt, wat betere orkestratie mogelijk maakt. +- **Intelligente contextcompressie** - Vat de gespreksgeschiedenis intelligent samen wanneer de context vol raakt in plaats van deze af te kappen (in te schakelen via Instellingen -> Experimenteel). + +--- + +## Wat kan Roo Code? + +- 🚀 **Genereer code** vanuit natuurlijke taalbeschrijvingen +- 🔧 **Refactor & Debug** bestaande code +- 📝 **Schrijf & Update** documentatie +- 🤔 **Beantwoord vragen** over je codebase +- 🔄 **Automatiseer** repetitieve taken +- 🏗️ **Maak** nieuwe bestanden en projecten + +## Snelstart + +1. [Installeer Roo Code](https://docs.roocode.com/getting-started/installing) +2. [Verbind je AI-provider](https://docs.roocode.com/getting-started/connecting-api-provider) +3. [Probeer je eerste taak](https://docs.roocode.com/getting-started/your-first-task) + +## Belangrijkste functies + +### Meerdere Modi + +Roo Code past zich aan jouw behoeften aan met gespecialiseerde [modi](https://docs.roocode.com/basic-usage/using-modes): + +- **Code-modus:** Voor algemene coderingstaken +- **Architect-modus:** Voor planning en technisch leiderschap +- **Vraag-modus:** Voor het beantwoorden van vragen en het geven van informatie +- **Debug-modus:** Voor systematische probleemdiagnose +- **[Aangepaste modi](https://docs.roocode.com/advanced-usage/custom-modes):** Maak onbeperkt gespecialiseerde persona's voor beveiligingsaudits, prestatieoptimalisatie, documentatie of andere taken + +### Slimme Tools + +Roo Code wordt geleverd met krachtige [tools](https://docs.roocode.com/basic-usage/how-tools-work) die kunnen: + +- Bestanden in je project lezen en schrijven +- Commando's uitvoeren in je VS Code-terminal +- Een webbrowser aansturen +- Externe tools gebruiken via [MCP (Model Context Protocol)](https://docs.roocode.com/advanced-usage/mcp) + +MCP breidt de mogelijkheden van Roo Code uit door je in staat te stellen onbeperkt aangepaste tools toe te voegen. Integreer met externe API's, maak verbinding met databases of creëer gespecialiseerde ontwikkeltools - MCP biedt het framework om Roo Code uit te breiden naar jouw specifieke wensen. + +### Aanpassen + +Laat Roo Code werken zoals jij wilt met: + +- [Aangepaste instructies](https://docs.roocode.com/advanced-usage/custom-instructions) voor gepersonaliseerd gedrag +- [Aangepaste modi](https://docs.roocode.com/advanced-usage/custom-modes) voor specialistische taken +- [Lokale modellen](https://docs.roocode.com/advanced-usage/local-models) voor offline gebruik +- [Auto-Approve-instellingen](https://docs.roocode.com/advanced-usage/auto-approving-actions) voor snellere workflows + +## Bronnen + +### Documentatie + +- [Basisgebruik](https://docs.roocode.com/basic-usage/the-chat-interface) +- [Geavanceerde functies](https://docs.roocode.com/advanced-usage/auto-approving-actions) +- [Veelgestelde vragen](https://docs.roocode.com/faq) + +### Community + +- **Discord:** [Word lid van onze Discord-server](https://discord.gg/roocode) voor directe hulp en discussies +- **Reddit:** [Bezoek onze subreddit](https://www.reddit.com/r/RooCode) om ervaringen en tips te delen +- **GitHub:** Meld [problemen](https://github.com/RooVetGit/Roo-Code/issues) of vraag [features](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) aan + +--- + +## Lokale installatie & ontwikkeling + +1. **Kloon** de repo: + +```sh +git clone https://github.com/RooVetGit/Roo-Code.git +``` + +2. **Installeer afhankelijkheden**: + +```sh +npm run install:all +``` + +3. **Start de webview (Vite/React-app met HMR)**: + +```sh +npm run dev +``` + +4. **Debuggen**: + Druk op `F5` (of **Run** → **Start Debugging**) in VSCode om een nieuwe sessie met Roo Code te openen. + +Wijzigingen aan de webview verschijnen direct. Wijzigingen aan de core-extensie vereisen een herstart van de extensiehost. + +Je kunt ook een .vsix bouwen en deze direct in VSCode installeren: + +```sh +npm run build +``` + +Een `.vsix`-bestand verschijnt in de `bin/`-map en kan worden geïnstalleerd met: + +```sh +code --install-extension bin/roo-cline-.vsix +``` + +We gebruiken [changesets](https://github.com/changesets/changesets) voor versiebeheer en publicatie. Bekijk onze `CHANGELOG.md` voor release-opmerkingen. + +--- + +## Disclaimer + +**Let op**: Roo Code, Inc geeft **geen** garanties of waarborgen met betrekking tot enige code, modellen of andere tools die worden geleverd of beschikbaar worden gesteld in verband met Roo Code, bijbehorende tools van derden, of enige resulterende output. Je neemt **alle risico's** die gepaard gaan met het gebruik van dergelijke tools of output; deze tools worden geleverd op een **"AS IS"** en **"AS AVAILABLE"** basis. Risico's kunnen onder meer zijn: inbreuk op intellectueel eigendom, cyberkwetsbaarheden of -aanvallen, vooringenomenheid, onnauwkeurigheden, fouten, defecten, virussen, uitval, verlies of schade aan eigendommen en/of persoonlijk letsel. Je bent zelf volledig verantwoordelijk voor het gebruik van dergelijke tools of output (inclusief, maar niet beperkt tot, de legaliteit, geschiktheid en resultaten daarvan). + +--- + +## Bijdragen + +We houden van bijdragen uit de community! Begin met het lezen van onze [CONTRIBUTING.md](CONTRIBUTING.md). + +--- + +## Bijdragers + +Dank aan alle bijdragers die Roo Code beter hebben gemaakt! + + + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + + + +## Licentie + +[Apache 2.0 © 2025 Roo Code, Inc.](../../LICENSE) + +--- + +**Veel plezier met Roo Code!** Of je het nu kort houdt of autonoom laat werken, we zijn benieuwd wat je bouwt. Heb je vragen of ideeën voor functies, kom dan langs op onze [Reddit-community](https://www.reddit.com/r/RooCode/) of [Discord](https://discord.gg/roocode). Veel programmeerplezier! diff --git a/locales/pl/CODE_OF_CONDUCT.md b/locales/pl/CODE_OF_CONDUCT.md index c1a723936fa..689269bfbd5 100644 --- a/locales/pl/CODE_OF_CONDUCT.md +++ b/locales/pl/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • Polski • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Kodeks Postępowania Covenant Współtwórców ## Nasze Zobowiązanie diff --git a/locales/pl/CONTRIBUTING.md b/locales/pl/CONTRIBUTING.md index b669cb2f2ce..b19e6a81645 100644 --- a/locales/pl/CONTRIBUTING.md +++ b/locales/pl/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Wkład w Roo Code +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -Cieszymy się, że jesteś zainteresowany wniesieniem wkładu do Roo Code. Czy naprawiasz błąd, dodajesz funkcję, czy ulepszasz naszą dokumentację, każdy wkład sprawia, że Roo Code staje się mądrzejszy! Aby utrzymać naszą społeczność żywą i przyjazną, wszyscy członkowie muszą przestrzegać naszego [Kodeksu Postępowania](CODE_OF_CONDUCT.md). +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • Polski • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -## Dołącz do naszej społeczności +# Współtworzenie Roo Code -Gorąco zachęcamy wszystkich współtwórców do dołączenia do naszej [społeczności Discord](https://discord.gg/roocode)! Bycie częścią naszego serwera Discord pomaga: +Roo Code to projekt napędzany przez społeczność i bardzo cenimy każdy wkład. Aby usprawnić współpracę, działamy według zasady [Issue-First](#podejście-issue-first), co oznacza, że wszystkie [Pull Requesty (PR)](#zgłaszanie-pull-requesta) muszą najpierw być powiązane z GitHub Issue. Prosimy o uważne zapoznanie się z tym przewodnikiem. -- Uzyskać pomoc i wskazówki w czasie rzeczywistym dotyczące Twoich wkładów -- Połączyć się z innymi współtwórcami i członkami głównego zespołu -- Być na bieżąco z rozwojem projektu i jego priorytetami -- Uczestniczyć w dyskusjach, które kształtują przyszłość Roo Code -- Znaleźć możliwości współpracy z innymi programistami +## Spis treści -## Zgłaszanie błędów lub problemów +- [Zanim zaczniesz współtworzyć](#zanim-zaczniesz-współtworzyć) +- [Znajdowanie i planowanie swojego wkładu](#znajdowanie-i-planowanie-swojego-wkładu) +- [Proces rozwoju i zgłaszania](#proces-rozwoju-i-zgłaszania) +- [Prawne](#prawne) -Raporty o błędach pomagają ulepszyć Roo Code dla wszystkich! Przed utworzeniem nowego zgłoszenia, proszę [przeszukaj istniejące](https://github.com/RooVetGit/Roo-Code/issues), aby uniknąć duplikatów. Kiedy jesteś gotowy, aby zgłosić błąd, przejdź do naszej [strony zgłoszeń](https://github.com/RooVetGit/Roo-Code/issues/new/choose), gdzie znajdziesz szablon, który pomoże Ci wypełnić odpowiednie informacje. +## Zanim zaczniesz współtworzyć -
- 🔐 Ważne: Jeśli odkryjesz lukę w zabezpieczeniach, proszę użyj narzędzia bezpieczeństwa Github, aby zgłosić ją prywatnie. -
+### 1. Kodeks postępowania -## Decydowanie nad czym pracować +Wszyscy współtwórcy muszą przestrzegać naszego [Kodeksu postępowania](./CODE_OF_CONDUCT.md). -Szukasz dobrego pierwszego wkładu? Sprawdź problemy w sekcji "Issue [Unassigned]" naszego [projektu Github Roo Code](https://github.com/orgs/RooVetGit/projects/1). Te zostały specjalnie wybrane dla nowych współtwórców i obszarów, gdzie chętnie przyjmiemy pomoc! +### 2. Roadmapa projektu -Cieszymy się również z wkładu do naszej [dokumentacji](https://docs.roocode.com/)! Czy to poprawianie literówek, ulepszanie istniejących przewodników, czy tworzenie nowych treści edukacyjnych - chcielibyśmy zbudować repozytorium zasobów napędzane przez społeczność, które pomaga każdemu czerpać maksimum z Roo Code. Możesz kliknąć "Edit this page" na dowolnej stronie, aby szybko przejść do odpowiedniego miejsca w Github, aby edytować plik, lub możesz przejść bezpośrednio do https://github.com/RooVetGit/Roo-Code-Docs. +Nasza roadmapa wyznacza kierunek projektu. Dostosuj swój wkład do tych kluczowych celów: -Jeśli planujesz pracować nad większą funkcją, proszę najpierw utwórz [prośbę o funkcję](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop), abyśmy mogli przedyskutować, czy jest ona zgodna z wizją Roo Code. Możesz również sprawdzić naszą [Mapę Drogową Projektu](#mapa-drogowa-projektu) poniżej, aby zobaczyć, czy Twój pomysł pasuje do naszego strategicznego kierunku. +### Niezawodność przede wszystkim -## Mapa Drogowa Projektu +- Zapewnienie, że edycja różnic i wykonywanie poleceń są konsekwentnie niezawodne +- Zmniejszenie punktów tarcia, które zniechęcają do regularnego użytkowania +- Gwarancja płynnego działania we wszystkich językach i na wszystkich platformach +- Rozszerzenie solidnego wsparcia dla szerokiej gamy dostawców i modeli AI -Roo Code posiada jasną mapę drogową rozwoju, która kieruje naszymi priorytetami i przyszłym kierunkiem. Zrozumienie naszej mapy drogowej może pomóc Ci: +### Ulepszone doświadczenie użytkownika -- Dostosować swoje wkłady do celów projektu -- Zidentyfikować obszary, w których Twoja wiedza byłaby najbardziej wartościowa -- Zrozumieć kontekst stojący za pewnymi decyzjami projektowymi -- Znaleźć inspirację dla nowych funkcji, które wspierają naszą wizję +- Uproszczenie interfejsu użytkownika dla większej przejrzystości i intuicyjności +- Ciągłe doskonalenie przepływu pracy, aby spełnić wysokie oczekiwania programistów -Nasza obecna mapa drogowa koncentruje się na sześciu kluczowych filarach: +### Wiodąca pozycja w wydajności agentów -### Wsparcie dla Dostawców +- Ustanowienie kompleksowych punktów odniesienia (evals) do mierzenia produktywności w rzeczywistym świecie +- Ułatwienie wszystkim łatwego uruchamiania i interpretowania tych ocen +- Dostarczanie ulepszeń, które wykazują wyraźny wzrost wyników ocen -Dążymy do wspierania jak największej liczby dostawców: +Wspomnij o powiązaniu z tymi obszarami w swoich PR. -- Bardziej wszechstronne wsparcie dla "OpenAI Compatible" -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Ulepszone wsparcie dla Ollama i LM Studio +### 3. Dołącz do społeczności Roo Code -### Wsparcie dla Modeli +- **Główna metoda:** Dołącz do naszego [Discorda](https://discord.gg/roocode) i wyślij wiadomość prywatną do **Hannes Rudolph (`hrudolph`)**. +- **Alternatywa:** Doświadczeni współtwórcy mogą angażować się bezpośrednio przez [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -Chcemy, aby Roo działał jak najlepiej na jak największej liczbie modeli, w tym modeli lokalnych: +## Znajdowanie i planowanie swojego wkładu -- Wsparcie dla modeli lokalnych poprzez niestandardowe promptowanie systemowe i przepływy pracy -- Benchmarki ewaluacyjne i przypadki testowe +### Typy wkładów -### Wsparcie dla Systemów +- **Poprawki błędów:** Naprawianie problemów w kodzie. +- **Nowe funkcje:** Dodawanie nowych funkcjonalności. +- **Dokumentacja:** Ulepszanie przewodników i zwiększanie przejrzystości. -Chcemy, aby Roo działał dobrze na komputerze każdego: +### Podejście Issue-First -- Integracja terminala międzyplatformowego -- Silne i spójne wsparcie dla Mac, Windows i Linux +Każdy wkład musi zaczynać się od GitHub Issue. -### Dokumentacja +- **Sprawdź istniejące issues:** Przeszukaj [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Utwórz issue:** Używaj odpowiednich szablonów: + - **Błędy:** Szablon "Bug Report". + - **Funkcje:** Szablon "Detailed Feature Proposal". Wymagane zatwierdzenie przed rozpoczęciem. +- **Zgłoś chęć pracy:** Skomentuj i poczekaj na oficjalne przypisanie. -Chcemy kompleksowej, dostępnej dokumentacji dla wszystkich użytkowników i współtwórców: +**PR bez zatwierdzonego issue może zostać zamknięty.** -- Rozszerzone przewodniki użytkownika i tutoriale -- Jasna dokumentacja API -- Lepsze wskazówki dla współtwórców -- Wielojęzyczne zasoby dokumentacji -- Interaktywne przykłady i próbki kodu +### Decydowanie, nad czym pracować -### Stabilność +- Sprawdź [Projekt GitHub](https://github.com/orgs/RooVetGit/projects/1) w poszukiwaniu nieprzypisanych "Good First Issues". +- W kwestii dokumentacji odwiedź [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). -Chcemy znacznie zmniejszyć liczbę błędów i zwiększyć zautomatyzowane testowanie: +### Zgłaszanie błędów -- Przełącznik rejestrowania debugowania -- Przycisk kopiowania "Informacji o Maszynie/Zadaniu" do wysyłania z prośbami o pomoc/zgłoszeniami błędów +- Najpierw sprawdź istniejące zgłoszenia. +- Twórz nowe zgłoszenia błędów używając [szablonu "Bug Report"](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Luki bezpieczeństwa:** Zgłaszaj prywatnie przez [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -### Internacjonalizacja +## Proces rozwoju i zgłaszania -Chcemy, aby Roo mówił językiem każdego: +### Konfiguracja środowiska -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +1. **Fork & Clone:** -Szczególnie witamy wkłady, które przyspieszają realizację celów naszej mapy drogowej. Jeśli pracujesz nad czymś, co jest zgodne z tymi filarami, proszę wspomnij o tym w opisie swojego PR. - -## Konfiguracja rozwojowa - -1. **Sklonuj** repozytorium: - -```sh -git clone https://github.com/RooVetGit/Roo-Code.git ``` - -2. **Zainstaluj zależności**: - -```sh -npm run install:all -``` - -3. **Uruchom webview (aplikację Vite/React z HMR)**: - -```sh -npm run dev +git clone https://github.com/TWÓJ_UŻYTKOWNIK/Roo-Code.git ``` -4. **Debugowanie**: - Naciśnij `F5` (lub **Uruchom** → **Rozpocznij debugowanie**) w VSCode, aby otworzyć nową sesję z załadowanym Roo Code. +2. **Instalacja zależności:** -Zmiany w webview pojawią się natychmiast. Zmiany w podstawowym rozszerzeniu będą wymagać ponownego uruchomienia hosta rozszerzenia. - -Alternatywnie możesz zbudować plik .vsix i zainstalować go bezpośrednio w VSCode: - -```sh -npm run build ``` - -Plik `.vsix` pojawi się w katalogu `bin/` i można go zainstalować za pomocą: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Pisanie i przesyłanie kodu - -Każdy może wnieść wkład w kod Roo Code, ale prosimy o przestrzeganie tych wytycznych, aby zapewnić płynną integrację Twoich wkładów: - -1. **Utrzymuj Pull Requesty skupione** - - - Ogranicz PR do jednej funkcji lub naprawy błędu - - Podziel większe zmiany na mniejsze, powiązane PR - - Podziel zmiany na logiczne commity, które można przeglądać niezależnie - -2. **Jakość kodu** +3. **Debugowanie:** Otwórz w VS Code (`F5`). - - Wszystkie PR muszą przejść kontrole CI, które obejmują zarówno linting, jak i formatowanie - - Rozwiąż wszelkie ostrzeżenia lub błędy ESLint przed przesłaniem - - Odpowiedz na wszystkie informacje zwrotne od Ellipsis, naszego zautomatyzowanego narzędzia do przeglądu kodu - - Przestrzegaj najlepszych praktyk TypeScript i zachowaj bezpieczeństwo typów +### Wytyczne dotyczące pisania kodu -3. **Testowanie** +- Jeden skoncentrowany PR na funkcję lub poprawkę. +- Przestrzegaj dobrych praktyk ESLint i TypeScript. +- Pisz jasne, opisowe commity odnoszące się do issues (np. `Fixes #123`). +- Zapewnij dokładne testy (`npm test`). +- Zrebase'uj na najnowszą gałąź `main` przed zgłoszeniem. - - Dodaj testy dla nowych funkcji - - Uruchom `npm test`, aby upewnić się, że wszystkie testy przechodzą - - Zaktualizuj istniejące testy, jeśli Twoje zmiany na nie wpływają - - Uwzględnij zarówno testy jednostkowe, jak i integracyjne, gdy jest to właściwe +### Zgłaszanie Pull Requesta -4. **Wytyczne dotyczące commitów** +- Zacznij od **wersji roboczej PR**, jeśli szukasz wczesnego feedbacku. +- Jasno opisz swoje zmiany, zgodnie z szablonem Pull Request. +- Dostarcz zrzuty ekranu/wideo dla zmian UI. +- Wskaż, czy potrzebne są aktualizacje dokumentacji. - - Pisz jasne, opisowe komunikaty commitów - - Odwołuj się do odpowiednich problemów w commitach, używając #numer-problemu +### Polityka Pull Request -5. **Przed przesłaniem** +- Musi odnosić się do wcześniej zatwierdzonych i przypisanych issues. +- PR niezgodne z polityką mogą zostać zamknięte. +- PR powinny przechodzić testy CI, być zgodne z roadmapą i mieć jasną dokumentację. - - Rebase swojej gałęzi na najnowszego maina - - Upewnij się, że Twoja gałąź buduje się pomyślnie - - Sprawdź ponownie, czy wszystkie testy przechodzą - - Przejrzyj swoje zmiany pod kątem wszelkiego kodu debugującego lub logów konsoli +### Proces recenzji -6. **Opis Pull Requesta** - - Jasno opisz, co robią Twoje zmiany - - Dołącz kroki do przetestowania zmian - - Wymień wszelkie istotne zmiany - - Dodaj zrzuty ekranu dla zmian UI +- **Codzienna selekcja:** Szybkie sprawdzenia przez maintainerów. +- **Cotygodniowy dokładny przegląd:** Kompleksowa ocena. +- **Szybko iteruj** na podstawie feedbacku. -## Umowa o współpracy +## Prawne -Przesyłając pull request, zgadzasz się, że Twoje wkłady będą licencjonowane na tej samej licencji co projekt ([Apache 2.0](../LICENSE)). +Zgłaszając pull request, zgadzasz się, że twój wkład będzie licencjonowany na licencji Apache 2.0, zgodnie z licencją Roo Code. diff --git a/locales/pl/README.md b/locales/pl/README.md index 08ad175d278..f84d974004a 100644 --- a/locales/pl/README.md +++ b/locales/pl/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ Niezależnie od tego, czy szukasz elastycznego partnera do kodowania, architekta systemu, czy wyspecjalizowanych ról, takich jak inżynier QA lub menedżer produktu, Roo Code może pomóc Ci budować oprogramowanie efektywniej. -Sprawdź [CHANGELOG](../CHANGELOG.md), aby uzyskać szczegółowe informacje o aktualizacjach i poprawkach. +Sprawdź [CHANGELOG](../../CHANGELOG.md), aby uzyskać szczegółowe informacje o aktualizacjach i poprawkach. --- -## 🎉 Roo Code 3.15 został wydany +## 🎉 Roo Code 3.17 został wydany -Roo Code 3.15 wprowadza nowe funkcje i usprawnienia na podstawie opinii użytkowników! +Roo Code 3.17 wprowadza potężne nowe funkcje i usprawnienia na podstawie opinii użytkowników! -- **Pamięć podręczna dla promptów w Vertex** - Vertex AI teraz obsługuje pamięć podręczną promptów, poprawiając czas odpowiedzi i zmniejszając koszty API. -- **Awaryjny tryb terminala** - Zaimplementowano mechanizm awaryjny na wypadek niepowodzenia integracji powłoki terminala VSCode, zapewniając bardziej niezawodne działanie terminala. -- **Ulepszone fragmenty kodu** - Udoskonalono renderowanie i interakcję z fragmentami kodu w interfejsie czatu dla lepszej czytelności i użyteczności. +- **Niejawne buforowanie dla Gemini** - Wywołania API Gemini są teraz automatycznie buforowane, zmniejszając koszty API. +- **Inteligentniejszy wybór trybu** - Definicje trybów mogą teraz zawierać wskazówki dotyczące tego, kiedy każdy tryb powinien być używany, umożliwiając lepszą orkiestrację. +- **Inteligentne kondensowanie kontekstu** - Inteligentnie podsumowuje historię konwersacji, gdy kontekst się zapełnia, zamiast ucinać (włącz w Ustawienia -> Eksperymentalne). --- @@ -178,32 +178,36 @@ Kochamy wkład społeczności! Zacznij od przeczytania naszego [CONTRIBUTING.md] Dziękujemy wszystkim naszym współtwórcom, którzy pomogli ulepszyć Roo Code! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Licencja diff --git a/locales/pt-BR/CODE_OF_CONDUCT.md b/locales/pt-BR/CODE_OF_CONDUCT.md index da84a569a03..88bcaae7061 100644 --- a/locales/pt-BR/CODE_OF_CONDUCT.md +++ b/locales/pt-BR/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • Português (BR) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Código de Conduta do Pacto de Colaboradores ## Nosso Compromisso diff --git a/locales/pt-BR/CONTRIBUTING.md b/locales/pt-BR/CONTRIBUTING.md index a3e04811b3e..a67a72f6e40 100644 --- a/locales/pt-BR/CONTRIBUTING.md +++ b/locales/pt-BR/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Contribuindo para o Roo Code - -Estamos entusiasmados por você estar interessado em contribuir para o Roo Code. Seja corrigindo um bug, adicionando um recurso ou melhorando nossa documentação, cada contribuição torna o Roo Code mais inteligente! Para manter nossa comunidade vibrante e acolhedora, todos os membros devem aderir ao nosso [Código de Conduta](CODE_OF_CONDUCT.md). - -## Junte-se à Nossa Comunidade - -Incentivamos fortemente todos os colaboradores a se juntarem à nossa [comunidade no Discord](https://discord.gg/roocode)! Fazer parte do nosso servidor Discord ajuda você a: - -- Obter ajuda e orientação em tempo real sobre suas contribuições -- Conectar-se com outros colaboradores e membros da equipe principal -- Manter-se atualizado sobre os desenvolvimentos e prioridades do projeto -- Participar de discussões que moldam o futuro do Roo Code -- Encontrar oportunidades de colaboração com outros desenvolvedores +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -## Relatando Bugs ou Problemas +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • Português (BR) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -Relatórios de bugs ajudam a tornar o Roo Code melhor para todos! Antes de criar uma nova issue, por favor [pesquise as existentes](https://github.com/RooVetGit/Roo-Code/issues) para evitar duplicatas. Quando estiver pronto para relatar um bug, vá para nossa [página de issues](https://github.com/RooVetGit/Roo-Code/issues/new/choose) onde você encontrará um modelo para ajudá-lo a preencher as informações relevantes. - -
- 🔐 Importante: Se você descobrir uma vulnerabilidade de segurança, por favor use a ferramenta de segurança do Github para relatá-la de forma privada. -
+# Contribuindo para o Roo Code -## Decidindo no que Trabalhar +O Roo Code é um projeto impulsionado pela comunidade e valorizamos muito cada contribuição. Para simplificar a colaboração, operamos com uma abordagem [Issue-First](#abordagem-issue-first), o que significa que todos os [Pull Requests (PRs)](#enviando-um-pull-request) devem primeiro estar vinculados a uma Issue do GitHub. Por favor, leia este guia com atenção. -Procurando uma boa primeira contribuição? Verifique as issues na seção "Issue [Unassigned]" do nosso [Projeto Github Roo Code](https://github.com/orgs/RooVetGit/projects/1). Estas são especialmente selecionadas para novos colaboradores e áreas onde gostaríamos de ter alguma ajuda! +## Índice -Também damos as boas-vindas a contribuições para nossa [documentação](https://docs.roocode.com/)! Seja corrigindo erros de digitação, melhorando guias existentes ou criando novo conteúdo educacional - adoraríamos construir um repositório de recursos impulsionado pela comunidade que ajude todos a obter o máximo do Roo Code. Você pode clicar em "Edit this page" em qualquer página para ir rapidamente ao local certo no Github para editar o arquivo, ou pode mergulhar diretamente em https://github.com/RooVetGit/Roo-Code-Docs. +- [Antes de Contribuir](#antes-de-contribuir) +- [Encontrando & Planejando sua Contribuição](#encontrando--planejando-sua-contribuição) +- [Processo de Desenvolvimento & Submissão](#processo-de-desenvolvimento--submissão) +- [Legal](#legal) -Se você está planejando trabalhar em um recurso maior, por favor crie primeiro uma [solicitação de recurso](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) para que possamos discutir se está alinhado com a visão do Roo Code. Você também pode verificar nosso [Roteiro do Projeto](#roteiro-do-projeto) abaixo para ver se sua ideia se encaixa em nossa direção estratégica. +## Antes de Contribuir -## Roteiro do Projeto +### 1. Código de Conduta -O Roo Code possui um roteiro de desenvolvimento claro que orienta nossas prioridades e direção futura. Entender nosso roteiro pode ajudar você a: +Todos os colaboradores devem seguir nosso [Código de Conduta](./CODE_OF_CONDUCT.md). -- Alinhar suas contribuições com os objetivos do projeto -- Identificar áreas onde sua expertise seria mais valiosa -- Entender o contexto por trás de certas decisões de design -- Encontrar inspiração para novos recursos que apoiem nossa visão +### 2. Roadmap do Projeto -Nosso roteiro atual se concentra em seis pilares principais: +Nosso roadmap orienta a direção do projeto. Alinhe suas contribuições com estes objetivos principais: -### Suporte a Provedores +### Confiabilidade em Primeiro Lugar -Nosso objetivo é oferecer suporte a tantos provedores quanto possível: +- Garantir que a edição de diferenças e a execução de comandos sejam consistentemente confiáveis +- Reduzir pontos de atrito que desencorajam o uso regular +- Garantir operação suave em todos os idiomas e plataformas +- Expandir o suporte robusto para uma ampla variedade de provedores e modelos de IA -- Suporte mais versátil para "OpenAI Compatible" -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Suporte aprimorado para Ollama e LM Studio +### Experiência de Usuário Aprimorada -### Suporte a Modelos +- Simplificar a interface do usuário para maior clareza e intuitividade +- Melhorar continuamente o fluxo de trabalho para atender às altas expectativas dos desenvolvedores -Queremos que o Roo funcione bem em tantos modelos quanto possível, incluindo modelos locais: +### Liderança em Desempenho de Agentes -- Suporte a modelos locais através de prompts de sistema personalizados e fluxos de trabalho -- Avaliações de benchmark e casos de teste +- Estabelecer benchmarks de avaliação abrangentes (evals) para medir a produtividade no mundo real +- Facilitar para que todos possam executar e interpretar essas avaliações +- Fornecer melhorias que demonstrem aumentos claros nas pontuações de avaliação -### Suporte a Sistemas +Mencione o alinhamento com estas áreas em seus PRs. -Queremos que o Roo funcione bem no computador de todos: +### 3. Junte-se à Comunidade Roo Code -- Integração de terminal multiplataforma -- Suporte forte e consistente para Mac, Windows e Linux +- **Principal:** Junte-se ao nosso [Discord](https://discord.gg/roocode) e envie um DM para **Hannes Rudolph (`hrudolph`)**. +- **Alternativa:** Colaboradores experientes podem participar diretamente via [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -### Documentação +## Encontrando & Planejando sua Contribuição -Queremos documentação abrangente e acessível para todos os usuários e colaboradores: +### Tipos de Contribuição -- Guias de usuário e tutoriais expandidos -- Documentação clara da API -- Melhor orientação para colaboradores -- Recursos de documentação multilíngues -- Exemplos interativos e amostras de código +- **Correção de bugs:** Corrigir problemas no código. +- **Novos recursos:** Adicionar novas funcionalidades. +- **Documentação:** Melhorar guias e clareza. -### Estabilidade +### Abordagem Issue-First -Queremos diminuir significativamente o número de bugs e aumentar os testes automatizados: +Todas as contribuições devem começar com uma Issue do GitHub. -- Interruptor de registro de depuração -- Botão de cópia "Informações de Máquina/Tarefa" para enviar com solicitações de suporte/bug +- **Verificar issues existentes:** Procure em [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Criar uma issue:** Use os templates apropriados: + - **Bugs:** Template "Bug Report". + - **Recursos:** Template "Detailed Feature Proposal". Aprovação necessária antes de começar. +- **Reivindicar issues:** Comente e aguarde atribuição oficial. -### Internacionalização +**PRs sem issues aprovadas podem ser fechados.** -Queremos que o Roo fale o idioma de todos: +### Decidindo no que Trabalhar -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +- Confira o [Projeto GitHub](https://github.com/orgs/RooVetGit/projects/1) para "Good First Issues" não atribuídas. +- Para documentação, visite [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). -Damos especialmente as boas-vindas a contribuições que avançam os objetivos do nosso roteiro. Se você estiver trabalhando em algo que se alinha com esses pilares, por favor mencione isso na descrição do seu PR. +### Relatando Bugs -## Configuração de Desenvolvimento +- Verifique primeiro se já existem relatórios. +- Crie novos relatórios de bugs usando o [template "Bug Report"](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Vulnerabilidades de segurança:** Relate de forma privada via [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -1. **Clone** o repositório: +## Processo de Desenvolvimento & Submissão -```sh -git clone https://github.com/RooVetGit/Roo-Code.git -``` +### Configuração de Desenvolvimento -2. **Instale as dependências**: +1. **Fork & Clone:** -```sh -npm run install:all ``` - -3. **Inicie o webview (aplicativo Vite/React com HMR)**: - -```sh -npm run dev +git clone https://github.com/SEU_USUÁRIO/Roo-Code.git ``` -4. **Depuração**: - Pressione `F5` (ou **Executar** → **Iniciar Depuração**) no VSCode para abrir uma nova sessão com o Roo Code carregado. - -Alterações no webview aparecerão imediatamente. Alterações na extensão principal exigirão a reinicialização do host da extensão. - -Alternativamente, você pode construir um .vsix e instalá-lo diretamente no VSCode: +2. **Instalar dependências:** -```sh -npm run build ``` - -Um arquivo `.vsix` aparecerá no diretório `bin/` que pode ser instalado com: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Escrevendo e Enviando Código - -Qualquer pessoa pode contribuir com código para o Roo Code, mas pedimos que você siga estas diretrizes para garantir que suas contribuições possam ser integradas sem problemas: - -1. **Mantenha os Pull Requests Focados** - - - Limite os PRs a um único recurso ou correção de bug - - Divida mudanças maiores em PRs menores e relacionados - - Divida as mudanças em commits lógicos que possam ser revisados independentemente - -2. **Qualidade do Código** +3. **Depuração:** Abra com VS Code (`F5`). - - Todos os PRs devem passar nas verificações de CI que incluem tanto linting quanto formatação - - Resolva quaisquer avisos ou erros do ESLint antes de enviar - - Responda a todos os feedbacks do Ellipsis, nossa ferramenta automatizada de revisão de código - - Siga as melhores práticas de TypeScript e mantenha a segurança de tipos +### Diretrizes para Escrever Código -3. **Testes** +- Um PR focado por recurso ou correção. +- Siga as melhores práticas de ESLint e TypeScript. +- Escreva commits claros e descritivos referenciando issues (ex: `Fixes #123`). +- Forneça testes completos (`npm test`). +- Rebase na branch `main` mais recente antes do envio. - - Adicione testes para novos recursos - - Execute `npm test` para garantir que todos os testes passem - - Atualize os testes existentes se suas mudanças os afetarem - - Inclua tanto testes unitários quanto de integração quando apropriado +### Enviando um Pull Request -4. **Diretrizes de Commit** +- Comece como **PR em rascunho** se buscar feedback antecipado. +- Descreva claramente suas alterações seguindo o Template de Pull Request. +- Forneça capturas de tela/vídeos para alterações de UI. +- Indique se atualizações de documentação são necessárias. - - Escreva mensagens de commit claras e descritivas - - Referencie issues relevantes nos commits usando #número-da-issue +### Política de Pull Request -5. **Antes de Enviar** +- Deve referenciar issues pré-aprovadas e atribuídas. +- PRs que não seguem a política podem ser fechados. +- PRs devem passar nos testes de CI, alinhar-se ao roadmap e ter documentação clara. - - Faça rebase da sua branch na última main - - Certifique-se de que sua branch é construída com sucesso - - Verifique novamente se todos os testes estão passando - - Revise suas mudanças para qualquer código de depuração ou logs de console +### Processo de Revisão -6. **Descrição do Pull Request** - - Descreva claramente o que suas mudanças fazem - - Inclua passos para testar as mudanças - - Liste quaisquer mudanças significativas - - Adicione capturas de tela para mudanças na UI +- **Triagem diária:** Verificações rápidas pelos mantenedores. +- **Revisão semanal detalhada:** Avaliação abrangente. +- **Itere rapidamente** com base no feedback. -## Acordo de Contribuição +## Legal -Ao enviar um pull request, você concorda que suas contribuições serão licenciadas sob a mesma licença do projeto ([Apache 2.0](../LICENSE)). +Ao enviar um pull request, você concorda que suas contribuições serão licenciadas sob a Licença Apache 2.0, consistente com o licenciamento do Roo Code. diff --git a/locales/pt-BR/README.md b/locales/pt-BR/README.md index 153580a893f..10d827d9583 100644 --- a/locales/pt-BR/README.md +++ b/locales/pt-BR/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ Seja você esteja buscando um parceiro de codificação flexível, um arquiteto de sistema ou funções especializadas como engenheiro de QA ou gerente de produto, o Roo Code pode ajudá-lo a construir software com mais eficiência. -Confira o [CHANGELOG](../CHANGELOG.md) para atualizações e correções detalhadas. +Confira o [CHANGELOG](../../CHANGELOG.md) para atualizações e correções detalhadas. --- -## 🎉 Roo Code 3.15 Lançado +## 🎉 Roo Code 3.17 Lançado -O Roo Code 3.15 traz novas funcionalidades e melhorias baseadas no seu feedback! +O Roo Code 3.17 traz poderosas novas funcionalidades e melhorias baseadas no seu feedback! -- **Cache para prompts no Vertex** - O Vertex AI agora suporta cache de prompts, melhorando os tempos de resposta e reduzindo custos de API. -- **Fallback para Terminal** - Implementado um mecanismo de fallback quando a integração do shell do terminal do VSCode falha, garantindo operações de terminal mais confiáveis. -- **Snippets de Código Aprimorados** - Renderização e interação aprimoradas com snippets de código na interface de chat para melhor legibilidade e usabilidade. +- **Cache Implícito para Gemini** - Chamadas de API Gemini agora são automaticamente armazenadas em cache, reduzindo custos de API. +- **Seleção de Modo mais Inteligente** - Definições de modo agora podem incluir orientações sobre quando cada modo deve ser usado, permitindo melhor orquestração. +- **Condensação Inteligente de Contexto** - Resume de forma inteligente o histórico de conversas quando o contexto se enche, em vez de truncar (ative em Configurações -> Experimental). --- @@ -178,32 +178,36 @@ Adoramos contribuições da comunidade! Comece lendo nosso [CONTRIBUTING.md](CON Obrigado a todos os nossos contribuidores que ajudaram a tornar o Roo Code melhor! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Licença diff --git a/locales/ru/CODE_OF_CONDUCT.md b/locales/ru/CODE_OF_CONDUCT.md index cfc93a3b73c..203a836d24f 100644 --- a/locales/ru/CODE_OF_CONDUCT.md +++ b/locales/ru/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • Русский + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Кодекс поведения участников ## Наше обязательство diff --git a/locales/ru/CONTRIBUTING.md b/locales/ru/CONTRIBUTING.md index 0c12ef5cd22..38d75b3f9dc 100644 --- a/locales/ru/CONTRIBUTING.md +++ b/locales/ru/CONTRIBUTING.md @@ -1,72 +1,129 @@ -# Руководство по участию в проекте +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • Русский -Спасибо за интерес к участию в развитии Roo Code! Мы рады приветствовать новых участников в нашем сообществе. +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -## Присоединяйтесь к сообществу +# Вклад в Roo Code -- [Discord](https://discord.gg/roocode) -- [Reddit](https://www.reddit.com/r/roocode) +Roo Code — проект, управляемый сообществом, и мы высоко ценим каждый вклад. Для упрощения сотрудничества мы работаем по принципу [Issue-First](#подход-issue-first), что означает, что все [Pull Request (PR)](#отправка-pull-request) должны сначала быть связаны с GitHub Issue. Пожалуйста, внимательно ознакомься с этим руководством. -## Сообщение об ошибках +## Содержание -Если вы обнаружили ошибку, пожалуйста, создайте issue в нашем репозитории. Убедитесь, что: +- [Перед тем как внести вклад](#перед-тем-как-внести-вклад) +- [Поиск и планирование вклада](#поиск-и-планирование-вклада) +- [Процесс разработки и отправки](#процесс-разработки-и-отправки) +- [Юридическая информация](#юридическая-информация) -1. Ошибка воспроизводима -2. Вы предоставили всю необходимую информацию для воспроизведения ошибки -3. Вы проверили, что подобная проблема еще не была зарегистрирована +## Перед тем как внести вклад -## Над чем работать +### 1. Кодекс поведения -Есть несколько способов начать участие в проекте: +Все участники должны соблюдать наш [Кодекс поведения](./CODE_OF_CONDUCT.md). -1. Просмотрите открытые issues с меткой "good first issue" -2. Исправьте опечатки в документации -3. Добавьте тесты для существующего кода -4. Предложите новые функции через issues +### 2. Дорожная карта проекта -## Дорожная карта проекта +Наша дорожная карта определяет направление проекта. Согласуй свой вклад с этими ключевыми целями: -Наши текущие приоритеты: +### Надежность в первую очередь -- Улучшение производительности и стабильности -- Расширение поддержки языков программирования -- Улучшение пользовательского интерфейса -- Интеграция с популярными инструментами разработки +- Обеспечение стабильной работы редактирования различий и выполнения команд +- Сокращение точек трения, препятствующих регулярному использованию +- Гарантия бесперебойной работы на всех языках и платформах +- Расширение надежной поддержки для широкого спектра ИИ-провайдеров и моделей -## Настройка среды разработки +### Улучшенный пользовательский опыт -1. Форкните репозиторий -2. Клонируйте ваш форк: - ```bash - git clone https://github.com/YOUR_USERNAME/roo-code.git - ``` -3. Установите зависимости: - ```bash - npm install - ``` -4. Создайте новую ветку для ваших изменений: - ```bash - git checkout -b feature/your-feature-name - ``` +- Упрощение пользовательского интерфейса для большей ясности и интуитивности +- Постоянное совершенствование рабочего процесса для соответствия высоким ожиданиям разработчиков -## Написание и отправка кода +### Лидерство в производительности агентов -1. Следуйте существующему стилю кода -2. Добавляйте тесты для нового кода -3. Обновляйте документацию при необходимости -4. Убедитесь, что все тесты проходят -5. Создайте pull request с описанием ваших изменений +- Создание комплексных показателей оценки (evals) для измерения реальной продуктивности +- Упрощение запуска и интерпретации этих оценок для всех пользователей +- Внедрение улучшений, демонстрирующих явное повышение оценочных показателей -## Соглашение о сотрудничестве +Упоминай связь с этими направлениями в своих PR. -Отправляя pull request, вы соглашаетесь с тем, что ваш код будет распространяться под лицензией проекта. Все участники должны следовать нашему [Кодексу поведения](CODE_OF_CONDUCT.md). +### 3. Присоединяйся к сообществу Roo Code -## Получение помощи +- **Основной способ:** Присоединись к нашему [Discord](https://discord.gg/roocode) и отправь личное сообщение **Hannes Rudolph (`hrudolph`)**. +- **Альтернатива:** Опытные участники могут взаимодействовать напрямую через [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -Если у вас возникли вопросы или нужна помощь: +## Поиск и планирование вклада -1. Проверьте существующую документацию -2. Спросите в Discord сообществе -3. Создайте issue с меткой "question" +### Виды вклада -Еще раз спасибо за ваш интерес к улучшению Roo Code! +- **Исправление ошибок:** Решение проблем в коде. +- **Новые функции:** Добавление функциональности. +- **Документация:** Улучшение руководств и ясности. + +### Подход Issue-First + +Весь вклад должен начинаться с GitHub Issue. + +- **Проверь существующие issues:** Поищи в [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Создай issue:** Используй подходящие шаблоны: + - **Баги:** Шаблон "Bug Report". + - **Функции:** Шаблон "Detailed Feature Proposal". Требуется одобрение перед началом. +- **Заяви issue:** Оставь комментарий и дождись официального назначения. + +**PR без одобренных issue могут быть закрыты.** + +### Решение, над чем работать + +- Проверь [GitHub проект](https://github.com/orgs/RooVetGit/projects/1) на наличие незанятых "Good First Issues". +- Для документации посети [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). + +### Сообщение об ошибках + +- Сначала проверь существующие сообщения. +- Создай новые сообщения об ошибках, используя [шаблон "Bug Report"](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Уязвимости безопасности:** Сообщай приватно через [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). + +## Процесс разработки и отправки + +### Настройка среды разработки + +1. **Fork & Clone:** + +``` +git clone https://github.com/ТВОЙ_ПОЛЬЗОВАТЕЛЬ/Roo-Code.git +``` + +2. **Установка зависимостей:** + +``` +npm run install:all +``` + +3. **Отладка:** Открой в VS Code (`F5`). + +### Руководство по написанию кода + +- Один сфокусированный PR на функцию или исправление. +- Следуй лучшим практикам ESLint и TypeScript. +- Пиши ясные, описательные сообщения коммитов с ссылками на issues (например, `Fixes #123`). +- Обеспечь тщательное тестирование (`npm test`). +- Перебазируй на последнюю ветку `main` перед отправкой. + +### Отправка Pull Request + +- Начни с **черновика PR**, если ищешь ранний фидбек. +- Четко опиши свои изменения, следуя шаблону Pull Request. +- Предоставь скриншоты/видео для изменений UI. +- Укажи, нужны ли обновления документации. + +### Политика Pull Request + +- Должен ссылаться на предварительно одобренные и назначенные issue. +- PR, не соответствующие политике, могут быть закрыты. +- PR должны проходить CI-тесты, соответствовать дорожной карте и иметь четкую документацию. + +### Процесс проверки + +- **Ежедневный отбор:** Быстрые проверки мейнтейнерами. +- **Еженедельный глубокий обзор:** Комплексная оценка. +- **Быстро итерируй** на основе полученного фидбека. + +## Юридическая информация + +Отправляя pull request, ты соглашаешься, что твой вклад будет лицензирован под лицензией Apache 2.0, в соответствии с лицензией Roo Code. diff --git a/locales/ru/README.md b/locales/ru/README.md index d61b395731e..d25f9cd861e 100644 --- a/locales/ru/README.md +++ b/locales/ru/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../ca/README.md) • [Deutsch](../de/README.md) • [Español](../es/README.md) • [Français](../fr/README.md) • [हिन्दी](../hi/README.md) • [Italiano](../it/README.md) • Русский +[English](../../README.md) • [Català](../ca/README.md) • [Deutsch](../de/README.md) • [Español](../es/README.md) • [Français](../fr/README.md) • [हिन्दी](../hi/README.md) • [Italiano](../it/README.md) • [Nederlands](../nl/README.md) • Русский @@ -49,13 +49,13 @@ --- -## 🎉 Выпущен Roo Code 3.15 +## 🎉 Выпущен Roo Code 3.17 -Roo Code 3.15 приносит новые функции и улучшения на основе ваших отзывов! +Roo Code 3.17 предлагает мощные новые функции и улучшения на основе ваших отзывов! -- **Кэширование промптов для Vertex** - Vertex AI теперь поддерживает кэширование промптов, улучшая время отклика и снижая затраты на API. -- **Резервный механизм для терминала** - Реализован резервный механизм на случай сбоя интеграции оболочки терминала VSCode, обеспечивающий более надежную работу терминала. -- **Улучшенные фрагменты кода** - Улучшены отображение и взаимодействие с фрагментами кода в интерфейсе чата для лучшей читаемости и удобства использования. +- **Неявное кэширование для Gemini** - Вызовы API Gemini теперь автоматически кэшируются, снижая затраты на API. +- **Более умный выбор режима** - Определения режимов теперь могут включать рекомендации о том, когда следует использовать каждый режим, обеспечивая лучшую оркестровку. +- **Интеллектуальное сжатие контекста** - Интеллектуально резюмирует историю разговоров, когда контекст заполняется, вместо простого обрезания (включается в Настройки -> Экспериментальные). --- @@ -180,32 +180,36 @@ code --install-extension bin/roo-cline-.vsix Спасибо всем нашим участникам, которые помогли сделать Roo Code лучше! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Лицензия diff --git a/locales/tr/CODE_OF_CONDUCT.md b/locales/tr/CODE_OF_CONDUCT.md index 6476418f30a..678676a0b43 100644 --- a/locales/tr/CODE_OF_CONDUCT.md +++ b/locales/tr/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • Türkçe • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Katkıda Bulunan Sözleşmesi Davranış Kuralları ## Taahhüdümüz diff --git a/locales/tr/CONTRIBUTING.md b/locales/tr/CONTRIBUTING.md index 39ddb8b1b76..81bbd7f72b0 100644 --- a/locales/tr/CONTRIBUTING.md +++ b/locales/tr/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Roo Code'a Katkıda Bulunma - -Roo Code'a katkıda bulunmakla ilgilendiğiniz için çok mutluyuz. İster bir hatayı düzeltiyor, ister bir özellik ekliyor, ister belgelerimizi geliştiriyor olun, her katkı Roo Code'u daha akıllı hale getirir! Topluluğumuzu canlı ve misafirperver tutmak için tüm üyelerin [Davranış Kuralları](CODE_OF_CONDUCT.md)'na uyması gerekir. - -## Topluluğumuza Katılın - -Tüm katkıda bulunanları [Discord topluluğumuza](https://discord.gg/roocode) katılmaya şiddetle teşvik ediyoruz! Discord sunucumuzun bir parçası olmak size şu konularda yardımcı olur: - -- Katkılarınız hakkında gerçek zamanlı yardım ve rehberlik alın -- Diğer katkıda bulunanlar ve çekirdek ekip üyeleriyle bağlantı kurun -- Proje gelişmeleri ve öncelikleri hakkında güncel kalın -- Roo Code'un geleceğini şekillendiren tartışmalara katılın -- Diğer geliştiricilerle işbirliği fırsatları bulun +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -## Hataları veya Sorunları Bildirme +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • Türkçe • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -Hata raporları Roo Code'u herkes için daha iyi hale getirmeye yardımcı olur! Yeni bir sorun oluşturmadan önce, lütfen yinelemeleri önlemek için [mevcut olanları arayın](https://github.com/RooVetGit/Roo-Code/issues). Bir hatayı bildirmeye hazır olduğunuzda, ilgili bilgileri doldurmanıza yardımcı olacak bir şablon bulacağınız [sorunlar sayfamıza](https://github.com/RooVetGit/Roo-Code/issues/new/choose) gidin. - -
- 🔐 Önemli: Bir güvenlik açığı keşfederseniz, lütfen özel olarak bildirmek için Github güvenlik aracını kullanın. -
+# Roo Code'a Katkıda Bulunma -## Ne Üzerinde Çalışacağınıza Karar Verme +Roo Code, topluluk odaklı bir projedir ve her katkıyı çok önemsiyoruz. İşbirliğini kolaylaştırmak için [Issue-First](#issue-first-yaklaşımı) yaklaşımıyla çalışıyoruz; bu, tüm [Pull Request'lerin (PR'lar)](#pull-request-gönderme) önce bir GitHub Issue'ya bağlanması gerektiği anlamına gelir. Lütfen bu rehberi dikkatlice incele. -İyi bir ilk katkı mı arıyorsunuz? [Roo Code Sorunları](https://github.com/orgs/RooVetGit/projects/1) Github Projemizin "Issue [Unassigned]" bölümündeki sorunları kontrol edin. Bunlar özellikle yeni katkıda bulunanlar ve biraz yardıma ihtiyaç duyduğumuz alanlar için seçilmiştir! +## İçindekiler -[Belgelerimize](https://docs.roocode.com/) katkıları da memnuniyetle karşılıyoruz! İster yazım hatalarını düzeltmek, mevcut kılavuzları geliştirmek veya yeni eğitim içeriği oluşturmak olsun - herkesin Roo Code'dan en iyi şekilde yararlanmasına yardımcı olan topluluk odaklı bir kaynak deposu oluşturmak istiyoruz. Dosyayı düzenlemek için Github'daki doğru yere hızlıca gitmek için herhangi bir sayfada "Edit this page" düğmesine tıklayabilir veya doğrudan https://github.com/RooVetGit/Roo-Code-Docs adresine dalabilirsiniz. +- [Katkıdan Önce](#katkıdan-önce) +- [Katkı Bulma & Planlama](#katkı-bulma--planlama) +- [Geliştirme & Gönderim Süreci](#geliştirme--gönderim-süreci) +- [Yasal](#yasal) -Daha büyük bir özellik üzerinde çalışmayı planlıyorsanız, lütfen önce bir [özellik isteği](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) oluşturun, böylece Roo Code'un vizyonuyla uyumlu olup olmadığını tartışabiliriz. Ayrıca, fikrinizin stratejik yönümüze uyup uymadığını görmek için aşağıdaki [Proje Yol Haritası](#proje-yol-haritası)'nı kontrol edebilirsiniz. +## Katkıdan Önce -## Proje Yol Haritası +### 1. Davranış Kuralları -Roo Code, önceliklerimizi ve gelecekteki yönümüzü yönlendiren net bir geliştirme yol haritasına sahiptir. Yol haritamızı anlamak size şu konularda yardımcı olabilir: +Tüm katkı sağlayanlar [Davranış Kuralları](./CODE_OF_CONDUCT.md)'na uymalıdır. -- Katkılarınızı proje hedefleriyle uyumlu hale getirmek -- Uzmanlığınızın en değerli olacağı alanları belirlemek -- Belirli tasarım kararlarının arkasındaki bağlamı anlamak -- Vizyonumuzu destekleyen yeni özellikler için ilham bulmak +### 2. Proje Yol Haritası -Mevcut yol haritamız altı temel sütun üzerine odaklanmaktadır: +Yol haritamız projenin yönünü belirler. Katkılarını bu temel hedeflerle uyumlu hale getir: -### Sağlayıcı Desteği +### Güvenilirlik Öncelikli -Mümkün olduğunca çok sağlayıcıyı desteklemeyi hedefliyoruz: +- Diff düzenleme ve komut yürütme işlemlerinin sürekli olarak güvenilir olmasını sağlamak +- Düzenli kullanımı engelleyen sürtünme noktalarını azaltmak +- Tüm dillerde ve platformlarda sorunsuz çalışmayı garanti etmek +- Çok çeşitli yapay zeka sağlayıcıları ve modelleri için güçlü desteği genişletmek -- Daha çok yönlü "OpenAI Uyumlu" destek -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Ollama ve LM Studio için geliştirilmiş destek +### Geliştirilmiş Kullanıcı Deneyimi -### Model Desteği +- Daha fazla netlik ve sezgisellik için kullanıcı arayüzünü basitleştirmek +- Geliştiricilerin yüksek beklentilerini karşılamak üzere iş akışını sürekli iyileştirmek -Roo'nun yerel modeller de dahil olmak üzere mümkün olduğunca çok modelde iyi çalışmasını istiyoruz: +### Ajan Performansında Liderlik -- Özel sistem yönlendirmesi ve iş akışları aracılığıyla yerel model desteği -- Kıyaslama değerlendirmeleri ve test vakaları +- Gerçek dünyadaki üretkenliği ölçmek için kapsamlı değerlendirme kriterleri (evals) oluşturmak +- Herkesin bu değerlendirmeleri kolayca çalıştırıp yorumlamasını sağlamak +- Değerlendirme puanlarında net artışlar gösteren iyileştirmeler sunmak -### Sistem Desteği +PR'larında bu alanlarla olan bağlantıyı belirt. -Roo'nun herkesin bilgisayarında iyi çalışmasını istiyoruz: +### 3. Roo Code Topluluğuna Katıl -- Çapraz platform terminal entegrasyonu -- Mac, Windows ve Linux için güçlü ve tutarlı destek +- **Ana yöntem:** [Discord](https://discord.gg/roocode)'umuza katıl ve **Hannes Rudolph (`hrudolph`)**'a DM gönder. +- **Alternatif:** Deneyimli katkı sağlayanlar [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1) üzerinden doğrudan katılabilir. -### Dokümantasyon +## Katkı Bulma & Planlama -Tüm kullanıcılar ve katkıda bulunanlar için kapsamlı, erişilebilir dokümantasyon istiyoruz: +### Katkı Türleri -- Genişletilmiş kullanıcı kılavuzları ve öğreticiler -- Net API dokümantasyonu -- Katkıda bulunanlar için daha iyi rehberlik -- Çok dilli dokümantasyon kaynakları -- Etkileşimli örnekler ve kod örnekleri +- **Hata düzeltmeleri:** Koddaki sorunları çözmek. +- **Yeni özellikler:** Yeni işlevsellik eklemek. +- **Dokümantasyon:** Rehberleri geliştirmek ve netleştirmek. -### Kararlılık +### Issue-First Yaklaşımı -Hata sayısını önemli ölçüde azaltmak ve otomatik testleri artırmak istiyoruz: +Tüm katkılar bir GitHub Issue ile başlamalıdır. -- Hata ayıklama günlüğü anahtarı -- Hata/destek istekleriyle birlikte göndermek için "Makine/Görev Bilgisi" kopyalama düğmesi +- **Mevcut issue'ları kontrol et:** [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues)'da ara. +- **Issue oluştur:** Uygun şablonları kullan: + - **Hatalar:** "Bug Report" şablonu. + - **Özellikler:** "Detailed Feature Proposal" şablonu. Başlamadan önce onay gerekir. +- **Issue'ları sahiplen:** Yorum yap ve resmi atamayı bekle. -### Uluslararasılaştırma +**Onaylanmış issue'lara bağlı olmayan PR'lar kapatılabilir.** -Roo'nun herkesin dilini konuşmasını istiyoruz: +### Ne Üzerinde Çalışacağına Karar Verme -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +- [GitHub Projesi](https://github.com/orgs/RooVetGit/projects/1)'nde atanmamış "Good First Issues" bak. +- Dokümantasyon için [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs)'u ziyaret et. -Özellikle yol haritamızın hedeflerini ileriye taşıyan katkıları memnuniyetle karşılıyoruz. Bu sütunlarla uyumlu bir şey üzerinde çalışıyorsanız, lütfen PR açıklamanızda bundan bahsedin. +### Hata veya Sorun Bildirme -## Geliştirme Kurulumu +- Önce mevcut raporları kontrol et. +- ["Bug Report" şablonu](https://github.com/RooVetGit/Roo-Code/issues/new/choose) kullanarak yeni hata raporları oluştur. +- **Güvenlik açıkları:** [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new) aracılığıyla özel olarak bildir. -1. Depoyu **klonlayın**: +## Geliştirme & Gönderim Süreci -```sh -git clone https://github.com/RooVetGit/Roo-Code.git -``` +### Geliştirme Ortamı Kurulumu -2. **Bağımlılıkları yükleyin**: +1. **Fork & Clone:** -```sh -npm run install:all ``` - -3. **Webview'ı başlatın (HMR ile Vite/React uygulaması)**: - -```sh -npm run dev +git clone https://github.com/KULLANICI_ADIN/Roo-Code.git ``` -4. **Hata ayıklama**: - VSCode'da `F5` tuşuna basın (veya **Run** → **Start Debugging**) Roo Code yüklenmiş yeni bir oturum açmak için. - -Webview'daki değişiklikler anında görünecektir. Ana uzantıdaki değişiklikler uzantı ana bilgisayarının yeniden başlatılmasını gerektirecektir. - -Alternatif olarak, bir .vsix dosyası oluşturabilir ve doğrudan VSCode'a kurabilirsiniz: +2. **Bağımlılıkları yükle:** -```sh -npm run build ``` - -`bin/` dizininde bir `.vsix` dosyası görünecek ve şu komutla kurulabilir: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Kod Yazma ve Gönderme - -Herkes Roo Code'a kod katkısında bulunabilir, ancak katkılarınızın sorunsuz bir şekilde entegre edilebilmesi için bu kurallara uymanızı rica ediyoruz: - -1. **Pull Request'leri Odaklı Tutun** - - - PR'leri tek bir özellik veya hata düzeltmesiyle sınırlayın - - Daha büyük değişiklikleri daha küçük, ilgili PR'lere bölün - - Değişiklikleri bağımsız olarak incelenebilen mantıklı commitlere bölün - -2. **Kod Kalitesi** +3. **Hata ayıklama:** VS Code'da `F5` ile aç. - - Tüm PR'ler hem linting hem de formatlama içeren CI kontrollerini geçmelidir - - Göndermeden önce tüm ESLint uyarılarını veya hatalarını çözün - - Otomatik kod inceleme aracımız Ellipsis'ten gelen tüm geri bildirimlere yanıt verin - - TypeScript en iyi uygulamalarını takip edin ve tip güvenliğini koruyun +### Kod Yazma Rehberi -3. **Test Etme** +- Her özellik veya düzeltme için odaklı bir PR. +- ESLint ve TypeScript en iyi uygulamalarını takip et. +- Issue'lara referans veren açık, açıklayıcı commit mesajları yaz (örn. `Fixes #123`). +- Kapsamlı testler sağla (`npm test`). +- Göndermeden önce en son `main` branch'i üzerine rebase yap. - - Yeni özellikler için testler ekleyin - - Tüm testlerin geçtiğinden emin olmak için `npm test` çalıştırın - - Değişiklikleriniz etkiliyorsa mevcut testleri güncelleyin - - Uygun olduğunda hem birim testlerini hem de entegrasyon testlerini dahil edin +### Pull Request Gönderme -4. **Commit Yönergeleri** +- Erken geri bildirim istiyorsan **taslak PR** olarak başla. +- Pull Request Şablonunu takip ederek değişikliklerini açıkça tanımla. +- UI değişiklikleri için ekran görüntüleri/videolar sağla. +- Dokümantasyon güncellemeleri gerekip gerekmediğini belirt. - - Net, açıklayıcı commit mesajları yazın - - #issue-number kullanarak commitlerdeki ilgili sorunlara atıfta bulunun +### Pull Request Politikası -5. **Göndermeden Önce** +- Önceden onaylanmış ve atanmış issue'lara referans vermelidir. +- Politikaya uymayan PR'lar kapatılabilir. +- PR'lar CI testlerini geçmeli, yol haritasıyla uyumlu olmalı ve net dokümantasyona sahip olmalıdır. - - Dalınızı en son main üzerine rebase edin - - Dalınızın başarıyla oluşturulduğundan emin olun - - Tüm testlerin geçtiğini tekrar kontrol edin - - Değişikliklerinizi hata ayıklama kodu veya konsol günlükleri için gözden geçirin +### İnceleme Süreci -6. **Pull Request Açıklaması** - - Değişikliklerinizin ne yaptığını açıkça açıklayın - - Değişiklikleri test etmek için adımlar ekleyin - - Herhangi bir önemli değişikliği listeleyin - - UI değişiklikleri için ekran görüntüleri ekleyin +- **Günlük triyaj:** Maintainer'lar tarafından hızlı kontroller. +- **Haftalık detaylı inceleme:** Kapsamlı değerlendirme. +- **Geri bildirim temelinde hızla yinele.** -## Katkı Anlaşması +## Yasal -Bir pull request göndererek, katkılarınızın projeyle aynı lisans altında ([Apache 2.0](../LICENSE)) lisanslanacağını kabul edersiniz. +Pull request göndererek, katkılarının Roo Code'un lisanslamasıyla tutarlı olarak Apache 2.0 Lisansı altında lisanslanacağını kabul etmiş olursun. diff --git a/locales/tr/README.md b/locales/tr/README.md index 44b5f286187..2f9632356ae 100644 --- a/locales/tr/README.md +++ b/locales/tr/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ İster esnek bir kodlama ortağı, ister bir sistem mimarı, isterse QA mühendisi veya ürün yöneticisi gibi uzmanlaşmış roller arıyor olun, Roo Code yazılım geliştirme sürecinizi daha verimli hale getirmenize yardımcı olabilir. -Detaylı güncellemeler ve düzeltmeler için [CHANGELOG](../CHANGELOG.md) dosyasını kontrol edin. +Detaylı güncellemeler ve düzeltmeler için [CHANGELOG](../../CHANGELOG.md) dosyasını kontrol edin. --- -## 🎉 Roo Code 3.15 Yayınlandı +## 🎉 Roo Code 3.17 Yayınlandı -Roo Code 3.15 geri bildirimlerinize dayanarak yeni özellikler ve iyileştirmeler getiriyor! +Roo Code 3.17 geri bildirimlerinize dayanarak güçlü yeni özellikler ve iyileştirmeler getiriyor! -- **Vertex için Prompt Önbelleği** - Vertex AI artık prompt önbelleklemeyi destekliyor, yanıt sürelerini iyileştiriyor ve API maliyetlerini azaltıyor. -- **Terminal Yedek Mekanizması** - VSCode terminal kabuk entegrasyonu başarısız olduğunda devreye giren bir yedek mekanizma uygulandı, daha güvenilir terminal işlemleri sağlanıyor. -- **Geliştirilmiş Kod Parçacıkları** - Daha iyi okunabilirlik ve kullanılabilirlik için sohbet arayüzünde kod parçacıklarının görüntülenmesi ve etkileşimi geliştirildi. +- **Gemini için Örtük Önbelleğe Alma** - Gemini API çağrıları artık otomatik olarak önbelleğe alınarak API maliyetleri azaltılıyor. +- **Daha Akıllı Mod Seçimi** - Mod tanımları artık her modun ne zaman kullanılması gerektiği konusunda yönlendirme içerebiliyor, daha iyi orkestrasyon sağlıyor. +- **Akıllı Bağlam Yoğunlaştırma** - Bağlam dolduğunda, kırpmak yerine konuşma geçmişini akıllıca özetliyor (Ayarlar -> Deneysel bölümünden etkinleştirin). --- @@ -178,32 +178,36 @@ Topluluk katkılarını seviyoruz! [CONTRIBUTING.md](CONTRIBUTING.md) dosyasın Roo Code'u daha iyi hale getirmeye yardımcı olan tüm katkıda bulunanlara teşekkür ederiz! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Lisans diff --git a/locales/vi/CODE_OF_CONDUCT.md b/locales/vi/CODE_OF_CONDUCT.md index 78496b66c78..79e0094b9ea 100644 --- a/locales/vi/CODE_OF_CONDUCT.md +++ b/locales/vi/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • Tiếng Việt • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # Quy Tắc Ứng Xử theo Giao Ước Người Đóng Góp ## Cam Kết Của Chúng Tôi diff --git a/locales/vi/CONTRIBUTING.md b/locales/vi/CONTRIBUTING.md index 6f4c2c74709..d8f9374b091 100644 --- a/locales/vi/CONTRIBUTING.md +++ b/locales/vi/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# Đóng Góp cho Roo Code +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -Chúng tôi rất vui mừng vì bạn quan tâm đến việc đóng góp cho Roo Code. Cho dù bạn đang sửa lỗi, thêm tính năng, hay cải thiện tài liệu của chúng tôi, mỗi đóng góp đều làm cho Roo Code thông minh hơn! Để giữ cho cộng đồng của chúng tôi sôi động và thân thiện, tất cả thành viên phải tuân thủ [Quy Tắc Ứng Xử](CODE_OF_CONDUCT.md) của chúng tôi. +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • Tiếng Việt • [简体中文](../zh-CN/CONTRIBUTING.md) • [繁體中文](../zh-TW/CONTRIBUTING.md) -## Tham Gia Cộng Đồng của Chúng Tôi +# Đóng góp cho Roo Code -Chúng tôi mạnh mẽ khuyến khích tất cả người đóng góp tham gia [cộng đồng Discord](https://discord.gg/roocode) của chúng tôi! Việc là một phần của máy chủ Discord của chúng tôi giúp bạn: +Roo Code là một dự án do cộng đồng dẫn dắt và chúng mình rất trân trọng mọi đóng góp. Để đơn giản hóa quy trình hợp tác, chúng mình áp dụng cách tiếp cận [Issue-First](#cách-tiếp-cận-issue-first), nghĩa là tất cả [Pull Request (PR)](#gửi-pull-request) phải được liên kết với một GitHub Issue trước. Vui lòng đọc kỹ hướng dẫn này. -- Nhận hỗ trợ và hướng dẫn thời gian thực về đóng góp của bạn -- Kết nối với những người đóng góp khác và các thành viên nhóm cốt lõi -- Cập nhật về sự phát triển và ưu tiên của dự án -- Tham gia vào các cuộc thảo luận định hình tương lai của Roo Code -- Tìm cơ hội hợp tác với các nhà phát triển khác +## Mục lục -## Báo Cáo Lỗi hoặc Vấn Đề +- [Trước khi đóng góp](#trước-khi-đóng-góp) +- [Tìm kiếm & lên kế hoạch đóng góp](#tìm-kiếm--lên-kế-hoạch-đóng-góp) +- [Quy trình phát triển & gửi bài](#quy-trình-phát-triển--gửi-bài) +- [Pháp lý](#pháp-lý) -Báo cáo lỗi giúp cải thiện Roo Code cho mọi người! Trước khi tạo một vấn đề mới, vui lòng [tìm kiếm những vấn đề hiện có](https://github.com/RooVetGit/Roo-Code/issues) để tránh trùng lặp. Khi bạn đã sẵn sàng báo cáo lỗi, hãy truy cập [trang vấn đề](https://github.com/RooVetGit/Roo-Code/issues/new/choose) của chúng tôi, nơi bạn sẽ tìm thấy một mẫu để giúp bạn điền thông tin liên quan. +## Trước khi đóng góp -
- 🔐 Quan trọng: Nếu bạn phát hiện lỗ hổng bảo mật, vui lòng sử dụng công cụ bảo mật Github để báo cáo riêng tư. -
+### 1. Quy tắc ứng xử -## Quyết Định Làm Việc trên Cái Gì +Tất cả thành viên đóng góp phải tuân thủ [Quy tắc ứng xử](./CODE_OF_CONDUCT.md) của chúng mình. -Tìm kiếm đóng góp đầu tiên tốt? Kiểm tra các vấn đề trong phần "Issue [Unassigned]" của [Dự án Github Roo Code](https://github.com/orgs/RooVetGit/projects/1) của chúng tôi. Những vấn đề này được chọn lọc đặc biệt cho người đóng góp mới và các lĩnh vực mà chúng tôi muốn nhận được sự giúp đỡ! +### 2. Lộ trình phát triển dự án -Chúng tôi cũng hoan nghênh đóng góp cho [tài liệu](https://docs.roocode.com/) của chúng tôi! Dù là sửa lỗi chính tả, cải thiện hướng dẫn hiện có, hay tạo nội dung giáo dục mới - chúng tôi muốn xây dựng một kho tài nguyên do cộng đồng thúc đẩy giúp mọi người tận dụng tối đa Roo Code. Bạn có thể nhấp vào "Edit this page" trên bất kỳ trang nào để nhanh chóng đến đúng vị trí trong Github để chỉnh sửa tệp, hoặc bạn có thể đi trực tiếp vào https://github.com/RooVetGit/Roo-Code-Docs. +Lộ trình của chúng mình định hướng dự án. Hãy điều chỉnh đóng góp của bạn theo các mục tiêu chính: -Nếu bạn đang lên kế hoạch làm việc trên một tính năng lớn hơn, vui lòng tạo [yêu cầu tính năng](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) trước để chúng tôi có thể thảo luận xem nó có phù hợp với tầm nhìn của Roo Code không. Bạn cũng có thể kiểm tra [Lộ Trình Dự Án](#lộ-trình-dự-án) bên dưới để xem liệu ý tưởng của bạn có phù hợp với định hướng chiến lược của chúng tôi không. +### Độ tin cậy là ưu tiên hàng đầu -## Lộ Trình Dự Án +- Đảm bảo việc chỉnh sửa diff và thực thi lệnh luôn đáng tin cậy +- Giảm thiểu các điểm cản trở khiến người dùng ngại sử dụng thường xuyên +- Đảm bảo hoạt động mượt mà trên mọi ngôn ngữ và nền tảng +- Mở rộng hỗ trợ mạnh mẽ cho nhiều nhà cung cấp và mô hình AI đa dạng -Roo Code có một lộ trình phát triển rõ ràng hướng dẫn các ưu tiên và định hướng tương lai của chúng tôi. Hiểu lộ trình của chúng tôi có thể giúp bạn: +### Nâng cao trải nghiệm người dùng -- Điều chỉnh đóng góp của bạn với mục tiêu của dự án -- Xác định các lĩnh vực mà chuyên môn của bạn sẽ có giá trị nhất -- Hiểu bối cảnh đằng sau một số quyết định thiết kế -- Tìm cảm hứng cho các tính năng mới hỗ trợ tầm nhìn của chúng tôi +- Đơn giản hóa giao diện người dùng để tăng tính rõ ràng và trực quan +- Liên tục cải thiện quy trình làm việc để đáp ứng kỳ vọng cao của các nhà phát triển -Lộ trình hiện tại của chúng tôi tập trung vào sáu trụ cột chính: +### Dẫn đầu về hiệu suất agent -### Hỗ Trợ Nhà Cung Cấp +- Thiết lập các tiêu chuẩn đánh giá toàn diện (evals) để đo lường năng suất trong thực tế +- Giúp mọi người dễ dàng chạy và hiểu các đánh giá này +- Cung cấp các cải tiến thể hiện rõ sự tăng trưởng trong điểm đánh giá -Chúng tôi hướng đến việc hỗ trợ càng nhiều nhà cung cấp càng tốt: +Đề cập đến sự liên quan với các lĩnh vực này trong PR của bạn. -- Hỗ trợ "OpenAI Compatible" linh hoạt hơn -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- Hỗ trợ nâng cao cho Ollama và LM Studio +### 3. Tham gia cộng đồng Roo Code -### Hỗ Trợ Mô Hình +- **Cách chính:** Tham gia [Discord](https://discord.gg/roocode) của chúng mình và nhắn tin trực tiếp cho **Hannes Rudolph (`hrudolph`)**. +- **Cách thay thế:** Cộng tác viên có kinh nghiệm có thể tham gia trực tiếp qua [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1). -Chúng tôi muốn Roo hoạt động tốt trên càng nhiều mô hình càng tốt, bao gồm cả mô hình cục bộ: +## Tìm kiếm & lên kế hoạch đóng góp -- Hỗ trợ mô hình cục bộ thông qua prompting hệ thống tùy chỉnh và quy trình làm việc -- Đánh giá hiệu suất và các trường hợp thử nghiệm +### Các loại đóng góp -### Hỗ Trợ Hệ Thống +- **Sửa lỗi:** Khắc phục vấn đề trong mã nguồn. +- **Tính năng mới:** Thêm chức năng mới. +- **Tài liệu:** Cải thiện hướng dẫn và độ rõ ràng. -Chúng tôi muốn Roo chạy tốt trên máy tính của mọi người: +### Cách tiếp cận Issue-First -- Tích hợp terminal đa nền tảng -- Hỗ trợ mạnh mẽ và nhất quán cho Mac, Windows và Linux +Mọi đóng góp đều phải bắt đầu bằng một GitHub Issue. -### Tài Liệu +- **Kiểm tra issue hiện có:** Tìm kiếm trong [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues). +- **Tạo issue mới:** Sử dụng mẫu phù hợp: + - **Lỗi:** Mẫu "Bug Report". + - **Tính năng:** Mẫu "Detailed Feature Proposal". Cần được phê duyệt trước khi bắt đầu. +- **Nhận issue:** Bình luận và chờ được gán chính thức. -Chúng tôi muốn tài liệu toàn diện, dễ tiếp cận cho tất cả người dùng và người đóng góp: +**PR không có issue đã duyệt có thể bị đóng.** -- Hướng dẫn người dùng và hướng dẫn mở rộng -- Tài liệu API rõ ràng -- Hướng dẫn tốt hơn cho người đóng góp -- Tài nguyên tài liệu đa ngôn ngữ -- Ví dụ tương tác và mẫu mã +### Quyết định việc cần làm -### Ổn Định +- Xem [Dự án GitHub](https://github.com/orgs/RooVetGit/projects/1) để tìm "Good First Issues" chưa được gán. +- Về tài liệu, hãy xem [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs). -Chúng tôi muốn giảm đáng kể số lượng lỗi và tăng kiểm tra tự động: +### Báo cáo lỗi -- Công tắc ghi nhật ký gỡ lỗi -- Nút sao chép "Thông Tin Máy/Nhiệm Vụ" để gửi kèm với yêu cầu hỗ trợ/lỗi +- Kiểm tra báo cáo hiện có trước. +- Tạo báo cáo lỗi mới bằng [mẫu "Bug Report"](https://github.com/RooVetGit/Roo-Code/issues/new/choose). +- **Lỗ hổng bảo mật:** Báo cáo riêng qua [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new). -### Quốc Tế Hóa +## Quy trình phát triển & gửi bài -Chúng tôi muốn Roo nói ngôn ngữ của mọi người: +### Thiết lập môi trường phát triển -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +1. **Fork & Clone:** -Chúng tôi đặc biệt hoan nghênh những đóng góp thúc đẩy mục tiêu lộ trình của chúng tôi. Nếu bạn đang làm việc trên điều gì đó phù hợp với những trụ cột này, vui lòng đề cập đến điều đó trong mô tả PR của bạn. - -## Thiết Lập Phát Triển - -1. **Clone** kho lưu trữ: - -```sh -git clone https://github.com/RooVetGit/Roo-Code.git ``` - -2. **Cài đặt các phụ thuộc**: - -```sh -npm run install:all -``` - -3. **Khởi động webview (ứng dụng Vite/React với HMR)**: - -```sh -npm run dev +git clone https://github.com/TEN_TAI_KHOAN/Roo-Code.git ``` -4. **Gỡ lỗi**: - Nhấn `F5` (hoặc **Run** → **Start Debugging**) trong VSCode để mở phiên mới với Roo Code được tải. +2. **Cài đặt phụ thuộc:** -Các thay đổi đối với webview sẽ xuất hiện ngay lập tức. Các thay đổi đối với phần mở rộng cốt lõi sẽ yêu cầu khởi động lại máy chủ phần mở rộng. - -Hoặc bạn có thể xây dựng một tệp .vsix và cài đặt nó trực tiếp trong VSCode: - -```sh -npm run build ``` - -Một tệp `.vsix` sẽ xuất hiện trong thư mục `bin/` có thể được cài đặt bằng: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## Viết và Gửi Mã - -Bất kỳ ai cũng có thể đóng góp mã cho Roo Code, nhưng chúng tôi yêu cầu bạn tuân theo những hướng dẫn này để đảm bảo đóng góp của bạn có thể được tích hợp suôn sẻ: - -1. **Giữ Pull Request Tập Trung** - - - Giới hạn PR vào một tính năng hoặc sửa lỗi duy nhất - - Chia các thay đổi lớn hơn thành các PR nhỏ hơn, có liên quan - - Chia các thay đổi thành các commit hợp lý có thể được xem xét độc lập - -2. **Chất Lượng Mã** +3. **Debug:** Mở bằng VS Code (`F5`). - - Tất cả PR phải vượt qua kiểm tra CI bao gồm cả linting và định dạng - - Giải quyết mọi cảnh báo hoặc lỗi ESLint trước khi gửi - - Phản hồi tất cả phản hồi từ Ellipsis, công cụ đánh giá mã tự động của chúng tôi - - Tuân theo các thực hành tốt nhất của TypeScript và duy trì an toàn kiểu +### Hướng dẫn viết mã -3. **Kiểm Tra** +- Mỗi PR chỉ tập trung vào một tính năng hoặc sửa lỗi. +- Tuân thủ các thực hành tốt nhất của ESLint và TypeScript. +- Viết thông điệp commit rõ ràng, tham chiếu đến issue (ví dụ: `Fixes #123`). +- Cung cấp bài kiểm tra đầy đủ (`npm test`). +- Rebase trên nhánh `main` mới nhất trước khi gửi. - - Thêm kiểm tra cho các tính năng mới - - Chạy `npm test` để đảm bảo tất cả các kiểm tra đều vượt qua - - Cập nhật các bài kiểm tra hiện có nếu thay đổi của bạn ảnh hưởng đến chúng - - Bao gồm cả kiểm tra đơn vị và kiểm tra tích hợp khi thích hợp +### Gửi Pull Request -4. **Hướng Dẫn Commit** +- Bắt đầu với **PR nháp** nếu muốn nhận phản hồi sớm. +- Mô tả rõ ràng các thay đổi, tuân theo Mẫu Pull Request. +- Cung cấp ảnh chụp/video cho thay đổi UI. +- Chỉ rõ nếu cần cập nhật tài liệu. - - Viết thông điệp commit rõ ràng, mô tả - - Tham chiếu các vấn đề có liên quan trong commit bằng cách sử dụng #số-vấn-đề +### Chính sách Pull Request -5. **Trước Khi Gửi** +- Phải tham chiếu đến issue đã được phê duyệt và gán. +- PR không tuân thủ chính sách có thể bị đóng. +- PR cần vượt qua kiểm tra CI, phù hợp với lộ trình và có tài liệu rõ ràng. - - Rebase nhánh của bạn trên main mới nhất - - Đảm bảo nhánh của bạn xây dựng thành công - - Kiểm tra lại rằng tất cả các bài kiểm tra đều vượt qua - - Xem xét các thay đổi của bạn cho bất kỳ mã gỡ lỗi hoặc bản ghi console nào +### Quy trình đánh giá -6. **Mô Tả Pull Request** - - Mô tả rõ ràng những gì thay đổi của bạn làm - - Bao gồm các bước để kiểm tra các thay đổi - - Liệt kê bất kỳ thay đổi đáng kể nào - - Thêm ảnh chụp màn hình cho các thay đổi UI +- **Phân loại hàng ngày:** Kiểm tra nhanh bởi maintainer. +- **Đánh giá chi tiết hàng tuần:** Đánh giá toàn diện. +- **Lặp lại nhanh chóng** dựa trên phản hồi. -## Thỏa Thuận Đóng Góp +## Pháp lý -Bằng cách gửi một pull request, bạn đồng ý rằng đóng góp của bạn sẽ được cấp phép theo cùng giấy phép với dự án ([Apache 2.0](../LICENSE)). +Khi gửi pull request, bạn đồng ý rằng đóng góp của mình sẽ được cấp phép theo Giấy phép Apache 2.0, phù hợp với giấy phép của Roo Code. diff --git a/locales/vi/README.md b/locales/vi/README.md index 5a63bd65348..b9249666333 100644 --- a/locales/vi/README.md +++ b/locales/vi/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ Cho dù bạn đang tìm kiếm một đối tác lập trình linh hoạt, một kiến trúc sư hệ thống, hay các vai trò chuyên biệt như kỹ sư QA hoặc quản lý sản phẩm, Roo Code có thể giúp bạn xây dựng phần mềm hiệu quả hơn. -Kiểm tra [CHANGELOG](../CHANGELOG.md) để biết thông tin chi tiết về các cập nhật và sửa lỗi. +Kiểm tra [CHANGELOG](../../CHANGELOG.md) để biết thông tin chi tiết về các cập nhật và sửa lỗi. --- -## 🎉 Đã Phát Hành Roo Code 3.15 +## 🎉 Đã Phát Hành Roo Code 3.17 -Roo Code 3.15 mang đến những tính năng mới và cải tiến dựa trên phản hồi của bạn! +Roo Code 3.17 mang đến những tính năng mạnh mẽ mới và cải tiến dựa trên phản hồi của bạn! -- **Bộ nhớ đệm cho prompt trên Vertex** - Vertex AI giờ đây hỗ trợ bộ nhớ đệm prompt, cải thiện thời gian phản hồi và giảm chi phí API. -- **Cơ chế dự phòng cho Terminal** - Đã triển khai cơ chế dự phòng khi tích hợp shell terminal VSCode thất bại, đảm bảo hoạt động terminal đáng tin cậy hơn. -- **Cải thiện đoạn mã (code snippets)** - Nâng cao hiển thị và tương tác với đoạn mã trong giao diện trò chuyện để dễ đọc và sử dụng hơn. +- **Bộ nhớ đệm ngầm cho Gemini** - Các cuộc gọi API Gemini hiện được tự động lưu vào bộ nhớ đệm, giảm chi phí API. +- **Lựa chọn chế độ thông minh hơn** - Định nghĩa chế độ giờ đây có thể bao gồm hướng dẫn về thời điểm mỗi chế độ nên được sử dụng, cho phép điều phối tốt hơn. +- **Nén ngữ cảnh thông minh** - Tóm tắt thông minh lịch sử hội thoại khi ngữ cảnh đầy thay vì cắt bớt (bật trong Cài đặt -> Thử nghiệm). --- @@ -178,32 +178,36 @@ Chúng tôi rất hoan nghênh đóng góp từ cộng đồng! Bắt đầu b Cảm ơn tất cả những người đóng góp đã giúp cải thiện Roo Code! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## Giấy Phép diff --git a/locales/zh-CN/CODE_OF_CONDUCT.md b/locales/zh-CN/CODE_OF_CONDUCT.md index 411d38865df..d37b644535a 100644 --- a/locales/zh-CN/CODE_OF_CONDUCT.md +++ b/locales/zh-CN/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • 简体中文 • [繁體中文](../zh-TW/CODE_OF_CONDUCT.md) + # 贡献者契约行为准则 ## 我们的承诺 diff --git a/locales/zh-CN/CONTRIBUTING.md b/locales/zh-CN/CONTRIBUTING.md index b5c0429bd31..24e13fd9f22 100644 --- a/locales/zh-CN/CONTRIBUTING.md +++ b/locales/zh-CN/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# 为 Roo Code 做贡献 +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -我们很高兴您有兴趣为 Roo Code 做贡献。无论您是修复错误、添加功能,还是改进我们的文档,每一个贡献都让 Roo Code 变得更智能!为了保持我们的社区充满活力和欢迎,所有成员必须遵守我们的[行为准则](CODE_OF_CONDUCT.md)。 +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • 简体中文 • [繁體中文](../zh-TW/CONTRIBUTING.md) -## 加入我们的社区 +# 参与 Roo Code 贡献 -我们强烈鼓励所有贡献者加入我们的 [Discord 社区](https://discord.gg/roocode)!成为我们 Discord 服务器的一部分可以帮助您: +Roo Code 是一个由社区驱动的项目,我们高度重视每一份贡献。为了简化协作流程,我们采用 [Issue-First](#issue-first-方式) 原则,这意味着所有 [Pull Request (PR)](#提交-pull-request) 必须首先关联到 GitHub Issue。请仔细阅读本指南。 -- 获得关于您贡献的实时帮助和指导 -- 与其他贡献者和核心团队成员建立联系 -- 了解项目发展和优先事项的最新信息 -- 参与塑造 Roo Code 未来的讨论 -- 寻找与其他开发者的合作机会 +## 目录 -## 报告错误或问题 +- [贡献前须知](#贡献前须知) +- [寻找与规划你的贡献](#寻找与规划你的贡献) +- [开发与提交流程](#开发与提交流程) +- [法律声明](#法律声明) -错误报告有助于使 Roo Code 对每个人都更好!在创建新问题之前,请[搜索现有问题](https://github.com/RooVetGit/Roo-Code/issues)以避免重复。当您准备报告错误时,前往我们的[问题页面](https://github.com/RooVetGit/Roo-Code/issues/new/choose),那里有模板可以帮助您填写相关信息。 +## 贡献前须知 -
- 🔐 重要提示:如果您发现安全漏洞,请使用 Github 安全工具私下报告它。 -
+### 1. 行为准则 -## 决定做什么 +所有贡献者必须遵守我们的[行为准则](./CODE_OF_CONDUCT.md)。 -寻找一个好的首次贡献?查看我们 [Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1) Github 项目中"Issue [Unassigned]"部分的问题。这些是专门为新贡献者精心挑选的,也是我们希望得到一些帮助的领域! +### 2. 项目路线图 -我们也欢迎对我们的[文档](https://docs.roocode.com/)做贡献!无论是修复错别字、改进现有指南,还是创建新的教育内容 - 我们希望建立一个由社区驱动的资源库,帮助每个人充分利用 Roo Code。您可以点击任何页面上的"Edit this page"快速进入 Github 中编辑文件的正确位置,或者直接访问 https://github.com/RooVetGit/Roo-Code-Docs。 +我们的路线图指引项目方向。请将你的贡献与这些关键目标保持一致: -如果您计划处理更大的功能,请先创建一个[功能请求](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop),以便我们讨论它是否符合 Roo Code 的愿景。您还可以查看下面的[项目路线图](#项目路线图),看看您的想法是否符合我们的战略方向。 +### 可靠性优先 -## 项目路线图 +- 确保差异编辑和命令执行始终可靠 +- 减少阻碍常规使用的摩擦点 +- 确保在所有语言环境和平台上流畅运行 +- 扩展对各种 AI 提供商和模型的强大支持 -Roo Code 有一个明确的开发路线图,指导我们的优先事项和未来方向。了解我们的路线图可以帮助您: +### 增强用户体验 -- 使您的贡献与项目目标保持一致 -- 确定您的专业知识最有价值的领域 -- 理解某些设计决策背后的背景 -- 为支持我们愿景的新功能找到灵感 +- 简化用户界面,提高清晰度和直观性 +- 持续改进工作流程,满足开发者对日常工具的高期望 -我们当前的路线图专注于六个关键支柱: +### 引领代理性能 -### 提供商支持 +- 建立全面的评估基准(evals)衡量实际工作中的生产力 +- 让每个人都能轻松运行和解读这些评估 +- 提供明显提升评分的改进 -我们的目标是尽可能支持更多的提供商: +在 PR 中请提及与这些领域的关联。 -- 更加多功能的 "OpenAI Compatible" 支持 -- xAI, Microsoft Azure AI, Alibaba Cloud Qwen, IBM Watsonx, Together AI, DeepInfra, Fireworks AI, Cohere, Perplexity AI, FriendliAI, Replicate -- 增强对 Ollama 和 LM Studio 的支持 +### 3. 加入 Roo Code 社区 -### 模型支持 +- **主要方式:** 加入我们的 [Discord](https://discord.gg/roocode) 并私信 **Hannes Rudolph (`hrudolph`)**。 +- **替代方式:** 有经验的贡献者可通过 [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1) 直接参与。 -我们希望 Roo 在尽可能多的模型上运行良好,包括本地模型: +## 寻找与规划你的贡献 -- 通过自定义系统提示和工作流程支持本地模型 -- 基准评估和测试案例 +### 贡献类型 -### 系统支持 +- **Bug 修复:** 解决代码问题。 +- **新功能:** 添加新功能。 +- **文档:** 完善指南和提高清晰度。 -我们希望 Roo 在每个人的计算机上都能良好运行: +### Issue-First 方式 -- 跨平台终端集成 -- 对 Mac、Windows 和 Linux 的强大一致支持 +所有贡献必须从 GitHub Issue 开始。 -### 文档 +- **检查现有 issue:** 搜索 [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues)。 +- **创建 issue:** 使用适当模板: + - **Bug:** "Bug Report" 模板。 + - **功能:** "Detailed Feature Proposal" 模板。开始前需获得批准。 +- **认领 issue:** 评论并等待正式分配。 -我们希望为所有用户和贡献者提供全面、易于访问的文档: +**未关联已批准 issue 的 PR 可能会被关闭。** -- 扩展的用户指南和教程 -- 清晰的 API 文档 -- 更好的贡献者指导 -- 多语言文档资源 -- 交互式示例和代码示例 +### 决定要做什么 -### 稳定性 +- 查看 [GitHub 项目](https://github.com/orgs/RooVetGit/projects/1) 中未分配的 "Good First Issues"。 +- 文档相关,请访问 [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs)。 -我们希望显著减少错误数量并增加自动化测试: +### 报告 Bug -- 调试日志开关 -- 用于发送错误/支持请求的"机器/任务信息"复制按钮 +- 先检查是否已有相关报告。 +- 使用 ["Bug Report" 模板](https://github.com/RooVetGit/Roo-Code/issues/new/choose) 创建新 bug 报告。 +- **安全问题:** 通过 [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new) 私下报告。 -### 国际化 +## 开发与提交流程 -我们希望 Roo 能说每个人的语言: +### 开发环境配置 -- 我们希望 Roo Code 说每个人的语言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +1. **Fork & Clone:** -我们特别欢迎推进我们路线图目标的贡献。如果您正在处理符合这些支柱的内容,请在您的 PR 描述中提及。 - -## 开发设置 - -1. **克隆**仓库: - -```sh -git clone https://github.com/RooVetGit/Roo-Code.git ``` - -2. **安装依赖**: - -```sh -npm run install:all -``` - -3. **启动 webview(Vite/React 应用,具有热模块替换)**: - -```sh -npm run dev +git clone https://github.com/你的用户名/Roo-Code.git ``` -4. **调试**: - 在 VSCode 中按 `F5`(或**运行** → **开始调试**)打开一个加载了 Roo Code 的新会话。 +2. **安装依赖:** -对 webview 的更改将立即显示。对核心扩展的更改将需要重新启动扩展主机。 - -或者,您可以构建一个 .vsix 文件并直接在 VSCode 中安装: - -```sh -npm run build ``` - -`bin/` 目录中将出现一个 `.vsix` 文件,可以用以下命令安装: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## 编写和提交代码 - -任何人都可以为 Roo Code 贡献代码,但我们要求您遵循这些指导方针,以确保您的贡献能够顺利集成: - -1. **保持 Pull Requests 聚焦** - - - 将 PR 限制在单一功能或错误修复 - - 将较大的更改分割成更小的相关 PR - - 将更改分解为可以独立审查的逻辑提交 - -2. **代码质量** +3. **调试:** 在 VS Code 中按 `F5` 打开。 - - 所有 PR 必须通过包括 linting 和格式化的 CI 检查 - - 在提交之前解决任何 ESLint 警告或错误 - - 回应 Ellipsis(我们的自动代码审查工具)的所有反馈 - - 遵循 TypeScript 最佳实践并保持类型安全 +### 编码规范 -3. **测试** +- 每个 PR 专注于一个功能或修复。 +- 遵循 ESLint 和 TypeScript 最佳实践。 +- 编写清晰的提交信息,引用相关 issue(如 `Fixes #123`)。 +- 提供完整测试(`npm test`)。 +- 提交前先在最新 `main` 分支上进行 rebase。 - - 为新功能添加测试 - - 运行 `npm test` 确保所有测试通过 - - 如果您的更改影响现有测试,请更新它们 - - 在适当的情况下包括单元测试和集成测试 +### 提交 Pull Request -4. **提交指南** +- 如需早期反馈,可先提交**草稿 PR**。 +- 清晰描述你的更改,遵循 Pull Request 模板。 +- 为 UI 变更提供截图/视频。 +- 说明是否需要更新文档。 - - 编写清晰、描述性的提交消息 - - 在提交中使用 #issue-number 引用相关问题 +### Pull Request 政策 -5. **提交前** +- 必须引用已批准并分配的 issue。 +- 不遵守政策的 PR 可能会被关闭。 +- PR 应通过 CI 测试,符合路线图,并有清晰文档。 - - 在最新的 main 分支上变基您的分支 - - 确保您的分支成功构建 - - 再次检查所有测试是否通过 - - 检查您的更改中是否有任何调试代码或控制台日志 +### 审查流程 -6. **Pull Request 描述** - - 清晰描述您的更改做了什么 - - 包括测试更改的步骤 - - 列出任何破坏性更改 - - 为 UI 更改添加截图 +- **每日筛查:** 维护者快速检查。 +- **每周深入审查:** 全面评估。 +- **根据反馈快速迭代**。 -## 贡献协议 +## 法律声明 -通过提交 pull request,您同意您的贡献将在与项目相同的许可下获得许可([Apache 2.0](../LICENSE))。 +提交贡献即表示你同意你的贡献将基于 Apache 2.0 许可证,与 Roo Code 的许可一致。 diff --git a/locales/zh-CN/README.md b/locales/zh-CN/README.md index 98e29ac8102..deeb6e189d2 100644 --- a/locales/zh-CN/README.md +++ b/locales/zh-CN/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -43,17 +43,17 @@ 无论您是寻找灵活的编码伙伴、系统架构师,还是像 QA 工程师或产品经理这样的专业角色,Roo Code 都可以帮助您更高效地构建软件。 -查看 [CHANGELOG](../CHANGELOG.md) 获取详细更新和修复信息。 +查看 [CHANGELOG](../../CHANGELOG.md) 获取详细更新和修复信息。 --- -## 🎉 Roo Code 3.15 已发布 +## 🎉 Roo Code 3.17 已发布 -Roo Code 3.15 基于您的反馈带来新功能和改进! +Roo Code 3.17 基于您的反馈带来强大的新功能和改进! -- **Vertex 提示词缓存** - Vertex AI 现已支持提示词缓存,改善响应时间并降低 API 费用。 -- **终端回退机制** - 实现了 VSCode 终端 shell 集成失败时的回退机制,确保更可靠的终端操作。 -- **代码片段优化** - 增强了聊天界面中代码片段的渲染和交互,提高了可读性和易用性。 +- **Gemini 隐式缓存** - Gemini API 调用现在会自动缓存,降低 API 成本。 +- **更智能的模式选择** - 模式定义现在可以包含何时应使用各模式的指导,实现更好的编排。 +- **智能上下文压缩** - 当上下文填满时,智能摘要对话历史而非简单截断(在"设置 -> 实验性"中启用)。 --- @@ -178,32 +178,36 @@ code --install-extension bin/roo-cline-.vsix 感谢所有帮助改进 Roo Code 的贡献者! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## 许可证 diff --git a/locales/zh-TW/CODE_OF_CONDUCT.md b/locales/zh-TW/CODE_OF_CONDUCT.md index b83928b01b7..1a01171ad03 100644 --- a/locales/zh-TW/CODE_OF_CONDUCT.md +++ b/locales/zh-TW/CODE_OF_CONDUCT.md @@ -1,3 +1,7 @@ +[English](../../CODE_OF_CONDUCT.md) • [Català](../ca/CODE_OF_CONDUCT.md) • [Deutsch](../de/CODE_OF_CONDUCT.md) • [Español](../es/CODE_OF_CONDUCT.md) • [Français](../fr/CODE_OF_CONDUCT.md) • [हिंदी](../hi/CODE_OF_CONDUCT.md) • [Italiano](../it/CODE_OF_CONDUCT.md) • [Nederlands](../nl/CODE_OF_CONDUCT.md) • [Русский](../ru/CODE_OF_CONDUCT.md) + +[日本語](../ja/CODE_OF_CONDUCT.md) • [한국어](../ko/CODE_OF_CONDUCT.md) • [Polski](../pl/CODE_OF_CONDUCT.md) • [Português (BR)](../pt-BR/CODE_OF_CONDUCT.md) • [Türkçe](../tr/CODE_OF_CONDUCT.md) • [Tiếng Việt](../vi/CODE_OF_CONDUCT.md) • [简体中文](../zh-CN/CODE_OF_CONDUCT.md) • 繁體中文 + # 貢獻者公約行為準則 ## 我們的承諾 diff --git a/locales/zh-TW/CONTRIBUTING.md b/locales/zh-TW/CONTRIBUTING.md index d42ea003e5a..e676900abfe 100644 --- a/locales/zh-TW/CONTRIBUTING.md +++ b/locales/zh-TW/CONTRIBUTING.md @@ -1,173 +1,129 @@ -# 參與貢獻 Roo Code +[English](../../CONTRIBUTING.md) • [Català](../ca/CONTRIBUTING.md) • [Deutsch](../de/CONTRIBUTING.md) • [Español](../es/CONTRIBUTING.md) • [Français](../fr/CONTRIBUTING.md) • [हिंदी](../hi/CONTRIBUTING.md) • [Italiano](../it/CONTRIBUTING.md) • [Nederlands](../nl/CONTRIBUTING.md) • [Русский](../ru/CONTRIBUTING.md) -我們非常歡迎您參與貢獻 Roo Code。無論是修正錯誤、新增功能或改善文件,每一份貢獻都能讓 Roo Code 變得更加出色!為了維持社群的活力與友善氛圍,所有成員皆須遵守我們的[行為準則](CODE_OF_CONDUCT.md)。 +[日本語](../ja/CONTRIBUTING.md) • [한국어](../ko/CONTRIBUTING.md) • [Polski](../pl/CONTRIBUTING.md) • [Português (BR)](../pt-BR/CONTRIBUTING.md) • [Türkçe](../tr/CONTRIBUTING.md) • [Tiếng Việt](../vi/CONTRIBUTING.md) • [简体中文](../zh-CN/CONTRIBUTING.md) • 繁體中文 -## 加入我們的社群 +# 參與 Roo Code 貢獻 -我們強烈建議所有貢獻者加入我們的 [Discord 社群](https://discord.gg/roocode)!加入 Discord 伺服器後,您可以: +Roo Code 是一個由社群驅動的專案,我們深深重視每一份貢獻。為了簡化協作流程,我們採用 [Issue-First](#issue-first-方式) 原則,這表示所有 [Pull Request (PR)](#提交-pull-request) 必須先關聯至 GitHub Issue。請仔細閱讀本指南。 -- 即時取得貢獻相關的協助與指引 -- 與其他貢獻者及核心團隊成員交流 -- 掌握專案的最新進展與優先事項 -- 參與討論,共同塑造 Roo Code 的未來 -- 尋找與其他開發者合作的機會 +## 目錄 -## 回報錯誤或問題 +- [貢獻前須知](#貢獻前須知) +- [尋找與規劃你的貢獻](#尋找與規劃你的貢獻) +- [開發與提交流程](#開發與提交流程) +- [法律聲明](#法律聲明) -回報錯誤能幫助我們改善 Roo Code!在建立新議題前,請先[搜尋現有議題](https://github.com/RooVetGit/Roo-Code/issues),避免重複回報。當您準備好回報錯誤時,請前往我們的 [議題頁面](https://github.com/RooVetGit/Roo-Code/issues/new/choose),您將找到協助填寫相關資訊的範本。 +## 貢獻前須知 -
- 🔐 重要: 若您發現安全性漏洞,請透過 GitHub 安全性通報工具進行私密回報。 -
+### 1. 行為準則 -## 決定貢獻方向 +所有貢獻者必須遵守我們的[行為準則](./CODE_OF_CONDUCT.md)。 -正在尋找適合新手的貢獻機會嗎?請查看我們 [Roo Code Issues](https://github.com/orgs/RooVetGit/projects/1) GitHub 專案中的「Issue [Unassigned]」區塊。這些議題特別適合新進貢獻者,也是我們最需要協助的領域! +### 2. 專案藍圖 -我們也歡迎您對[文件](https://docs.roocode.com/)提出貢獻!無論是修正錯字、改善現有指南,或建立新的教學內容,我們都希望打造一個由社群推動的知識庫,協助每個人充分運用 Roo Code。您可以點選任何頁面上的「編輯此頁面」按鈕,快速前往 GitHub 上的檔案編輯介面,或直接造訪 https://github.com/RooVetGit/Roo-Code-Docs。 +我們的藍圖指引專案方向。請將你的貢獻與這些關鍵目標保持一致: -若您計畫開發較大型的功能,請先建立一個[功能請求](https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop),讓我們能討論該功能是否符合 Roo Code 的願景。您也可以參考下方的[專案藍圖](#專案藍圖),確認您的想法是否符合我們的策略方向。 +### 可靠性優先 -## 專案藍圖 +- 確保差異編輯和命令執行始終可靠 +- 減少阻礙常規使用的摩擦點 +- 確保在所有語言環境和平台上順暢運行 +- 擴展對各種 AI 供應商和模型的強大支援 -Roo Code 擁有明確的開發藍圖,指引我們的優先事項與未來方向。了解我們的藍圖能協助您: +### 增強使用者體驗 -- 讓您的貢獻與專案目標保持一致 -- 找到最能發揮您專長的領域 -- 理解特定設計決策的脈絡 -- 為支援我們願景的新功能尋找靈感 +- 簡化使用者介面,提高清晰度和直覺性 +- 持續改進工作流程,滿足開發者對日常工具的高期望 -目前的藍圖聚焦於六大核心支柱: +### 引領代理效能 -### 供應商支援 +- 建立全面的評估基準(evals)衡量實際工作中的生產力 +- 讓每個人都能輕鬆執行和解讀這些評估 +- 提供明顯提升評分的改進 -我們致力於完善各家供應商的支援: +在 PR 中請提及與這些領域的關聯。 -- 對於「OpenAI 相容」API 的更全面支援 -- xAI、Microsoft Azure AI、Alibaba Cloud Qwen、IBM Watsonx、Together AI、DeepInfra、Fireworks AI、Cohere、Perplexity AI、FriendliAI、Replicate -- 強化對 Ollama 與 LM Studio 的支援 +### 3. 加入 Roo Code 社群 -### 模型支援 +- **主要方式:** 加入我們的 [Discord](https://discord.gg/roocode) 並私訊 **Hannes Rudolph (`hrudolph`)**。 +- **替代方式:** 有經驗的貢獻者可透過 [GitHub Projects](https://github.com/orgs/RooVetGit/projects/1) 直接參與。 -我們希望 Roo 能在更多模型上順暢運作,包括本機模型: +## 尋找與規劃你的貢獻 -- 透過自訂系統提示與工作流程支援本機模型 -- 基準測試評估與測試案例 +### 貢獻類型 -### 系統支援 +- **Bug 修正:** 解決程式碼問題。 +- **新功能:** 新增功能。 +- **文件:** 完善指南和提高清晰度。 -我們希望 Roo 能在每個人的電腦上順暢運作: +### Issue-First 方式 -- 跨平台終端機整合 -- 為 Mac、Windows 與 Linux 提供穩定且一致的支援 +所有貢獻必須從 GitHub Issue 開始。 -### 文件 +- **檢查現有 issue:** 搜尋 [GitHub Issues](https://github.com/RooVetGit/Roo-Code/issues)。 +- **建立 issue:** 使用適當範本: + - **Bug:** 「Bug Report」範本。 + - **功能:** 「Detailed Feature Proposal」範本。開始前需獲得批准。 +- **認領 issue:** 留言並等待正式分配。 -我們希望為所有使用者與貢獻者提供完整且易於取得的文件: +**未關聯已批准 issue 的 PR 可能會被關閉。** -- 擴充使用者指南與教學 -- 清晰的 API 文件 -- 更完善的貢獻者指引 -- 多語言文件資源 -- 互動式範例與程式碼範例 +### 決定要做什麼 -### 穩定性 +- 查看 [GitHub 專案](https://github.com/orgs/RooVetGit/projects/1) 中未分配的「Good First Issues」。 +- 文件相關,請訪問 [Roo Code Docs](https://github.com/RooVetGit/Roo-Code-Docs)。 -我們希望顯著降低錯誤數量並增加自動化測試: +### 回報 Bug -- 除錯記錄開關 -- 用於傳送錯誤/支援請求的「機器/工作資訊」複製按鈕 +- 先檢查是否已有相關報告。 +- 使用 [「Bug Report」範本](https://github.com/RooVetGit/Roo-Code/issues/new/choose) 建立新 bug 報告。 +- **安全問題:** 透過 [security advisories](https://github.com/RooVetGit/Roo-Code/security/advisories/new) 私下回報。 -### 國際化 +## 開發與提交流程 -我們希望 Roo 能說每個人的語言: +### 開發環境設定 -- 我們希望 Roo Code 說每個人的語言 -- Queremos que Roo Code hable el idioma de todos -- हम चाहते हैं कि Roo Code हर किसी की भाषा बोले -- نريد أن يتحدث Roo Code لغة الجميع +1. **Fork & Clone:** -我們特別歡迎推動藍圖目標的貢獻。如果您的貢獻符合這些核心支柱,請在 PR 描述中提及。 - -## 開發環境設定 - -1. **複製**儲存庫: - -```sh -git clone https://github.com/RooVetGit/Roo-Code.git ``` - -2. **安裝相依套件**: - -```sh -npm run install:all -``` - -3. **啟動網頁檢視(Vite/React 應用程式,支援 HMR)**: - -```sh -npm run dev +git clone https://github.com/你的帳號/Roo-Code.git ``` -4. **除錯**: - 在 VSCode 中按下 `F5`(或選擇**執行** → **開始除錯**)以開啟載入 Roo Code 的新工作階段。 +2. **安裝相依套件:** -網頁檢視的變更會立即顯示。核心擴充功能的變更則需要重新啟動擴充主機。 - -或者,您也可以建置 .vsix 檔案並直接在 VSCode 中安裝: - -```sh -npm run build ``` - -建置完成後,`.vsix` 檔案會出現在 `bin/` 目錄中,可使用以下指令安裝: - -```sh -code --install-extension bin/roo-cline-.vsix +npm run install:all ``` -## 撰寫與提交程式碼 - -任何人都能為 Roo Code 貢獻程式碼,但請遵守以下準則,確保您的貢獻能順利整合: - -1. **保持 Pull Request 聚焦** - - - 每個 PR 限制在單一功能或錯誤修正 - - 將較大的變更拆分成較小且相關的 PR - - 將變更拆分成可獨立審查的邏輯提交 - -2. **程式碼品質** +3. **除錯:** 在 VS Code 中按 `F5` 開啟。 - - 所有 PR 必須通過包含程式碼檢查與格式化的 CI 檢查 - - 提交前解決所有 ESLint 警告或錯誤 - - 回應 Ellipsis(我們的自動化程式碼審查工具)的所有建議 - - 遵循 TypeScript 最佳實務並維持型別安全 +### 程式碼規範 -3. **測試** +- 每個 PR 專注於一個功能或修正。 +- 遵循 ESLint 和 TypeScript 最佳實踐。 +- 撰寫清晰的提交訊息,引用相關 issue(如 `Fixes #123`)。 +- 提供完整測試(`npm test`)。 +- 提交前先在最新 `main` 分支上進行 rebase。 - - 為新功能新增測試 - - 執行 `npm test` 確保所有測試通過 - - 如果變更影響現有測試,請更新測試 - - 在適當情況下包含單元測試和整合測試 +### 提交 Pull Request -4. **提交準則** +- 如需早期回饋,可先提交**草稿 PR**。 +- 清晰描述你的更改,遵循 Pull Request 範本。 +- 為 UI 變更提供截圖/影片。 +- 說明是否需要更新文件。 - - 撰寫清晰、具描述性的提交訊息 - - 使用 #issue-number 在提交中引用相關議題 +### Pull Request 政策 -5. **提交前** +- 必須引用已批准並分配的 issue。 +- 不遵守政策的 PR 可能會被關閉。 +- PR 應通過 CI 測試,符合藍圖,並有清晰文件。 - - 將您的分支重新基於最新的 main - - 確保您的分支能成功建置 - - 再次檢查所有測試是否通過 - - 檢查您的變更中是否有任何除錯程式碼或主控台記錄 +### 審查流程 -6. **PR 描述** - - 清楚描述您的變更內容 - - 包含測試變更的步驟 - - 列出任何重大變更 - - 為使用者介面變更附上截圖 +- **每日篩查:** 維護者快速檢查。 +- **每週深入審查:** 全面評估。 +- **根據回饋快速迭代**。 -## 貢獻協議 +## 法律聲明 -透過提交 Pull Request,您同意您的貢獻將依照與專案相同的授權條款([Apache 2.0](../LICENSE))進行授權。 +提交貢獻即表示你同意你的貢獻將基於 Apache 2.0 授權條款,與 Roo Code 的授權一致。 diff --git a/locales/zh-TW/README.md b/locales/zh-TW/README.md index 91151e07638..7ad8d5d1c1c 100644 --- a/locales/zh-TW/README.md +++ b/locales/zh-TW/README.md @@ -1,7 +1,7 @@
-[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Русский](../../locales/ru/README.md) +[English](../../README.md) • [Català](../../locales/ca/README.md) • [Deutsch](../../locales/de/README.md) • [Español](../../locales/es/README.md) • [Français](../../locales/fr/README.md) • [हिन्दी](../../locales/hi/README.md) • [Italiano](../../locales/it/README.md) • [Nederlands](../../locales/nl/README.md) • [Русский](../../locales/ru/README.md) @@ -44,17 +44,17 @@ 無論您需要的是一位靈活的程式設計夥伴、系統架構師,或是 QA 工程師、產品經理等特定角色,Roo Code 都能協助您更有效率地開發軟體。 -請檢視 [CHANGELOG](../CHANGELOG.md) 了解詳細的更新與修正內容。 +請檢視 [CHANGELOG](../../CHANGELOG.md) 了解詳細的更新與修正內容。 --- -## 🎉 Roo Code 3.15 已發布 +## 🎉 Roo Code 3.17 已發布 -Roo Code 3.15 根據您的回饋帶來新功能和改進! +Roo Code 3.17 根據您的回饋帶來強大的新功能和改進! -- **Vertex 提示詞快取** - Vertex AI 現已支援提示詞快取,改善回應時間並降低 API 成本。 -- **終端機備用機制** - 實作了 VSCode 終端機 shell 整合失敗時的備用機制,確保更可靠的終端機操作。 -- **程式碼片段優化** - 增強了聊天介面中程式碼片段的渲染和互動,提高了可讀性和易用性。 +- **Gemini 的隱式快取** - Gemini API 呼叫現在會自動快取,降低 API 成本。 +- **更智慧的模式選擇** - 模式定義現在可以包含何時應使用各模式的指引,實現更好的協調運作。 +- **智慧型上下文壓縮** - 當上下文填滿時,智慧地摘要對話歷史而非簡單截斷(在「設定 -> 實驗性」中啟用)。 --- @@ -179,32 +179,36 @@ code --install-extension bin/roo-cline-.vsix 感謝所有幫助改進 Roo Code 的貢獻者! -|mrubens
mrubens
|saoudrizwan
saoudrizwan
|cte
cte
|samhvw8
samhvw8
|daniel-lxs
daniel-lxs
|a8trejo
a8trejo
| -|:---:|:---:|:---:|:---:|:---:|:---:| -|ColemanRoo
ColemanRoo
|stea9499
stea9499
|joemanley201
joemanley201
|System233
System233
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
| -|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| -|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|wkordalski
wkordalski
|feifei325
feifei325
|lloydchang
lloydchang
|cannuri
cannuri
| -|vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|sachasayan
sachasayan
|qdaxb
qdaxb
|zhangtony239
zhangtony239
|lupuletic
lupuletic
| -|Premshay
Premshay
|psv2522
psv2522
|elianiva
elianiva
|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
| -|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|RaySinner
RaySinner
|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|dtrugman
dtrugman
| -|emshvac
emshvac
|kyle-apex
kyle-apex
|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|upamune
upamune
| -|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|anton-otee
anton-otee
| -|philfung
philfung
|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|eonghk
eonghk
|teddyOOXX
teddyOOXX
| -|vagadiya
vagadiya
|vincentsong
vincentsong
|yongjer
yongjer
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
| -|benzntech
benzntech
|axkirillov
axkirillov
|bramburn
bramburn
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
| -|Chenjiayuan195
Chenjiayuan195
|jr
jr
|julionav
julionav
|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
| -|nevermorec
nevermorec
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
|kohii
kohii
|kinandan
kinandan
| -|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
|hongzio
hongzio
|GOODBOY008
GOODBOY008
|dqroid
dqroid
| -|dlab-anton
dlab-anton
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|asychin
asychin
|PretzelVector
PretzelVector
| -|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|shariqriazz
shariqriazz
|seedlord
seedlord
| -|samir-nimbly
samir-nimbly
|ronyblum
ronyblum
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
| -|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|Jdo300
Jdo300
|hesara
hesara
| -|DeXtroTip
DeXtroTip
|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|dleen
dleen
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|QuinsZouls
QuinsZouls
| -|alarno
alarno
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
| -|vladstudio
vladstudio
|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|Sarke
Sarke
|kvokka
kvokka
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
| -|libertyteeth
libertyteeth
|shtse8
shtse8
| | | | | + +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| +| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| feifei325
feifei325
| +| zhangtony239
zhangtony239
| sachasayan
sachasayan
| lloydchang
lloydchang
| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| qdaxb
qdaxb
| +| lupuletic
lupuletic
| Premshay
Premshay
| psv2522
psv2522
| diarmidmackenzie
diarmidmackenzie
| aheizi
aheizi
| olweraltuve
olweraltuve
| +| jr
jr
| dtrugman
dtrugman
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| pugazhendhi-m
pugazhendhi-m
| +| afshawnlotfi
afshawnlotfi
| shariqriazz
shariqriazz
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| Lunchb0ne
Lunchb0ne
| +| xyOz-dev
xyOz-dev
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| +| gtaylor
gtaylor
| aitoroses
aitoroses
| benzntech
benzntech
| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| teddyOOXX
teddyOOXX
| vagadiya
vagadiya
| vincentsong
vincentsong
| yongjer
yongjer
| +| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| anton-otee
anton-otee
| axkirillov
axkirillov
| bramburn
bramburn
| +| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| SplittyDev
SplittyDev
| +| mdp
mdp
| napter
napter
| philfung
philfung
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| nevermorec
nevermorec
| +| hongzio
hongzio
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| SmartManoj
SmartManoj
| PretzelVector
PretzelVector
| +| zetaloop
zetaloop
| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| +| samir-nimbly
samir-nimbly
| ronyblum
ronyblum
| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| +| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| zxdvd
zxdvd
| +| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| +| dleen
dleen
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| vladstudio
vladstudio
| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| +| samsilveira
samsilveira
| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| Sarke
Sarke
| kvokka
kvokka
| ecmasx
ecmasx
| +| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| ksze
ksze
| +| Jdo300
Jdo300
| hesara
hesara
| | | | | + ## 授權 diff --git a/package-lock.json b/package-lock.json index 526bae528ac..2d36ceb4bdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "pearai-roo-cline", - "version": "3.15.3", + "version": "3.17.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pearai-roo-cline", - "version": "3.15.3", + "version": "3.17.2", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.37.0", "@anthropic-ai/vertex-sdk": "^0.7.0", "@aws-sdk/client-bedrock-runtime": "^3.779.0", - "@google/genai": "^0.9.0", + "@google/genai": "^0.13.0", "@mistralai/mistralai": "^1.3.6", - "@modelcontextprotocol/sdk": "^1.7.0", + "@modelcontextprotocol/sdk": "^1.9.0", "@pearai/core": "file:./../pearai-submodule/core", "@types/clone-deep": "^4.0.4", "@types/pdf-parse": "^1.1.4", @@ -53,6 +53,7 @@ "puppeteer-core": "^23.4.0", "react-tooltip": "^5.28.0", "reconnecting-eventsource": "^1.6.4", + "sanitize-filename": "^1.6.3", "say": "^0.16.0", "serialize-error": "^11.0.3", "simple-git": "^3.27.0", @@ -67,7 +68,7 @@ "vscode-material-icons": "^0.1.1", "web-tree-sitter": "^0.22.6", "workerpool": "^9.2.0", - "zod": "^3.23.8" + "zod": "^3.24.2" }, "devDependencies": { "@changesets/cli": "^2.27.10", @@ -87,8 +88,8 @@ "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.11.0", "@vscode/test-electron": "^2.5.2", - "@vscode/vsce": "^3.3.2", - "esbuild": "^0.24.0", + "@vscode/vsce": "3.3.2", + "esbuild": "^0.25.0", "eslint": "^8.57.0", "execa": "^9.5.2", "glob": "^11.0.1", @@ -99,13 +100,15 @@ "lint-staged": "^15.2.11", "mkdirp": "^3.0.1", "nock": "^14.0.4", - "npm-run-all": "^4.1.5", + "npm-run-all2": "^8.0.1", + "ovsx": "0.10.2", "prettier": "^3.4.2", "rimraf": "^6.0.1", "ts-jest": "^29.2.5", "tsup": "^8.4.0", "tsx": "^4.19.3", - "typescript": "^5.4.5", + "typescript": "5.8.3", + "vitest": "^3.1.3", "zod-to-ts": "^1.2.0" }, "engines": { @@ -449,26 +452,28 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime": { - "version": "3.779.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.779.0.tgz", - "integrity": "sha512-MyzZks8XxWwdsA4VlyPW4IekUjpgDI91VwMvEtOITHD8w+9nTGJtD32HcCKNQQCHWYfSoOj7yIoHRisVatR8yw==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.808.0.tgz", + "integrity": "sha512-OzjqAlevqurwAPiBGO++90pvpJCyjK6UrQH2av7oTwAwWYpY/wqVCGjch/pkme6G2+o76FjPvUKxfEcBu+5pKQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.775.0", - "@aws-sdk/credential-provider-node": "3.777.0", - "@aws-sdk/middleware-host-header": "3.775.0", - "@aws-sdk/middleware-logger": "3.775.0", - "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.775.0", - "@aws-sdk/region-config-resolver": "3.775.0", - "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.775.0", - "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.775.0", - "@smithy/config-resolver": "^4.1.0", - "@smithy/core": "^3.2.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/credential-provider-node": "3.808.0", + "@aws-sdk/eventstream-handler-node": "3.804.0", + "@aws-sdk/middleware-eventstream": "3.804.0", + "@aws-sdk/middleware-host-header": "3.804.0", + "@aws-sdk/middleware-logger": "3.804.0", + "@aws-sdk/middleware-recursion-detection": "3.804.0", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/region-config-resolver": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@aws-sdk/util-user-agent-browser": "3.804.0", + "@aws-sdk/util-user-agent-node": "3.808.0", + "@smithy/config-resolver": "^4.1.2", + "@smithy/core": "^3.3.1", "@smithy/eventstream-serde-browser": "^4.0.2", "@smithy/eventstream-serde-config-resolver": "^4.1.0", "@smithy/eventstream-serde-node": "^4.0.2", @@ -476,24 +481,24 @@ "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.0", - "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/middleware-retry": "^4.1.5", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.4", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.8", - "@smithy/util-defaults-mode-node": "^4.0.8", - "@smithy/util-endpoints": "^3.0.2", + "@smithy/util-defaults-mode-browser": "^4.0.12", + "@smithy/util-defaults-mode-node": "^4.0.12", + "@smithy/util-endpoints": "^3.0.4", "@smithy/util-middleware": "^4.0.2", - "@smithy/util-retry": "^4.0.2", + "@smithy/util-retry": "^4.0.3", "@smithy/util-stream": "^4.2.0", "@smithy/util-utf8": "^4.0.0", "@types/uuid": "^9.0.1", @@ -508,6 +513,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -521,16 +527,30 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, + "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -543,6 +563,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -552,47 +573,47 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/client-sso": { - "version": "3.777.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.777.0.tgz", - "integrity": "sha512-0+z6CiAYIQa7s6FJ+dpBYPi9zr9yY5jBg/4/FGcwYbmqWPXwL9Thdtr0FearYRZgKl7bhL3m3dILCCfWqr3teQ==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.808.0.tgz", + "integrity": "sha512-NxGomD0x9q30LPOXf4x7haOm6l2BJdLEzpiC/bPEXUkf2+4XudMQumMA/hDfErY5hCE19mFAouoO465m3Gl3JQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.775.0", - "@aws-sdk/middleware-host-header": "3.775.0", - "@aws-sdk/middleware-logger": "3.775.0", - "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.775.0", - "@aws-sdk/region-config-resolver": "3.775.0", - "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.775.0", - "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.775.0", - "@smithy/config-resolver": "^4.1.0", - "@smithy/core": "^3.2.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/middleware-host-header": "3.804.0", + "@aws-sdk/middleware-logger": "3.804.0", + "@aws-sdk/middleware-recursion-detection": "3.804.0", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/region-config-resolver": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@aws-sdk/util-user-agent-browser": "3.804.0", + "@aws-sdk/util-user-agent-node": "3.808.0", + "@smithy/config-resolver": "^4.1.2", + "@smithy/core": "^3.3.1", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.0", - "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/middleware-retry": "^4.1.5", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.4", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.8", - "@smithy/util-defaults-mode-node": "^4.0.8", - "@smithy/util-endpoints": "^3.0.2", + "@smithy/util-defaults-mode-browser": "^4.0.12", + "@smithy/util-defaults-mode-node": "^4.0.12", + "@smithy/util-endpoints": "^3.0.4", "@smithy/util-middleware": "^4.0.2", - "@smithy/util-retry": "^4.0.2", + "@smithy/util-retry": "^4.0.3", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -601,18 +622,18 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/core": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.775.0.tgz", - "integrity": "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.808.0.tgz", + "integrity": "sha512-+nTmxJVIPtAarGq9Fd/uU2qU/Ngfb9EntT0/kwXdKKMI0wU9fQNWi10xSTVeqOtzWERbQpOJgBAdta+v3W7cng==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", - "@smithy/core": "^3.2.0", - "@smithy/node-config-provider": "^4.0.2", + "@aws-sdk/types": "3.804.0", + "@smithy/core": "^3.3.1", + "@smithy/node-config-provider": "^4.1.1", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", - "@smithy/signature-v4": "^5.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/signature-v4": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", @@ -623,13 +644,13 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.775.0.tgz", - "integrity": "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.808.0.tgz", + "integrity": "sha512-snPRQnwG9PV4kYHQimo1tenf7P974RcdxkHUThzWSxPEV7HpjxTFYNWGlKbOKBhL4AcgeCVeiZ/j+zveF2lEPA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/types": "3.775.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -639,18 +660,18 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.775.0.tgz", - "integrity": "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.808.0.tgz", + "integrity": "sha512-gNXjlx3BIUeX7QpVqxbjBxG6zm45lC39QvUIo92WzEJd2OTPcR8TU0OTTsgq/lpn2FrKcISj5qXvhWykd41+CA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/types": "3.775.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.4", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" @@ -660,19 +681,19 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.777.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.777.0.tgz", - "integrity": "sha512-1X9mCuM9JSQPmQ+D2TODt4THy6aJWCNiURkmKmTIPRdno7EIKgAqrr/LLN++K5mBf54DZVKpqcJutXU2jwo01A==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.808.0.tgz", + "integrity": "sha512-Y53CW0pCvFQQEvtVFwExCCMbTg+6NOl8b3YOuZVzPmVmDoW7M1JIn9IScesqoGERXL3VoXny6nYTsZj+vfpp7Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/credential-provider-env": "3.775.0", - "@aws-sdk/credential-provider-http": "3.775.0", - "@aws-sdk/credential-provider-process": "3.775.0", - "@aws-sdk/credential-provider-sso": "3.777.0", - "@aws-sdk/credential-provider-web-identity": "3.777.0", - "@aws-sdk/nested-clients": "3.777.0", - "@aws-sdk/types": "3.775.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/credential-provider-env": "3.808.0", + "@aws-sdk/credential-provider-http": "3.808.0", + "@aws-sdk/credential-provider-process": "3.808.0", + "@aws-sdk/credential-provider-sso": "3.808.0", + "@aws-sdk/credential-provider-web-identity": "3.808.0", + "@aws-sdk/nested-clients": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", @@ -684,18 +705,18 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.777.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.777.0.tgz", - "integrity": "sha512-ZD66ywx1Q0KyUSuBXZIQzBe3Q7MzX8lNwsrCU43H3Fww+Y+HB3Ncws9grhSdNhKQNeGmZ+MgKybuZYaaeLwJEQ==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.808.0.tgz", + "integrity": "sha512-lASHlXJ6U5Cpnt9Gs+mWaaSmWcEibr1AFGhp+5UNvfyd+UU2Oiwgbo7rYXygmaVDGkbfXEiTkgYtoNOBSddnWQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.775.0", - "@aws-sdk/credential-provider-http": "3.775.0", - "@aws-sdk/credential-provider-ini": "3.777.0", - "@aws-sdk/credential-provider-process": "3.775.0", - "@aws-sdk/credential-provider-sso": "3.777.0", - "@aws-sdk/credential-provider-web-identity": "3.777.0", - "@aws-sdk/types": "3.775.0", + "@aws-sdk/credential-provider-env": "3.808.0", + "@aws-sdk/credential-provider-http": "3.808.0", + "@aws-sdk/credential-provider-ini": "3.808.0", + "@aws-sdk/credential-provider-process": "3.808.0", + "@aws-sdk/credential-provider-sso": "3.808.0", + "@aws-sdk/credential-provider-web-identity": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", @@ -707,13 +728,13 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.775.0.tgz", - "integrity": "sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.808.0.tgz", + "integrity": "sha512-ZLqp+xsQUatoo8pMozcfLwf/pwfXeIk0w3n0Lo/rWBgT3RcdECmmPCRcnkYBqxHQyE66aS9HiJezZUwMYPqh6w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/types": "3.775.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", @@ -724,15 +745,15 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.777.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.777.0.tgz", - "integrity": "sha512-9mPz7vk9uE4PBVprfINv4tlTkyq1OonNevx2DiXC1LY4mCUCNN3RdBwAY0BTLzj0uyc3k5KxFFNbn3/8ZDQP7w==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.808.0.tgz", + "integrity": "sha512-gWZByAokHX+aps1+syIW/hbKUBrjE2RpPRd/RGQvrBbVVgwsJzsHKsW0zy1B6mgARPG6IahmSUMjNkBCVsiAgw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.777.0", - "@aws-sdk/core": "3.775.0", - "@aws-sdk/token-providers": "3.777.0", - "@aws-sdk/types": "3.775.0", + "@aws-sdk/client-sso": "3.808.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/token-providers": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", @@ -743,14 +764,14 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.777.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.777.0.tgz", - "integrity": "sha512-uGCqr47fnthkqwq5luNl2dksgcpHHjSXz2jUra7TXtFOpqvnhOW8qXjoa1ivlkq8qhqlaZwCzPdbcN0lXpmLzQ==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.808.0.tgz", + "integrity": "sha512-SsGa1Gfa05aJM/qYOtHmfg0OKKW6Fl6kyMCcai63jWDVDYy0QSHcesnqRayJolISkdsVK6bqoWoFcPxiopcFcg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/nested-clients": "3.777.0", - "@aws-sdk/types": "3.775.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/nested-clients": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -760,12 +781,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.775.0.tgz", - "integrity": "sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.804.0.tgz", + "integrity": "sha512-bum1hLVBrn2lJCi423Z2fMUYtsbkGI2s4N+2RI2WSjvbaVyMSv/WcejIrjkqiiMR+2Y7m5exgoKeg4/TODLDPQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -775,12 +796,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-logger": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.775.0.tgz", - "integrity": "sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.804.0.tgz", + "integrity": "sha512-w/qLwL3iq0KOPQNat0Kb7sKndl9BtceigINwBU7SpkYWX9L/Lem6f8NPEKrC9Tl4wDBht3Yztub4oRTy/horJA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -789,12 +810,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.775.0.tgz", - "integrity": "sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.804.0.tgz", + "integrity": "sha512-zqHOrvLRdsUdN/ehYfZ9Tf8svhbiLLz5VaWUz22YndFv6m9qaAcijkpAOlKexsv3nLBMJdSdJ6GUTAeIy3BZzw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -804,15 +825,15 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.775.0.tgz", - "integrity": "sha512-7Lffpr1ptOEDE1ZYH1T78pheEY1YmeXWBfFt/amZ6AGsKSLG+JPXvof3ltporTGR2bhH/eJPo7UHCglIuXfzYg==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.808.0.tgz", + "integrity": "sha512-VckV6l5cf/rL3EtgzSHVTTD4mI0gd8UxDDWbKJsxbQ2bpNPDQG2L1wWGLaolTSzjEJ5f3ijDwQrNDbY9l85Mmg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.775.0", - "@smithy/core": "^3.2.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@smithy/core": "^3.3.1", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -822,13 +843,13 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.775.0.tgz", - "integrity": "sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.808.0.tgz", + "integrity": "sha512-9x2QWfphkARZY5OGkl9dJxZlSlYM2l5inFeo2bKntGuwg4A4YUe5h7d5yJ6sZbam9h43eBrkOdumx03DAkQF9A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", - "@smithy/node-config-provider": "^4.0.2", + "@aws-sdk/types": "3.804.0", + "@smithy/node-config-provider": "^4.1.1", "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.2", @@ -839,13 +860,13 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/token-providers": { - "version": "3.777.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.777.0.tgz", - "integrity": "sha512-Yc2cDONsHOa4dTSGOev6Ng2QgTKQUEjaUnsyKd13pc/nLLz/WLqHiQ/o7PcnKERJxXGs1g1C6l3sNXiX+kbnFQ==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.808.0.tgz", + "integrity": "sha512-PsfKanHmnyO7FxowXqxbLQ+QjURCdSGxyhUiSdZbfvlvme/wqaMyIoMV/i4jppndksoSdPbW2kZXjzOqhQF+ew==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/nested-clients": "3.777.0", - "@aws-sdk/types": "3.775.0", + "@aws-sdk/nested-clients": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", @@ -856,9 +877,9 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/types": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", - "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.804.0.tgz", + "integrity": "sha512-A9qnsy9zQ8G89vrPPlNG9d1d8QcKRGqJKqwyGgS0dclJpwy6d1EWgQLIolKPl6vcFpLoe6avLOLxr+h8ur5wpg==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.2.0", @@ -869,14 +890,14 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-endpoints": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.775.0.tgz", - "integrity": "sha512-yjWmUgZC9tUxAo8Uaplqmq0eUh0zrbZJdwxGRKdYxfm4RG6fMw1tj52+KkatH7o+mNZvg1GDcVp/INktxonJLw==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.808.0.tgz", + "integrity": "sha512-N6Lic98uc4ADB7fLWlzx+1uVnq04VgVjngZvwHoujcRg9YDhIg9dUDiTzD5VZv13g1BrPYmvYP1HhsildpGV6w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/types": "^4.2.0", - "@smithy/util-endpoints": "^3.0.2", + "@smithy/util-endpoints": "^3.0.4", "tslib": "^2.6.2" }, "engines": { @@ -884,26 +905,26 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.775.0.tgz", - "integrity": "sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.804.0.tgz", + "integrity": "sha512-KfW6T6nQHHM/vZBBdGn6fMyG/MgX5lq82TDdX4HRQRRuHKLgBWGpKXqqvBwqIaCdXwWHgDrg2VQups6GqOWW2A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.775.0.tgz", - "integrity": "sha512-N9yhTevbizTOMo3drH7Eoy6OkJ3iVPxhV7dwb6CMAObbLneS36CSfA6xQXupmHWcRvZPTz8rd1JGG3HzFOau+g==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.808.0.tgz", + "integrity": "sha512-5UmB6u7RBSinXZAVP2iDgqyeVA/odO2SLEcrXaeTCw8ICXEoqF0K+GL36T4iDbzCBOAIugOZ6OcQX5vH3ck5UA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.775.0", - "@aws-sdk/types": "3.775.0", - "@smithy/node-config-provider": "^4.0.2", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/node-config-provider": "^4.1.1", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -933,12 +954,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/config-resolver": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.0.tgz", - "integrity": "sha512-8smPlwhga22pwl23fM5ew4T9vfLUCeFXlcqNOCD5M5h8VmNPNUE9j6bQSuRXpDSV11L/E/SwEBQuW8hr6+nS1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.2.tgz", + "integrity": "sha512-7r6mZGwb5LmLJ+zPtkLoznf2EtwEuSWdtid10pjGl/7HefCE4mueOkrfki8JCUm99W6UfP47/r3tbxx9CfBN5A==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.2", @@ -949,12 +970,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.2.0.tgz", - "integrity": "sha512-k17bgQhVZ7YmUvA8at4af1TDpl0NDMBuBKJl8Yg0nrefwmValU+CnA5l/AriVdQNthU/33H3nK71HrLgqOPr1Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.3.2.tgz", + "integrity": "sha512-GlLv+syoWolhtjX12XplL9BXBu10cjjD8iQC69fiKTrVNOB3Fjt8CVI9ccm6G3bLbMNe1gzrLD7yyMkYo4hchw==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-serde": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "@smithy/util-body-length-browser": "^4.0.0", @@ -968,12 +989,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/credential-provider-imds": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.2.tgz", - "integrity": "sha512-32lVig6jCaWBHnY+OEQ6e6Vnt5vDHaLiydGrwYMW9tPqO688hPGTYRamYJ1EptxEC2rAwJrHWmPoKRBl4iTa8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.4.tgz", + "integrity": "sha512-jN6M6zaGVyB8FmNGG+xOPQB4N89M1x97MMdMnm1ESjljLS3Qju/IegQizKujaNcy2vXAvrz0en8bobe6E55FEA==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", @@ -1042,14 +1063,15 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/middleware-content-length": { @@ -1067,14 +1089,14 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/middleware-endpoint": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.0.tgz", - "integrity": "sha512-xhLimgNCbCzsUppRTGXWkZywksuTThxaIB0HwbpsVLY5sceac4e1TZ/WKYqufQLaUy+gUSJGNdwD2jo3cXL0iA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.5.tgz", + "integrity": "sha512-WlpC9KVkajQf7RaGwi3n6lhHZzYTgm2PyX/2JjcwSHG417gFloNmYqN8rzDRXjT527/ZxZuvCsqq1gWZPW8lag==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.2.0", - "@smithy/middleware-serde": "^4.0.3", - "@smithy/node-config-provider": "^4.0.2", + "@smithy/core": "^3.3.2", + "@smithy/middleware-serde": "^4.0.4", + "@smithy/node-config-provider": "^4.1.1", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", @@ -1086,18 +1108,18 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/middleware-retry": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.0.tgz", - "integrity": "sha512-2zAagd1s6hAaI/ap6SXi5T3dDwBOczOMCSkkYzktqN1+tzbk1GAsHNAdo/1uzxz3Ky02jvZQwbi/vmDA6z4Oyg==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.6.tgz", + "integrity": "sha512-bl8q95nvCf7d22spxsBfs2giUDFf7prWLAxF5tmfgGBYHbUNW+OfnwMnabC15GMLA2AoE4HOtQR18a59lx6Blw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/protocol-http": "^5.1.0", - "@smithy/service-error-classification": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/service-error-classification": "^4.0.3", + "@smithy/smithy-client": "^4.2.5", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", - "@smithy/util-retry": "^4.0.2", + "@smithy/util-retry": "^4.0.3", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -1106,11 +1128,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/middleware-serde": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.3.tgz", - "integrity": "sha512-rfgDVrgLEVMmMn0BI8O+8OVr6vXzjV7HZj57l0QxslhzbvVfikZbVfBVthjLHqib4BW44QhcIgJpvebHlRaC9A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.4.tgz", + "integrity": "sha512-CaLvBtz+Xgs7eOwoinTXhZ02/9u8b28RT8lQAaDh7Q59nygeYYp1UiJjwl6zsay+lp0qVT/S7qLVI5RgcxjyfQ==", "license": "Apache-2.0", "dependencies": { + "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -1132,9 +1155,9 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/node-config-provider": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.2.tgz", - "integrity": "sha512-WgCkILRZfJwJ4Da92a6t3ozN/zcvYyJGUTmfGbgS/FkCcoCjl7G4FJaCDN1ySdvLvemnQeo25FdkyMSTSwulsw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.1.tgz", + "integrity": "sha512-1slS5jf5icHETwl5hxEVBj+mh6B+LbVW4yRINsGtUKH+nxM5Pw2H59+qf+JqYFCHp9jssG4vX81f5WKnjMN3Vw==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.2", @@ -1216,9 +1239,9 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/service-error-classification": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.2.tgz", - "integrity": "sha512-LA86xeFpTKn270Hbkixqs5n73S+LVM0/VZco8dqd+JT75Dyx3Lcw/MraL7ybjmz786+160K8rPOmhsq0SocoJQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.3.tgz", + "integrity": "sha512-FTbcajmltovWMjj3tksDQdD23b2w6gH+A0DYA1Yz3iSpjDj8fmkwy62UnXcWMy4d5YoMoSyLFHMfkEVEzbiN8Q==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.2.0" @@ -1241,9 +1264,9 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/signature-v4": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.2.tgz", - "integrity": "sha512-Mz+mc7okA73Lyz8zQKJNyr7lIcHLiPYp0+oiqiMNc/t7/Kf2BENs5d63pEj7oPqdjaum6g0Fc8wC78dY1TgtXw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.0.tgz", + "integrity": "sha512-4t5WX60sL3zGJF/CtZsUQTs3UrZEDO2P7pEaElrekbLqkWPYkgqNW1oeiNYC6xXifBnT9dVBOnNQRvOE9riU9w==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.0.0", @@ -1259,26 +1282,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/signature-v4/node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/smithy-client": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.0.tgz", - "integrity": "sha512-Qs65/w30pWV7LSFAez9DKy0Koaoh3iHhpcpCCJ4waj/iqwsuSzJna2+vYwq46yBaqO5ZbP9TjUsATUNxrKeBdw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.5.tgz", + "integrity": "sha512-T3gA/TShe52Ln0ywWGVoDiqRvaxqvrU0CKRRmzT71/I1rRBD8mY85rvMMME6vw5RpBLJC9ADmXSLmpohF7RRhA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.2.0", - "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/core": "^3.3.2", + "@smithy/middleware-endpoint": "^4.1.5", "@smithy/middleware-stack": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", @@ -1366,18 +1377,6 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/util-buffer-from/node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/util-config-provider": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", @@ -1391,13 +1390,13 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.8.tgz", - "integrity": "sha512-ZTypzBra+lI/LfTYZeop9UjoJhhGRTg3pxrNpfSTQLd3AJ37r2z4AXTKpq1rFXiiUIJsYyFgNJdjWRGP/cbBaQ==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.13.tgz", + "integrity": "sha512-HCLfXAyTEpVWLuyxDABg8UQukeRwChL1UErpSQ4KJK2ZoadmXuQY68pTL9KcuEtasTkIjnzyLUL9vhLdJ3VFHQ==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.5", "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -1407,16 +1406,16 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.8.tgz", - "integrity": "sha512-Rgk0Jc/UDfRTzVthye/k2dDsz5Xxs9LZaKCNPgJTRyoyBoeiNCnHsYGOyu1PKN+sDyPnJzMOz22JbwxzBp9NNA==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.13.tgz", + "integrity": "sha512-lu8E2RyzKzzFbNu4ICmY/2HltMZlJxMNg3saJ+r8I9vWbWbwdX7GOWUJdP4fbjEOm6aa52mnnd+uIRrT3dNEyA==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.1.0", - "@smithy/credential-provider-imds": "^4.0.2", - "@smithy/node-config-provider": "^4.0.2", + "@smithy/config-resolver": "^4.1.2", + "@smithy/credential-provider-imds": "^4.0.4", + "@smithy/node-config-provider": "^4.1.1", "@smithy/property-provider": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.5", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -1425,12 +1424,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/util-endpoints": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.2.tgz", - "integrity": "sha512-6QSutU5ZyrpNbnd51zRTL7goojlcnuOB55+F9VBD+j8JpRY50IGamsjlycrmpn8PQkmJucFW8A0LSfXj7jjtLQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.4.tgz", + "integrity": "sha512-VfFATC1bmZLV2858B/O1NpMcL32wYo8DPPhHxYxDCodDl3f3mSZ5oJheW1IF91A0EeAADz2WsakM/hGGPGNKLg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -1464,12 +1463,12 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/util-retry": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.2.tgz", - "integrity": "sha512-Qryc+QG+7BCpvjloFLQrmlSd0RsVRHejRXd78jNO3+oREueCjwG1CCEH1vduw/ZkM1U9TztwIKVIi3+8MJScGg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.3.tgz", + "integrity": "sha512-DPuYjZQDXmKr/sNvy9Spu8R/ESa2e22wXZzSAY6NkjOLj6spbIje/Aq8rT97iUMdDj0qHMRIe+bTxvlU74d9Ng==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.0.2", + "@smithy/service-error-classification": "^4.0.3", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -1546,7 +1545,8 @@ "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/@aws-sdk/client-cognito-identity": { "version": "3.699.0", @@ -2872,6 +2872,111 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.804.0.tgz", + "integrity": "sha512-LZddQVBUCB86tZtLJRhqiDyIqr4hfRxZCcUp1fZSfpBMcf419lgcFRGWMR3J/kCWHQ0G05aor7fSeoeaxskuNQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@smithy/eventstream-codec": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node/node_modules/@aws-sdk/types": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.804.0.tgz", + "integrity": "sha512-A9qnsy9zQ8G89vrPPlNG9d1d8QcKRGqJKqwyGgS0dclJpwy6d1EWgQLIolKPl6vcFpLoe6avLOLxr+h8ur5wpg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node/node_modules/@smithy/types": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.2.0.tgz", + "integrity": "sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.804.0.tgz", + "integrity": "sha512-3lPxZshOJoKSxIMUq8FCiIre+FZ1g/t+O7DHwOMB6EuzJ8lp5QyUeh1wE5iD/gB8VhWZoj90rGIaWCmT8ccEuA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream/node_modules/@aws-sdk/types": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.804.0.tgz", + "integrity": "sha512-A9qnsy9zQ8G89vrPPlNG9d1d8QcKRGqJKqwyGgS0dclJpwy6d1EWgQLIolKPl6vcFpLoe6avLOLxr+h8ur5wpg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream/node_modules/@smithy/protocol-http": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.0.tgz", + "integrity": "sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream/node_modules/@smithy/types": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.2.0.tgz", + "integrity": "sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.696.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.696.0.tgz", @@ -3031,47 +3136,47 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.777.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.777.0.tgz", - "integrity": "sha512-bmmVRsCjuYlStYPt06hr+f8iEyWg7+AklKCA8ZLDEJujXhXIowgUIqXmqpTkXwkVvDQ9tzU7hxaONjyaQCGybA==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.808.0.tgz", + "integrity": "sha512-NparPojwoBul7XPCasy4psFMJbw7Ys4bz8lVB93ljEUD4VV7mM7zwK27Uhz20B8mBFGmFEoAprPsVymJcK9Vcw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.775.0", - "@aws-sdk/middleware-host-header": "3.775.0", - "@aws-sdk/middleware-logger": "3.775.0", - "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.775.0", - "@aws-sdk/region-config-resolver": "3.775.0", - "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.775.0", - "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.775.0", - "@smithy/config-resolver": "^4.1.0", - "@smithy/core": "^3.2.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/middleware-host-header": "3.804.0", + "@aws-sdk/middleware-logger": "3.804.0", + "@aws-sdk/middleware-recursion-detection": "3.804.0", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/region-config-resolver": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@aws-sdk/util-user-agent-browser": "3.804.0", + "@aws-sdk/util-user-agent-node": "3.808.0", + "@smithy/config-resolver": "^4.1.2", + "@smithy/core": "^3.3.1", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.0", - "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/middleware-retry": "^4.1.5", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.4", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.8", - "@smithy/util-defaults-mode-node": "^4.0.8", - "@smithy/util-endpoints": "^3.0.2", + "@smithy/util-defaults-mode-browser": "^4.0.12", + "@smithy/util-defaults-mode-node": "^4.0.12", + "@smithy/util-endpoints": "^3.0.4", "@smithy/util-middleware": "^4.0.2", - "@smithy/util-retry": "^4.0.2", + "@smithy/util-retry": "^4.0.3", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -3143,18 +3248,18 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/core": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.775.0.tgz", - "integrity": "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.808.0.tgz", + "integrity": "sha512-+nTmxJVIPtAarGq9Fd/uU2qU/Ngfb9EntT0/kwXdKKMI0wU9fQNWi10xSTVeqOtzWERbQpOJgBAdta+v3W7cng==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", - "@smithy/core": "^3.2.0", - "@smithy/node-config-provider": "^4.0.2", + "@aws-sdk/types": "3.804.0", + "@smithy/core": "^3.3.1", + "@smithy/node-config-provider": "^4.1.1", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", - "@smithy/signature-v4": "^5.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/signature-v4": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", @@ -3165,12 +3270,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.775.0.tgz", - "integrity": "sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.804.0.tgz", + "integrity": "sha512-bum1hLVBrn2lJCi423Z2fMUYtsbkGI2s4N+2RI2WSjvbaVyMSv/WcejIrjkqiiMR+2Y7m5exgoKeg4/TODLDPQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -3180,12 +3285,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-logger": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.775.0.tgz", - "integrity": "sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.804.0.tgz", + "integrity": "sha512-w/qLwL3iq0KOPQNat0Kb7sKndl9BtceigINwBU7SpkYWX9L/Lem6f8NPEKrC9Tl4wDBht3Yztub4oRTy/horJA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -3194,12 +3299,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.775.0.tgz", - "integrity": "sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.804.0.tgz", + "integrity": "sha512-zqHOrvLRdsUdN/ehYfZ9Tf8svhbiLLz5VaWUz22YndFv6m9qaAcijkpAOlKexsv3nLBMJdSdJ6GUTAeIy3BZzw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -3209,15 +3314,15 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.775.0.tgz", - "integrity": "sha512-7Lffpr1ptOEDE1ZYH1T78pheEY1YmeXWBfFt/amZ6AGsKSLG+JPXvof3ltporTGR2bhH/eJPo7UHCglIuXfzYg==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.808.0.tgz", + "integrity": "sha512-VckV6l5cf/rL3EtgzSHVTTD4mI0gd8UxDDWbKJsxbQ2bpNPDQG2L1wWGLaolTSzjEJ5f3ijDwQrNDbY9l85Mmg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.775.0", - "@smithy/core": "^3.2.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@smithy/core": "^3.3.1", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -3227,13 +3332,13 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.775.0.tgz", - "integrity": "sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.808.0.tgz", + "integrity": "sha512-9x2QWfphkARZY5OGkl9dJxZlSlYM2l5inFeo2bKntGuwg4A4YUe5h7d5yJ6sZbam9h43eBrkOdumx03DAkQF9A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", - "@smithy/node-config-provider": "^4.0.2", + "@aws-sdk/types": "3.804.0", + "@smithy/node-config-provider": "^4.1.1", "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.2", @@ -3244,9 +3349,9 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/types": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", - "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.804.0.tgz", + "integrity": "sha512-A9qnsy9zQ8G89vrPPlNG9d1d8QcKRGqJKqwyGgS0dclJpwy6d1EWgQLIolKPl6vcFpLoe6avLOLxr+h8ur5wpg==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.2.0", @@ -3257,14 +3362,14 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-endpoints": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.775.0.tgz", - "integrity": "sha512-yjWmUgZC9tUxAo8Uaplqmq0eUh0zrbZJdwxGRKdYxfm4RG6fMw1tj52+KkatH7o+mNZvg1GDcVp/INktxonJLw==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.808.0.tgz", + "integrity": "sha512-N6Lic98uc4ADB7fLWlzx+1uVnq04VgVjngZvwHoujcRg9YDhIg9dUDiTzD5VZv13g1BrPYmvYP1HhsildpGV6w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/types": "^4.2.0", - "@smithy/util-endpoints": "^3.0.2", + "@smithy/util-endpoints": "^3.0.4", "tslib": "^2.6.2" }, "engines": { @@ -3272,26 +3377,26 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.775.0.tgz", - "integrity": "sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.804.0.tgz", + "integrity": "sha512-KfW6T6nQHHM/vZBBdGn6fMyG/MgX5lq82TDdX4HRQRRuHKLgBWGpKXqqvBwqIaCdXwWHgDrg2VQups6GqOWW2A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.775.0", + "@aws-sdk/types": "3.804.0", "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.775.0.tgz", - "integrity": "sha512-N9yhTevbizTOMo3drH7Eoy6OkJ3iVPxhV7dwb6CMAObbLneS36CSfA6xQXupmHWcRvZPTz8rd1JGG3HzFOau+g==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.808.0.tgz", + "integrity": "sha512-5UmB6u7RBSinXZAVP2iDgqyeVA/odO2SLEcrXaeTCw8ICXEoqF0K+GL36T4iDbzCBOAIugOZ6OcQX5vH3ck5UA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.775.0", - "@aws-sdk/types": "3.775.0", - "@smithy/node-config-provider": "^4.0.2", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/node-config-provider": "^4.1.1", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -3321,12 +3426,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/config-resolver": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.0.tgz", - "integrity": "sha512-8smPlwhga22pwl23fM5ew4T9vfLUCeFXlcqNOCD5M5h8VmNPNUE9j6bQSuRXpDSV11L/E/SwEBQuW8hr6+nS1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.2.tgz", + "integrity": "sha512-7r6mZGwb5LmLJ+zPtkLoznf2EtwEuSWdtid10pjGl/7HefCE4mueOkrfki8JCUm99W6UfP47/r3tbxx9CfBN5A==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.2", @@ -3337,12 +3442,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.2.0.tgz", - "integrity": "sha512-k17bgQhVZ7YmUvA8at4af1TDpl0NDMBuBKJl8Yg0nrefwmValU+CnA5l/AriVdQNthU/33H3nK71HrLgqOPr1Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.3.2.tgz", + "integrity": "sha512-GlLv+syoWolhtjX12XplL9BXBu10cjjD8iQC69fiKTrVNOB3Fjt8CVI9ccm6G3bLbMNe1gzrLD7yyMkYo4hchw==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-serde": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "@smithy/util-body-length-browser": "^4.0.0", @@ -3356,12 +3461,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/credential-provider-imds": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.2.tgz", - "integrity": "sha512-32lVig6jCaWBHnY+OEQ6e6Vnt5vDHaLiydGrwYMW9tPqO688hPGTYRamYJ1EptxEC2rAwJrHWmPoKRBl4iTa8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.4.tgz", + "integrity": "sha512-jN6M6zaGVyB8FmNGG+xOPQB4N89M1x97MMdMnm1ESjljLS3Qju/IegQizKujaNcy2vXAvrz0en8bobe6E55FEA==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", @@ -3442,14 +3547,14 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-endpoint": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.0.tgz", - "integrity": "sha512-xhLimgNCbCzsUppRTGXWkZywksuTThxaIB0HwbpsVLY5sceac4e1TZ/WKYqufQLaUy+gUSJGNdwD2jo3cXL0iA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.5.tgz", + "integrity": "sha512-WlpC9KVkajQf7RaGwi3n6lhHZzYTgm2PyX/2JjcwSHG417gFloNmYqN8rzDRXjT527/ZxZuvCsqq1gWZPW8lag==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.2.0", - "@smithy/middleware-serde": "^4.0.3", - "@smithy/node-config-provider": "^4.0.2", + "@smithy/core": "^3.3.2", + "@smithy/middleware-serde": "^4.0.4", + "@smithy/node-config-provider": "^4.1.1", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", @@ -3461,18 +3566,18 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-retry": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.0.tgz", - "integrity": "sha512-2zAagd1s6hAaI/ap6SXi5T3dDwBOczOMCSkkYzktqN1+tzbk1GAsHNAdo/1uzxz3Ky02jvZQwbi/vmDA6z4Oyg==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.6.tgz", + "integrity": "sha512-bl8q95nvCf7d22spxsBfs2giUDFf7prWLAxF5tmfgGBYHbUNW+OfnwMnabC15GMLA2AoE4HOtQR18a59lx6Blw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/protocol-http": "^5.1.0", - "@smithy/service-error-classification": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/service-error-classification": "^4.0.3", + "@smithy/smithy-client": "^4.2.5", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", - "@smithy/util-retry": "^4.0.2", + "@smithy/util-retry": "^4.0.3", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -3481,11 +3586,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-serde": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.3.tgz", - "integrity": "sha512-rfgDVrgLEVMmMn0BI8O+8OVr6vXzjV7HZj57l0QxslhzbvVfikZbVfBVthjLHqib4BW44QhcIgJpvebHlRaC9A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.4.tgz", + "integrity": "sha512-CaLvBtz+Xgs7eOwoinTXhZ02/9u8b28RT8lQAaDh7Q59nygeYYp1UiJjwl6zsay+lp0qVT/S7qLVI5RgcxjyfQ==", "license": "Apache-2.0", "dependencies": { + "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -3507,9 +3613,9 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-config-provider": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.2.tgz", - "integrity": "sha512-WgCkILRZfJwJ4Da92a6t3ozN/zcvYyJGUTmfGbgS/FkCcoCjl7G4FJaCDN1ySdvLvemnQeo25FdkyMSTSwulsw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.1.tgz", + "integrity": "sha512-1slS5jf5icHETwl5hxEVBj+mh6B+LbVW4yRINsGtUKH+nxM5Pw2H59+qf+JqYFCHp9jssG4vX81f5WKnjMN3Vw==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.2", @@ -3591,9 +3697,9 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/service-error-classification": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.2.tgz", - "integrity": "sha512-LA86xeFpTKn270Hbkixqs5n73S+LVM0/VZco8dqd+JT75Dyx3Lcw/MraL7ybjmz786+160K8rPOmhsq0SocoJQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.3.tgz", + "integrity": "sha512-FTbcajmltovWMjj3tksDQdD23b2w6gH+A0DYA1Yz3iSpjDj8fmkwy62UnXcWMy4d5YoMoSyLFHMfkEVEzbiN8Q==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.2.0" @@ -3616,9 +3722,9 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/signature-v4": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.2.tgz", - "integrity": "sha512-Mz+mc7okA73Lyz8zQKJNyr7lIcHLiPYp0+oiqiMNc/t7/Kf2BENs5d63pEj7oPqdjaum6g0Fc8wC78dY1TgtXw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.0.tgz", + "integrity": "sha512-4t5WX60sL3zGJF/CtZsUQTs3UrZEDO2P7pEaElrekbLqkWPYkgqNW1oeiNYC6xXifBnT9dVBOnNQRvOE9riU9w==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.0.0", @@ -3635,13 +3741,13 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/smithy-client": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.0.tgz", - "integrity": "sha512-Qs65/w30pWV7LSFAez9DKy0Koaoh3iHhpcpCCJ4waj/iqwsuSzJna2+vYwq46yBaqO5ZbP9TjUsATUNxrKeBdw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.5.tgz", + "integrity": "sha512-T3gA/TShe52Ln0ywWGVoDiqRvaxqvrU0CKRRmzT71/I1rRBD8mY85rvMMME6vw5RpBLJC9ADmXSLmpohF7RRhA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.2.0", - "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/core": "^3.3.2", + "@smithy/middleware-endpoint": "^4.1.5", "@smithy/middleware-stack": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", @@ -3742,13 +3848,13 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.8.tgz", - "integrity": "sha512-ZTypzBra+lI/LfTYZeop9UjoJhhGRTg3pxrNpfSTQLd3AJ37r2z4AXTKpq1rFXiiUIJsYyFgNJdjWRGP/cbBaQ==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.13.tgz", + "integrity": "sha512-HCLfXAyTEpVWLuyxDABg8UQukeRwChL1UErpSQ4KJK2ZoadmXuQY68pTL9KcuEtasTkIjnzyLUL9vhLdJ3VFHQ==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.5", "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -3758,16 +3864,16 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.8.tgz", - "integrity": "sha512-Rgk0Jc/UDfRTzVthye/k2dDsz5Xxs9LZaKCNPgJTRyoyBoeiNCnHsYGOyu1PKN+sDyPnJzMOz22JbwxzBp9NNA==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.13.tgz", + "integrity": "sha512-lu8E2RyzKzzFbNu4ICmY/2HltMZlJxMNg3saJ+r8I9vWbWbwdX7GOWUJdP4fbjEOm6aa52mnnd+uIRrT3dNEyA==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.1.0", - "@smithy/credential-provider-imds": "^4.0.2", - "@smithy/node-config-provider": "^4.0.2", + "@smithy/config-resolver": "^4.1.2", + "@smithy/credential-provider-imds": "^4.0.4", + "@smithy/node-config-provider": "^4.1.1", "@smithy/property-provider": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.5", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -3776,12 +3882,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-endpoints": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.2.tgz", - "integrity": "sha512-6QSutU5ZyrpNbnd51zRTL7goojlcnuOB55+F9VBD+j8JpRY50IGamsjlycrmpn8PQkmJucFW8A0LSfXj7jjtLQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.4.tgz", + "integrity": "sha512-VfFATC1bmZLV2858B/O1NpMcL32wYo8DPPhHxYxDCodDl3f3mSZ5oJheW1IF91A0EeAADz2WsakM/hGGPGNKLg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -3815,12 +3921,12 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-retry": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.2.tgz", - "integrity": "sha512-Qryc+QG+7BCpvjloFLQrmlSd0RsVRHejRXd78jNO3+oREueCjwG1CCEH1vduw/ZkM1U9TztwIKVIi3+8MJScGg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.3.tgz", + "integrity": "sha512-DPuYjZQDXmKr/sNvy9Spu8R/ESa2e22wXZzSAY6NkjOLj6spbIje/Aq8rT97iUMdDj0qHMRIe+bTxvlU74d9Ng==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.0.2", + "@smithy/service-error-classification": "^4.0.3", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -4861,16 +4967,17 @@ "dev": true }, "node_modules/@changesets/apply-release-plan": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.6.tgz", - "integrity": "sha512-TKhVLtiwtQOgMAC0fCJfmv93faiViKSDqr8oMEqrnNs99gtSC1sZh/aEMS9a+dseU1ESZRCK+ofLgGY7o0fw/Q==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.12.tgz", + "integrity": "sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==", "dev": true, + "license": "MIT", "dependencies": { - "@changesets/config": "^3.0.4", + "@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/git": "^3.0.4", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", @@ -4906,52 +5013,55 @@ } }, "node_modules/@changesets/assemble-release-plan": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.5.tgz", - "integrity": "sha512-IgvBWLNKZd6k4t72MBTBK3nkygi0j3t3zdC1zrfusYo0KpdsvnDjrMM9vPnTCLCMlfNs55jRL4gIMybxa64FCQ==", + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.8.tgz", + "integrity": "sha512-y8+8LvZCkKJdbUlpXFuqcavpzJR80PN0OIfn8HZdwK7Sh6MgLXm4hKY5vu6/NDoKp8lAlM4ERZCqRMLxP4m+MQ==", "dev": true, + "license": "MIT", "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.5.3" } }, "node_modules/@changesets/changelog-git": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.0.tgz", - "integrity": "sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.1.tgz", + "integrity": "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==", "dev": true, + "license": "MIT", "dependencies": { - "@changesets/types": "^6.0.0" + "@changesets/types": "^6.1.0" } }, "node_modules/@changesets/cli": { - "version": "2.27.10", - "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.27.10.tgz", - "integrity": "sha512-PfeXjvs9OfQJV8QSFFHjwHX3QnUL9elPEQ47SgkiwzLgtKGyuikWjrdM+lO9MXzOE22FO9jEGkcs4b+B6D6X0Q==", + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.29.4.tgz", + "integrity": "sha512-VW30x9oiFp/un/80+5jLeWgEU6Btj8IqOgI+X/zAYu4usVOWXjPIK5jSSlt5jsCU7/6Z7AxEkarxBxGUqkAmNg==", "dev": true, + "license": "MIT", "dependencies": { - "@changesets/apply-release-plan": "^7.0.6", - "@changesets/assemble-release-plan": "^6.0.5", - "@changesets/changelog-git": "^0.2.0", - "@changesets/config": "^3.0.4", + "@changesets/apply-release-plan": "^7.0.12", + "@changesets/assemble-release-plan": "^6.0.8", + "@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.5", - "@changesets/git": "^3.0.2", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/get-release-plan": "^4.0.12", + "@changesets/git": "^3.0.4", "@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.5", + "@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.7.0", - "enquirer": "^2.3.0", + "enquirer": "^2.4.1", "external-editor": "^3.1.0", "fs-extra": "^7.0.1", "mri": "^1.2.0", @@ -4972,6 +5082,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -4987,20 +5098,22 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@changesets/config": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.0.4.tgz", - "integrity": "sha512-+DiIwtEBpvvv1z30f8bbOsUQGuccnZl9KRKMM/LxUHuDu5oEjmN+bJQ1RIBKNJjfYMQn8RZzoPiX0UgPaLQyXw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.1.1.tgz", + "integrity": "sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==", "dev": true, + "license": "MIT", "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" @@ -5016,28 +5129,30 @@ } }, "node_modules/@changesets/get-dependents-graph": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.2.tgz", - "integrity": "sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.3.tgz", + "integrity": "sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==", "dev": true, + "license": "MIT", "dependencies": { - "@changesets/types": "^6.0.0", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "picocolors": "^1.1.0", "semver": "^7.5.3" } }, "node_modules/@changesets/get-release-plan": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.5.tgz", - "integrity": "sha512-E6wW7JoSMcctdVakut0UB76FrrN3KIeJSXvB+DHMFo99CnC3ZVnNYDCVNClMlqAhYGmLmAj77QfApaI3ca4Fkw==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.12.tgz", + "integrity": "sha512-KukdEgaafnyGryUwpHG2kZ7xJquOmWWWk5mmoeQaSvZTWH1DC5D/Sw6ClgGFYtQnOMSQhgoEbDxAbpIIayKH1g==", "dev": true, + "license": "MIT", "dependencies": { - "@changesets/assemble-release-plan": "^6.0.5", - "@changesets/config": "^3.0.4", - "@changesets/pre": "^2.0.1", - "@changesets/read": "^0.6.2", - "@changesets/types": "^6.0.0", + "@changesets/assemble-release-plan": "^6.0.8", + "@changesets/config": "^3.1.1", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.5", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, @@ -5048,10 +5163,11 @@ "dev": true }, "node_modules/@changesets/git": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.2.tgz", - "integrity": "sha512-r1/Kju9Y8OxRRdvna+nxpQIsMsRQn9dhhAZt94FLDeu0Hij2hnOozW8iqnHBgvu+KdnJppCveQwK4odwfw/aWQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.4.tgz", + "integrity": "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==", "dev": true, + "license": "MIT", "dependencies": { "@changesets/errors": "^0.2.0", "@manypkg/get-packages": "^1.1.3", @@ -5070,12 +5186,13 @@ } }, "node_modules/@changesets/parse": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.0.tgz", - "integrity": "sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.1.tgz", + "integrity": "sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==", "dev": true, + "license": "MIT", "dependencies": { - "@changesets/types": "^6.0.0", + "@changesets/types": "^6.1.0", "js-yaml": "^3.13.1" } }, @@ -5084,6 +5201,7 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -5093,6 +5211,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -5105,60 +5224,66 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@changesets/pre": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.1.tgz", - "integrity": "sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.2.tgz", + "integrity": "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==", "dev": true, + "license": "MIT", "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" } }, "node_modules/@changesets/read": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.2.tgz", - "integrity": "sha512-wjfQpJvryY3zD61p8jR87mJdyx2FIhEcdXhKUqkja87toMrP/3jtg/Yg29upN+N4Ckf525/uvV7a4tzBlpk6gg==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.5.tgz", + "integrity": "sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==", "dev": true, + "license": "MIT", "dependencies": { - "@changesets/git": "^3.0.2", + "@changesets/git": "^3.0.4", "@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.0" } }, "node_modules/@changesets/should-skip-package": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.1.tgz", - "integrity": "sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.2.tgz", + "integrity": "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==", "dev": true, + "license": "MIT", "dependencies": { - "@changesets/types": "^6.0.0", + "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "node_modules/@changesets/types": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.0.0.tgz", - "integrity": "sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==", - "dev": true + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "dev": true, + "license": "MIT" }, "node_modules/@changesets/write": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.3.2.tgz", - "integrity": "sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.4.0.tgz", + "integrity": "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==", "dev": true, + "license": "MIT", "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.7.1" } }, @@ -5178,9 +5303,9 @@ } }, "node_modules/@dotenvx/dotenvx": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.34.0.tgz", - "integrity": "sha512-+Dp/xaI3IZ4eKv+b2vg4V89VnqLKbmJ7UZ7unnZxMu9SNLOSc2jYaXey1YHCJM+67T0pOr2Gbej3TewnuoqTWQ==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.44.0.tgz", + "integrity": "sha512-18Aa+7KP/L2Kj9lxmT4EJZnsCq/xGIHgzU26rdzsKMhjpeT3YY+qin/dNAnIaVHPZnee7kXpZL55M9htd30r7Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5237,9 +5362,9 @@ } }, "node_modules/@dotenvx/dotenvx/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5359,9 +5484,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", "cpu": [ "ppc64" ], @@ -5376,9 +5501,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", "cpu": [ "arm" ], @@ -5393,9 +5518,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", "cpu": [ "arm64" ], @@ -5410,9 +5535,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", "cpu": [ "x64" ], @@ -5427,9 +5552,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", "cpu": [ "arm64" ], @@ -5444,9 +5569,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", "cpu": [ "x64" ], @@ -5461,9 +5586,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", "cpu": [ "arm64" ], @@ -5478,9 +5603,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", "cpu": [ "x64" ], @@ -5495,9 +5620,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", "cpu": [ "arm" ], @@ -5512,9 +5637,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", "cpu": [ "arm64" ], @@ -5529,9 +5654,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", "cpu": [ "ia32" ], @@ -5546,9 +5671,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", "cpu": [ "loong64" ], @@ -5563,9 +5688,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", "cpu": [ "mips64el" ], @@ -5580,9 +5705,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", "cpu": [ "ppc64" ], @@ -5597,9 +5722,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", "cpu": [ "riscv64" ], @@ -5614,9 +5739,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", "cpu": [ "s390x" ], @@ -5631,9 +5756,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", "cpu": [ "x64" ], @@ -5648,9 +5773,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", "cpu": [ "arm64" ], @@ -5665,9 +5790,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", "cpu": [ "x64" ], @@ -5682,9 +5807,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", "cpu": [ "arm64" ], @@ -5699,9 +5824,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", "cpu": [ "x64" ], @@ -5716,9 +5841,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", "cpu": [ "x64" ], @@ -5733,9 +5858,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", "cpu": [ "arm64" ], @@ -5750,9 +5875,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", "cpu": [ "ia32" ], @@ -5767,9 +5892,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", "cpu": [ "x64" ], @@ -5890,9 +6015,9 @@ "license": "MIT" }, "node_modules/@google/genai": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-0.9.0.tgz", - "integrity": "sha512-FD2RizYGInsvfjeaN6O+wQGpRnGVglS1XWrGQr8K7D04AfMmvPodDSw94U9KyFtsVLzWH9kmlPyFM+G4jbmkqg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-0.13.0.tgz", + "integrity": "sha512-eaEncWt875H7046T04mOpxpHJUM+jLIljEf+5QctRyOeChylE/nhpwm1bZWTRWoOu/t46R9r+PmgsJFhTpE7tQ==", "license": "Apache-2.0", "dependencies": { "google-auth-library": "^9.14.2", @@ -5904,24 +6029,6 @@ "node": ">=18.0.0" } }, - "node_modules/@google/genai/node_modules/zod": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/@google/genai/node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -6743,17 +6850,18 @@ "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.7.0.tgz", - "integrity": "sha512-IYPe/FLpvF3IZrd/f5p5ffmWhMc3aEMuM2wGJASDqC2Ge7qatVCdbfPx3n/5xFeb19xN0j/911M2AaFuircsWA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.9.0.tgz", + "integrity": "sha512-Jq2EUCQpe0iyO5FGpzVYDNFR6oR53AIrwph9yWl7uSc7IWUMsrmpmSaTGra5hQNunXpM+9oit85p924jWuHzUA==", "license": "MIT", "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", + "cross-spawn": "^7.0.3", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", - "pkce-challenge": "^4.1.0", + "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" @@ -6762,22 +6870,13 @@ "node": ">=18" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "node_modules/@modelcontextprotocol/sdk/node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/zod-to-json-schema": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.3.tgz", - "integrity": "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" + "engines": { + "node": ">=16.20.0" } }, "node_modules/@mswjs/interceptors": { @@ -7229,6 +7328,19 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -8910,47 +9022,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "node_modules/@snyk/github-codeowners": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@snyk/github-codeowners/-/github-codeowners-1.1.0.tgz", - "integrity": "sha512-lGFf08pbkEac0NYgVf4hdANpAgApRjNByLXB+WBip3qj1iendOIyAwP2GKkKbQMNVy2r1xxDf0ssfWscoiC+Vw==", - "dev": true, - "dependencies": { - "commander": "^4.1.1", - "ignore": "^5.1.8", - "p-map": "^4.0.0" - }, - "bin": { - "github-codeowners": "dist/cli.js" - }, - "engines": { - "node": ">=8.10" - } - }, - "node_modules/@snyk/github-codeowners/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@snyk/github-codeowners/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -9109,21 +9180,23 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz", - "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==", + "version": "20.17.46", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.46.tgz", + "integrity": "sha512-0PQHLhZPWOxGW4auogW0eOQAuNIlCYvibIpG67ja0TOJ6/sehu+1en7sfceUn+QQtx4Rk3GxbLNwPh0Cav7TWw==", + "license": "MIT", "dependencies": { "undici-types": "~6.19.2" } }, "node_modules/@types/node-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/node-cache/-/node-cache-4.1.3.tgz", - "integrity": "sha512-3hsqnv3H1zkOhjygJaJUYmgz5+FcPO3vejBX7cE9/cnuINOJYrzkfOnUCvpwGe9kMZANIHJA7J5pOdeyv52OEw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/node-cache/-/node-cache-4.2.5.tgz", + "integrity": "sha512-faK2Owokboz53g8ooq2dw3iDJ6/HMTCIa2RvMte5WMTiABy+wA558K+iuyRtlR67Un5q9gEKysSDtqZYbSa0Pg==", + "deprecated": "This is a stub types definition. node-cache provides its own type definitions, so you do not need this installed.", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "node-cache": "*" } }, "node_modules/@types/node-fetch": { @@ -9146,9 +9219,13 @@ } }, "node_modules/@types/pdf-parse": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.4.tgz", - "integrity": "sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.5.tgz", + "integrity": "sha512-kBfrSXsloMnUJOKi25s3+hRmkycHfLK6A09eRGqF/N8BkQoPUmaCr+q8Cli5FnfohEz/rsv82zAiPz/LXtOGhA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/ps-tree": { "version": "1.1.6", @@ -9435,6 +9512,119 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vitest/expect": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.3.tgz", + "integrity": "sha512-7FTQQuuLKmN1Ig/h+h/GO+44Q1IlglPlR2es4ab7Yvfx+Uk5xsv+Ykk+MEt/M2Yn/xGmzaLKxGw2lgy2bwuYqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.1.3", + "@vitest/utils": "3.1.3", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.3.tgz", + "integrity": "sha512-PJbLjonJK82uCWHjzgBJZuR7zmAOrSvKk1QBxrennDIgtH4uK0TB1PvYmc0XBCigxxtiAVPfWtAdy4lpz8SQGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.1.3", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.3.tgz", + "integrity": "sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.3.tgz", + "integrity": "sha512-Tae+ogtlNfFei5DggOsSUvkIaSuVywujMj6HzR97AHK6XK8i3BuVyIifWAm/sE3a15lF5RH9yQIrbXYuo0IFyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.1.3", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.3.tgz", + "integrity": "sha512-XVa5OPNTYUsyqG9skuUkFzAeFnEzDp8hQu7kZ0N25B1+6KjGm4hWLtURyBbsIAOekfWQ7Wuz/N/XXzgYO3deWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.1.3", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.3.tgz", + "integrity": "sha512-x6w+ctOEmEXdWaa6TO4ilb7l9DxPR5bwEb6hILKuxfU1NqWT2mpJD9NJN7t3OTfxmVlOMrvtoFJGdgyzZ605lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.3.tgz", + "integrity": "sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.1.3", + "loupe": "^3.1.3", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@vscode/codicons": { "version": "0.0.36", "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.36.tgz", @@ -9939,19 +10129,6 @@ "node": ">= 8.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -10060,22 +10237,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -10085,26 +10246,14 @@ "node": ">=8" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, "node_modules/ast-types": { @@ -10134,25 +10283,10 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/axios": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", - "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -10402,6 +10536,7 @@ "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", "dev": true, + "license": "MIT", "dependencies": { "is-windows": "^1.0.0" }, @@ -10676,24 +10811,6 @@ "node": ">=8" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -10761,6 +10878,23 @@ } ] }, + "node_modules/chai": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -10804,6 +10938,16 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/cheerio": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", @@ -10880,6 +11024,15 @@ "devtools-protocol": "*" } }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -10907,15 +11060,6 @@ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", "license": "MIT" }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -11050,16 +11194,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -11469,57 +11603,6 @@ "node": ">= 14" } }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -11567,10 +11650,20 @@ } } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "license": "MIT", "optional": true, @@ -11634,36 +11727,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "optional": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -11677,23 +11740,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/degenerator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", @@ -11947,27 +11993,6 @@ "node": ">=6.0.0" } }, - "node_modules/easy-table": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", - "integrity": "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "optionalDependencies": { - "wcwidth": "^1.0.1" - } - }, - "node_modules/easy-table/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -12153,66 +12178,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-abstract": { - "version": "1.23.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", - "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -12229,6 +12194,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -12241,41 +12213,10 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -12286,31 +12227,31 @@ "node": ">=18" }, "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/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, "node_modules/escalade": { @@ -12559,6 +12500,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -12659,9 +12610,9 @@ } }, "node_modules/execa": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", - "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", + "version": "9.5.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.3.tgz", + "integrity": "sha512-QFNnTvU3UjgWFy8Ef9iDHvIdcgZ344ebkwYx4/KLbR+CKQA4xBaHzv+iRpp86QfMHP8faFQLh8iOc57215y4Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -12685,19 +12636,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execa/node_modules/@sindresorhus/merge-streams": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/execa/node_modules/is-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", @@ -12747,6 +12685,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/expect-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", + "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz", @@ -12971,22 +12919,18 @@ "dev": true }, "node_modules/fast-xml-parser": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", - "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } ], "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" @@ -13019,6 +12963,16 @@ "bser": "2.1.1" } }, + "node_modules/fd-package-json": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-1.2.0.tgz", + "integrity": "sha512-45LSPmWf+gC5tdCQMNH4s9Sr00bIkiD9aN7dc5hqkrEw1geRYyDQS1v1oMHAW3ysfxfndqGsrDREHHjNNbKUfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-up-path": "^3.0.1" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -13219,15 +13173,6 @@ } } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -13262,6 +13207,22 @@ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" }, + "node_modules/formatly": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.2.3.tgz", + "integrity": "sha512-WH01vbXEjh9L3bqn5V620xUAWs32CmK4IzWRRY6ep5zpa/mrisL4d9+pRVuETORVDTQw8OycSO1WC68PL51RaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fd-package-json": "^1.2.0" + }, + "bin": { + "formatly": "bin/index.mjs" + }, + "engines": { + "node": ">=18.3.0" + } + }, "node_modules/formdata-node": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", @@ -13346,33 +13307,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/fzf": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz", @@ -13578,23 +13512,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/get-tsconfig": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", @@ -13630,9 +13547,9 @@ "optional": true }, "node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", + "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", "dev": true, "license": "ISC", "dependencies": { @@ -13696,22 +13613,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/google-auth-library": { "version": "9.15.0", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.0.tgz", @@ -13762,15 +13663,6 @@ "node": ">=14.0.0" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -13780,33 +13672,6 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -13818,21 +13683,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -13849,12 +13699,6 @@ "node": ">= 0.4" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -13920,10 +13764,14 @@ } }, "node_modules/human-id": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/human-id/-/human-id-1.0.2.tgz", - "integrity": "sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==", - "dev": true + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.1.tgz", + "integrity": "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } }, "node_modules/human-signals": { "version": "8.0.0", @@ -14076,15 +13924,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -14108,20 +13947,6 @@ "license": "ISC", "optional": true }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", @@ -14143,85 +13968,31 @@ "node": ">= 0.10" } }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" + "ci-info": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "is-ci": "bin.js" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-async-function": { + "node_modules/is-ci/node_modules/ci-info": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.0.tgz", - "integrity": "sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, "node_modules/is-core-module": { "version": "2.15.1", @@ -14238,36 +14009,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, - "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -14293,21 +14034,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz", - "integrity": "sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -14325,21 +14051,6 @@ "node": ">=6" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -14384,30 +14095,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-node-process": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", @@ -14424,22 +14111,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.0.tgz", - "integrity": "sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -14479,51 +14150,6 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, - "node_modules/is-regex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz", - "integrity": "sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "gopd": "^1.1.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -14535,27 +14161,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.0.tgz", - "integrity": "sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-subdir": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", "dev": true, + "license": "MIT", "dependencies": { "better-path-resolve": "1.0.0" }, @@ -14563,38 +14174,6 @@ "node": ">=4" } }, - "node_modules/is-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.0.tgz", - "integrity": "sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "has-symbols": "^1.0.3", - "safe-regex-test": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-unicode-supported": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", @@ -14608,51 +14187,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15704,12 +15244,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -15878,9 +15412,9 @@ } }, "node_modules/knip": { - "version": "5.44.4", - "resolved": "https://registry.npmjs.org/knip/-/knip-5.44.4.tgz", - "integrity": "sha512-Ryn8LwWHLId8jSK1DgtT0hmg5DbzkqAtH+Gg3vZJpmSMgGHMspej9Ag+qKTm8wsPLDjVetuEz/lIsobo0XCMvQ==", + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.55.1.tgz", + "integrity": "sha512-NYXjgGrXgMdabUKCP2TlBH/e83m9KnLc1VLyWHUtoRrCEJ/C15YtbafrpTvm3td+jE4VdDPgudvXT1IMtCx8lw==", "dev": true, "funding": [ { @@ -15896,21 +15430,19 @@ "url": "https://polar.sh/webpro-nl" } ], + "license": "ISC", "dependencies": { - "@nodelib/fs.walk": "3.0.1", - "@snyk/github-codeowners": "1.1.0", - "easy-table": "1.2.0", - "enhanced-resolve": "^5.18.0", + "@nodelib/fs.walk": "^1.2.3", + "enhanced-resolve": "^5.18.1", "fast-glob": "^3.3.3", + "formatly": "^0.2.3", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "minimist": "^1.2.8", "picocolors": "^1.1.0", "picomatch": "^4.0.1", - "pretty-ms": "^9.0.0", "smol-toml": "^1.3.1", "strip-json-comments": "5.0.1", - "summary": "2.1.0", "zod": "^3.22.4", "zod-validation-error": "^3.0.3" }, @@ -15926,46 +15458,12 @@ "typescript": ">=5.0.4" } }, - "node_modules/knip/node_modules/@nodelib/fs.scandir": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-4.0.1.tgz", - "integrity": "sha512-vAkI715yhnmiPupY+dq+xenu5Tdf2TBQ66jLvBIcCddtz+5Q8LbMKaf9CIJJreez8fQ8fgaY+RaywQx8RJIWpw==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "4.0.0", - "run-parallel": "^1.2.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/knip/node_modules/@nodelib/fs.stat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-4.0.0.tgz", - "integrity": "sha512-ctr6bByzksKRCV0bavi8WoQevU6plSp2IkllIsEqaiKe2mwNNnaluhnRhcsgGZHrrHk57B3lf95MkLMO3STYcg==", - "dev": true, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/knip/node_modules/@nodelib/fs.walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-3.0.1.tgz", - "integrity": "sha512-nIh/M6Kh3ZtOmlY00DaUYB4xeeV6F3/ts1l29iwl3/cfyY/OuCfUx+v08zgx8TKPTifXRcjjqVQ4KB2zOYSbyw==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "4.0.1", - "fastq": "^1.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, "node_modules/knip/node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -15978,6 +15476,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -16034,21 +15533,22 @@ "dev": true }, "node_modules/lint-staged": { - "version": "15.2.11", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.11.tgz", - "integrity": "sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==", - "dev": true, - "dependencies": { - "chalk": "~5.3.0", - "commander": "~12.1.0", - "debug": "~4.4.0", - "execa": "~8.0.1", - "lilconfig": "~3.1.3", - "listr2": "~8.2.5", - "micromatch": "~4.0.8", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.6.1" + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -16061,10 +15561,11 @@ } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -16073,10 +15574,11 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } @@ -16086,6 +15588,7 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", @@ -16109,6 +15612,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -16121,6 +15625,7 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=16.17.0" } @@ -16130,6 +15635,7 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -16142,6 +15648,7 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -16154,6 +15661,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^4.0.0" }, @@ -16169,6 +15677,7 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" }, @@ -16184,6 +15693,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -16191,23 +15701,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/lint-staged/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -16284,43 +15783,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -16590,6 +16052,13 @@ "underscore": "^1.13.1" } }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -16610,6 +16079,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -16641,9 +16120,10 @@ } }, "node_modules/mammoth": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.8.0.tgz", - "integrity": "sha512-pJNfxSk9IEGVpau+tsZFz22ofjUsl2mnA5eT8PjPs2n0BP+rhVte4Nez6FdgEuxv3IGI3afiV46ImKqTGDVlbA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.9.0.tgz", + "integrity": "sha512-F+0NxzankQV9XSUAuVKvkdQK0GbtGGuqVnND9aVf9VSeUA82LQa29GjLqYU6Eez8LHqSJG3eGiDW3224OKdpZg==", + "license": "BSD-2-Clause", "dependencies": { "@xmldom/xmldom": "^0.8.6", "argparse": "~1.0.3", @@ -16651,7 +16131,7 @@ "bluebird": "~3.4.0", "dingbat-to-unicode": "^1.0.1", "jszip": "^3.7.1", - "lop": "^0.4.1", + "lop": "^0.4.2", "path-is-absolute": "^1.0.0", "underscore": "^1.13.1", "xmlbuilder": "^10.0.0" @@ -16667,6 +16147,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -16674,7 +16155,8 @@ "node_modules/mammoth/node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" }, "node_modules/map-stream": { "version": "0.1.0", @@ -16941,6 +16423,25 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/napi-build-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", @@ -16972,12 +16473,6 @@ "node": ">= 0.4.0" } }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "node_modules/nock": { "version": "14.0.4", "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.4.tgz", @@ -17139,27 +16634,6 @@ "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "license": "MIT" }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -17169,169 +16643,96 @@ "node": ">=0.10.0" } }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.1.tgz", + "integrity": "sha512-jkhE0AsELQeCtScrcJ/7mSIdk+ZsnWjvKk3KwE96HZ6+OFVB74XhxQtHT1W6kdUfn92fRnBb29Mz82j9bV2XEQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" + "minimatch": "^10.0.1", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" }, "bin": { "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js" }, "engines": { - "node": ">= 4" - } - }, - "node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" } }, - "node_modules/npm-run-all/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "license": "MIT", "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/npm-run-all/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "node": ">=12" }, - "engines": { - "node": ">=4.8" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/npm-run-all/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "license": "ISC", "engines": { - "node": ">=0.8.0" + "node": ">=16" } }, - "node_modules/npm-run-all/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/npm-run-all2/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" - } - }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" + "node": "20 || >=22" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, + "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "which": "bin/which" + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-run-path": { @@ -17408,15 +16809,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/object-treeify": { "version": "1.1.33", "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", @@ -17427,24 +16819,6 @@ "node": ">= 10" } }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -17665,25 +17039,73 @@ "dev": true, "license": "MIT" }, - "node_modules/p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "node_modules/ovsx": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.10.2.tgz", + "integrity": "sha512-osLwIOz5Uu1ePvYYSjKw05bkCZo2j/ydQLjm3uO7bCfstyFFzmWyzMGTAL3wJpI4qpo1S47Y52+q09h9A2ZRkQ==", "dev": true, + "license": "EPL-2.0", "dependencies": { - "p-map": "^2.0.0" + "@vscode/vsce": "^3.2.1", + "commander": "^6.2.1", + "follow-redirects": "^1.14.6", + "is-ci": "^2.0.0", + "leven": "^3.1.0", + "semver": "^7.6.0", + "tmp": "^0.2.3", + "yauzl": "^3.1.3" + }, + "bin": { + "ovsx": "lib/ovsx" }, "engines": { - "node": ">=8" + "node": ">= 20" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/ovsx/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/ovsx/node_modules/yauzl": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" @@ -17712,6 +17134,7 @@ "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -17969,6 +17392,23 @@ "node": ">=16" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -18025,10 +17465,11 @@ } }, "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, + "license": "MIT", "bin": { "pidtree": "bin/pidtree.js" }, @@ -18036,15 +17477,6 @@ "node": ">=0.10" } }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -18127,13 +17559,33 @@ "node": ">=8" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, "engines": { - "node": ">= 0.4" + "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-load-config": { @@ -18180,11 +17632,12 @@ } }, "node_modules/posthog-node": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-4.7.0.tgz", - "integrity": "sha512-RgdUKSW8MfMOkjUa8cYVqWndNjPePNuuxlGbrZC6z1WRBsVc6TdGl8caidmC10RW8mu/BOfmrGbP4cRTo2jARg==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-4.17.1.tgz", + "integrity": "sha512-cVlQPOwOPjakUnrueKRCQe1m2Ku+XzKaOos7Tn/zDZkkZFeBT/byP7tbNf7LiwhaBRWFBRowZZb/MsTtSRaorg==", + "license": "MIT", "dependencies": { - "axios": "^1.7.4" + "axios": "^1.8.2" }, "engines": { "node": ">=15.0.0" @@ -18276,10 +17729,11 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -18658,30 +18112,28 @@ "node": ">=0.8" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", "dev": true, + "license": "ISC", "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" }, "engines": { - "node": ">=4" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/read-yaml-file": { @@ -18785,51 +18237,11 @@ "node": ">=12.0.0" } }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", - "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "dunder-proto": "^1.0.0", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.2.0", - "which-builtin-type": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -19062,30 +18474,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -19105,28 +18493,20 @@ } ] }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -19153,9 +18533,10 @@ "peer": true }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -19235,38 +18616,6 @@ "node": ">= 18" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -19392,6 +18741,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -19574,6 +18930,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", @@ -19594,38 +18960,6 @@ "signal-exit": "^4.0.1" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", - "dev": true - }, "node_modules/split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", @@ -19664,6 +18998,13 @@ "node": ">=8" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -19673,6 +19014,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -19853,73 +19201,6 @@ "node": ">=8" } }, - "node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -19995,9 +19276,16 @@ } }, "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" }, "node_modules/strong-type": { "version": "1.1.0", @@ -20102,12 +19390,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/summary": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/summary/-/summary-2.1.0.tgz", - "integrity": "sha512-nMIjMrd5Z2nuB2RZCKJfFMjgS3fygbeyGk9PxPPaJR1RIcyN9yn4A63Isovzm3ZtQuEkLBVgMdPup8UeLH7aQw==", - "dev": true - }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -20303,6 +19585,13 @@ "integrity": "sha512-/kqtlepLMptX0OgbYD9aMYbM7EFrMZCL7EoHM8Psmg2FuhXoo/bH64KqOiZGGwa6oS9TPdSEDKBnV2LuB8+5vQ==", "license": "MIT" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -20311,13 +19600,13 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.3", + "fdir": "^6.4.4", "picomatch": "^4.0.2" }, "engines": { @@ -20328,9 +19617,9 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -20355,6 +19644,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -20414,6 +19733,15 @@ "tree-sitter-wasms": "^0.1.11" } }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -20434,10 +19762,11 @@ "license": "Apache-2.0" }, "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "version": "29.3.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.2.tgz", + "integrity": "sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug==", "dev": true, + "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", @@ -20446,7 +19775,8 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.6.3", + "semver": "^7.7.1", + "type-fest": "^4.39.1", "yargs-parser": "^21.1.1" }, "bin": { @@ -20481,6 +19811,19 @@ } } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -20538,516 +19881,50 @@ } } }, - "node_modules/tsup/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", - "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", - "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", - "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", - "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", - "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/darwin-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", - "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", - "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", - "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", - "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", - "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", - "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-loong64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", - "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", - "cpu": [ - "loong64" - ], + "node_modules/tsup/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsup/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", - "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", - "cpu": [ - "mips64el" - ], + "node_modules/tsup/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, "engines": { - "node": ">=18" + "node": ">= 8" } }, - "node_modules/tsup/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", - "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", - "cpu": [ - "ppc64" - ], + "node_modules/tsup/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/tsup/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", - "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", - "cpu": [ - "riscv64" - ], + "node_modules/tsup/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } + "license": "BSD-2-Clause" }, - "node_modules/tsup/node_modules/@esbuild/linux-s390x": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", - "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", - "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", - "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", - "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", - "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", - "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/sunos-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", - "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/win32-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", - "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/win32-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", - "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/win32-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", - "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/esbuild": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", - "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.1", - "@esbuild/android-arm": "0.25.1", - "@esbuild/android-arm64": "0.25.1", - "@esbuild/android-x64": "0.25.1", - "@esbuild/darwin-arm64": "0.25.1", - "@esbuild/darwin-x64": "0.25.1", - "@esbuild/freebsd-arm64": "0.25.1", - "@esbuild/freebsd-x64": "0.25.1", - "@esbuild/linux-arm": "0.25.1", - "@esbuild/linux-arm64": "0.25.1", - "@esbuild/linux-ia32": "0.25.1", - "@esbuild/linux-loong64": "0.25.1", - "@esbuild/linux-mips64el": "0.25.1", - "@esbuild/linux-ppc64": "0.25.1", - "@esbuild/linux-riscv64": "0.25.1", - "@esbuild/linux-s390x": "0.25.1", - "@esbuild/linux-x64": "0.25.1", - "@esbuild/netbsd-arm64": "0.25.1", - "@esbuild/netbsd-x64": "0.25.1", - "@esbuild/openbsd-arm64": "0.25.1", - "@esbuild/openbsd-x64": "0.25.1", - "@esbuild/sunos-x64": "0.25.1", - "@esbuild/win32-arm64": "0.25.1", - "@esbuild/win32-ia32": "0.25.1", - "@esbuild/win32-x64": "0.25.1" - } - }, - "node_modules/tsup/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tsup/node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tsup/node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/tsup/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/tsup/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "node_modules/tsup/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "license": "MIT", "dependencies": { @@ -21056,490 +19933,24 @@ "webidl-conversions": "^4.0.2" } }, - "node_modules/tsx": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz", - "integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", - "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", - "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", - "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", - "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", - "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/darwin-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", - "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", - "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", - "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", - "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", - "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", - "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-loong64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", - "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", - "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", - "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", - "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-s390x": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", - "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", - "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", - "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", - "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", - "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", - "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/sunos-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", - "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", - "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", - "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", - "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/esbuild": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", - "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "node_modules/tsx": { + "version": "4.19.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", + "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", "dev": true, - "hasInstallScript": true, "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, "bin": { - "esbuild": "bin/esbuild" + "tsx": "dist/cli.mjs" }, "engines": { - "node": ">=18" + "node": ">=18.0.0" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.1", - "@esbuild/android-arm": "0.25.1", - "@esbuild/android-arm64": "0.25.1", - "@esbuild/android-x64": "0.25.1", - "@esbuild/darwin-arm64": "0.25.1", - "@esbuild/darwin-x64": "0.25.1", - "@esbuild/freebsd-arm64": "0.25.1", - "@esbuild/freebsd-x64": "0.25.1", - "@esbuild/linux-arm": "0.25.1", - "@esbuild/linux-arm64": "0.25.1", - "@esbuild/linux-ia32": "0.25.1", - "@esbuild/linux-loong64": "0.25.1", - "@esbuild/linux-mips64el": "0.25.1", - "@esbuild/linux-ppc64": "0.25.1", - "@esbuild/linux-riscv64": "0.25.1", - "@esbuild/linux-s390x": "0.25.1", - "@esbuild/linux-x64": "0.25.1", - "@esbuild/netbsd-arm64": "0.25.1", - "@esbuild/netbsd-x64": "0.25.1", - "@esbuild/openbsd-arm64": "0.25.1", - "@esbuild/openbsd-x64": "0.25.1", - "@esbuild/sunos-x64": "0.25.1", - "@esbuild/win32-arm64": "0.25.1", - "@esbuild/win32-ia32": "0.25.1", - "@esbuild/win32-x64": "0.25.1" + "fsevents": "~2.3.3" } }, "node_modules/tunnel": { @@ -21642,80 +20053,6 @@ "node": ">= 0.6" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz", - "integrity": "sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/typed-query-selector": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", @@ -21734,10 +20071,11 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21746,21 +20084,6 @@ "node": ">=14.17" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -21866,6 +20189,12 @@ "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==" }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "license": "(WTFPL OR MIT)" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -21906,16 +20235,6 @@ "node": ">=10.12.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -21925,12 +20244,216 @@ "node": ">= 0.8" } }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.3.tgz", + "integrity": "sha512-uHV4plJ2IxCl4u1up1FQRrqclylKAogbtBfOTwcuJ28xFi+89PZ57BRh+naIRvH70HPwxy5QHYzg1OrEaC7AbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.3.tgz", + "integrity": "sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.1.3", + "@vitest/mocker": "3.1.3", + "@vitest/pretty-format": "^3.1.3", + "@vitest/runner": "3.1.3", + "@vitest/snapshot": "3.1.3", + "@vitest/spy": "3.1.3", + "@vitest/utils": "3.1.3", + "chai": "^5.2.0", + "debug": "^4.4.0", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.1.3", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.1.3", + "@vitest/ui": "3.1.3", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/vscode-material-icons": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/vscode-material-icons/-/vscode-material-icons-0.1.1.tgz", "integrity": "sha512-GsoEEF8Tbb0yUFQ6N6FPvh11kFkL9F95x0FkKlbbfRQN9eFms67h+L3t6b9cUv58dSn2gu8kEhNfoESVCrz4ag==", "license": "MIT" }, + "node_modules/walk-up-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", + "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", + "dev": true, + "license": "ISC" + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -21940,16 +20463,6 @@ "makeerror": "1.0.12" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "optional": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", @@ -22010,93 +20523,21 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", - "integrity": "sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==", - "dev": true, - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.0", - "is-number-object": "^1.1.0", - "is-string": "^1.1.0", - "is-symbol": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.0.tgz", - "integrity": "sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, + "license": "MIT", "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" + "siginfo": "^2.0.0", + "stackback": "0.0.2" }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", - "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" + "bin": { + "why-is-node-running": "cli.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, "node_modules/wide-align": { @@ -22457,10 +20898,11 @@ "dev": true }, "node_modules/yaml": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", - "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", "dev": true, + "license": "ISC", "bin": { "yaml": "bin.mjs" }, @@ -22575,13 +21017,23 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", + "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod-to-json-schema": { + "version": "3.24.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", + "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/zod-to-ts": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", diff --git a/package.json b/package.json index 6b3db19a012..35e4f36913e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "PearAI's integration of Roo Code / Cline, a coding agent.", "publisher": "PearAI", "icon": "assets/icons/pear.png", - "version": "3.15.3", + "version": "3.17.2", "galleryBanner": { "color": "#617A91", "theme": "dark" @@ -110,11 +110,6 @@ "title": "%command.settings.title%", "icon": "$(settings-gear)" }, - { - "command": "roo-cline.helpButtonClicked", - "title": "%command.documentation.title%", - "icon": "$(question)" - }, { "command": "roo-cline.openInNewTab", "title": "%command.openInNewTab.title%", @@ -283,11 +278,6 @@ "command": "roo-cline.settingsButtonClicked", "group": "navigation@6", "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" - }, - { - "command": "roo-cline.helpButtonClicked", - "group": "navigation@7", - "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" } ] }, @@ -336,7 +326,7 @@ "build:webview": "cd webview-ui && npm run build", "build:esbuild": "node esbuild.js --production", "compile": "tsc -p . --outDir out && node esbuild.js", - "install:all": "npm install npm-run-all && npm-run-all -l -p install-*", + "install:all": "npm install -D npm-run-all2@8.0.1 && npm-run-all -l -p install-*", "install-extension": "npm install", "install-webview": "cd webview-ui && npm install", "install-e2e": "cd e2e && npm install", @@ -351,11 +341,12 @@ "package": "npm-run-all -l -p build:webview build:esbuild check-types lint", "pretest": "npm run compile", "dev": "cd webview-ui && npm run dev -- --port 5174", - "test": "node scripts/run-tests.js", + "test": "npm-run-all test:*", "test:extension": "jest -w=40%", + "test:extension-esm": "vitest run", "test:webview": "cd webview-ui && npm run test", "prepare": "husky", - "publish:marketplace": "vsce publish && ovsx publish", + "publish:marketplace": "npx vsce publish && npx ovsx publish", "publish": "npm run build && changeset publish && npm install --package-lock-only", "version-packages": "changeset version && npm install --package-lock-only", "vscode:prepublish": "npm run package", @@ -381,9 +372,9 @@ "@anthropic-ai/sdk": "^0.37.0", "@anthropic-ai/vertex-sdk": "^0.7.0", "@aws-sdk/client-bedrock-runtime": "^3.779.0", - "@google/genai": "^0.9.0", + "@google/genai": "^0.13.0", "@mistralai/mistralai": "^1.3.6", - "@modelcontextprotocol/sdk": "^1.7.0", + "@modelcontextprotocol/sdk": "^1.9.0", "@pearai/core": "file:./../pearai-submodule/core", "@types/clone-deep": "^4.0.4", "@types/pdf-parse": "^1.1.4", @@ -422,6 +413,7 @@ "puppeteer-core": "^23.4.0", "react-tooltip": "^5.28.0", "reconnecting-eventsource": "^1.6.4", + "sanitize-filename": "^1.6.3", "say": "^0.16.0", "serialize-error": "^11.0.3", "simple-git": "^3.27.0", @@ -436,7 +428,7 @@ "vscode-material-icons": "^0.1.1", "web-tree-sitter": "^0.22.6", "workerpool": "^9.2.0", - "zod": "^3.23.8" + "zod": "^3.24.2" }, "devDependencies": { "@changesets/cli": "^2.27.10", @@ -456,8 +448,8 @@ "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.11.0", "@vscode/test-electron": "^2.5.2", - "@vscode/vsce": "^3.3.2", - "esbuild": "^0.24.0", + "@vscode/vsce": "3.3.2", + "esbuild": "^0.25.0", "eslint": "^8.57.0", "execa": "^9.5.2", "glob": "^11.0.1", @@ -468,13 +460,15 @@ "lint-staged": "^15.2.11", "mkdirp": "^3.0.1", "nock": "^14.0.4", - "npm-run-all": "^4.1.5", + "npm-run-all2": "^8.0.1", + "ovsx": "0.10.2", "prettier": "^3.4.2", "rimraf": "^6.0.1", "ts-jest": "^29.2.5", "tsup": "^8.4.0", "tsx": "^4.19.3", - "typescript": "^5.4.5", + "typescript": "5.8.3", + "vitest": "^3.1.3", "zod-to-ts": "^1.2.0" }, "lint-staged": { diff --git a/package.nls.nl.json b/package.nls.nl.json new file mode 100644 index 00000000000..8cd0b0e71f8 --- /dev/null +++ b/package.nls.nl.json @@ -0,0 +1,31 @@ +{ + "extension.displayName": "Roo Code (voorheen Roo Cline)", + "extension.description": "Een compleet ontwikkelteam van AI-agents in je editor.", + "views.contextMenu.label": "Roo Code", + "views.terminalMenu.label": "Roo Code", + "views.activitybar.title": "Roo Code", + "command.newTask.title": "Nieuwe Taak", + "command.mcpServers.title": "MCP Servers", + "command.prompts.title": "Prompts", + "command.history.title": "Geschiedenis", + "command.openInEditor.title": "Openen in Editor", + "command.settings.title": "Instellingen", + "command.documentation.title": "Documentatie", + "command.openInNewTab.title": "Openen in Nieuw Tabblad", + "command.explainCode.title": "Leg Code Uit", + "command.fixCode.title": "Repareer Code", + "command.improveCode.title": "Verbeter Code", + "command.addToContext.title": "Toevoegen aan Context", + "command.focusInput.title": "Focus op Invoerveld", + "command.setCustomStoragePath.title": "Aangepast Opslagpad Instellen", + "command.terminal.addToContext.title": "Terminalinhoud aan Context Toevoegen", + "command.terminal.fixCommand.title": "Repareer Dit Commando", + "command.terminal.explainCommand.title": "Leg Dit Commando Uit", + "command.acceptInput.title": "Invoer/Suggestie Accepteren", + "configuration.title": "Roo Code", + "commands.allowedCommands.description": "Commando's die automatisch kunnen worden uitgevoerd wanneer 'Altijd goedkeuren uitvoerbewerkingen' is ingeschakeld", + "settings.vsCodeLmModelSelector.description": "Instellingen voor VSCode Language Model API", + "settings.vsCodeLmModelSelector.vendor.description": "De leverancier van het taalmodel (bijv. copilot)", + "settings.vsCodeLmModelSelector.family.description": "De familie van het taalmodel (bijv. gpt-4)", + "settings.customStoragePath.description": "Aangepast opslagpad. Laat leeg om de standaardlocatie te gebruiken. Ondersteunt absolute paden (bijv. 'D:\\RooCodeStorage')" +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000000..ac084260c3c --- /dev/null +++ b/renovate.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"] +} diff --git a/scripts/generate-types.mts b/scripts/generate-types.mts index 2ad167b0d8f..141df12ee1d 100644 --- a/scripts/generate-types.mts +++ b/scripts/generate-types.mts @@ -1,9 +1,11 @@ -import fs from "fs/promises" +import path from "path" +import fs from "fs" import { zodToTs, createTypeAlias, printNode } from "zod-to-ts" import { $ } from "execa" import schemas from "../src/schemas" + const { typeDefinitions } = schemas async function main() { @@ -16,12 +18,17 @@ async function main() { types.push(`export type { ${identifier} }`) } - await fs.writeFile("src/exports/types.ts", types.join("\n\n")) + fs.writeFileSync("src/exports/types.ts", types.join("\n\n")) - await $`npx tsup src/exports/interface.ts --dts-only -d out` - await fs.copyFile('out/interface.d.ts', 'src/exports/roo-code.d.ts') + await $`npx tsup src/exports/interface.ts --dts -d out` + fs.copyFileSync("out/interface.d.ts", "src/exports/roo-code.d.ts") await $`npx prettier --write src/exports/types.ts src/exports/roo-code.d.ts` + + if (fs.existsSync(path.join("..", "Roo-Code-Types"))) { + fs.copyFileSync("out/interface.js", path.join("..", "Roo-Code-Types", "src", "index.js")) + fs.copyFileSync("out/interface.d.ts", path.join("..", "Roo-Code-Types", "src", "index.d.ts")) + } } main() diff --git a/scripts/run-tests.js b/scripts/run-tests.js deleted file mode 100644 index 9ea8a835dd2..00000000000 --- a/scripts/run-tests.js +++ /dev/null @@ -1,8 +0,0 @@ -// run-tests.js -const { execSync } = require("child_process") - -if (process.platform === "win32") { - execSync("npm-run-all test:*", { stdio: "inherit" }) -} else { - execSync("npm-run-all -p test:*", { stdio: "inherit" }) -} diff --git a/src/core/CodeActionProvider.ts b/src/activate/CodeActionProvider.ts similarity index 97% rename from src/core/CodeActionProvider.ts rename to src/activate/CodeActionProvider.ts index f749987d7e6..5f37d0320a3 100644 --- a/src/core/CodeActionProvider.ts +++ b/src/activate/CodeActionProvider.ts @@ -1,6 +1,6 @@ import * as vscode from "vscode" -import { EditorUtils } from "./EditorUtils" +import { EditorUtils } from "../integrations/editor/EditorUtils" export type CodeActionName = "EXPLAIN" | "FIX" | "IMPROVE" | "ADD_TO_CONTEXT" | "NEW_TASK" diff --git a/src/core/__tests__/CodeActionProvider.test.ts b/src/activate/__tests__/CodeActionProvider.test.ts similarity index 91% rename from src/core/__tests__/CodeActionProvider.test.ts rename to src/activate/__tests__/CodeActionProvider.test.ts index 1d6b84f09d6..1a56ad899f3 100644 --- a/src/core/__tests__/CodeActionProvider.test.ts +++ b/src/activate/__tests__/CodeActionProvider.test.ts @@ -1,12 +1,11 @@ -// npx jest src/core/__tests__/CodeActionProvider.test.ts +// npx jest src/activate/__tests__/CodeActionProvider.test.ts import * as vscode from "vscode" -import { EditorUtils } from "../EditorUtils" +import { EditorUtils } from "../../integrations/editor/EditorUtils" import { CodeActionProvider, ACTION_TITLES } from "../CodeActionProvider" -// Mock VSCode API jest.mock("vscode", () => ({ CodeAction: jest.fn().mockImplementation((title, kind) => ({ title, @@ -29,8 +28,7 @@ jest.mock("vscode", () => ({ }, })) -// Mock EditorUtils -jest.mock("../EditorUtils", () => ({ +jest.mock("../../integrations/editor/EditorUtils", () => ({ EditorUtils: { getEffectiveRange: jest.fn(), getFilePath: jest.fn(), @@ -48,7 +46,6 @@ describe("CodeActionProvider", () => { beforeEach(() => { provider = new CodeActionProvider() - // Mock document mockDocument = { getText: jest.fn(), lineAt: jest.fn(), @@ -56,15 +53,9 @@ describe("CodeActionProvider", () => { uri: { fsPath: "/test/file.ts" }, } - // Mock range mockRange = new vscode.Range(0, 0, 0, 10) - // Mock context - mockContext = { - diagnostics: [], - } - - // Setup default EditorUtils mocks + mockContext = { diagnostics: [] } ;(EditorUtils.getEffectiveRange as jest.Mock).mockReturnValue({ range: mockRange, text: "test code", @@ -106,6 +97,7 @@ describe("CodeActionProvider", () => { it("should handle errors gracefully", () => { const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {}) + ;(EditorUtils.getEffectiveRange as jest.Mock).mockImplementation(() => { throw new Error("Test error") }) diff --git a/src/activate/handleTask.ts b/src/activate/handleTask.ts index 0f99380df58..6f9d35c26f2 100644 --- a/src/activate/handleTask.ts +++ b/src/activate/handleTask.ts @@ -1,9 +1,11 @@ import * as vscode from "vscode" -import { COMMAND_IDS } from "../core/CodeActionProvider" import { ClineProvider } from "../core/webview/ClineProvider" + import { t } from "../i18n" +import { COMMAND_IDS } from "./CodeActionProvider" + export const handleNewTask = async (params: { prompt?: string } | null | undefined) => { let prompt = params?.prompt diff --git a/src/activate/index.ts b/src/activate/index.ts index 2fff46a5f04..74f82af9d15 100644 --- a/src/activate/index.ts +++ b/src/activate/index.ts @@ -3,3 +3,4 @@ export { registerCommands } from "./registerCommands" export { registerCodeActions } from "./registerCodeActions" export { registerTerminalActions } from "./registerTerminalActions" export { registerPearListener } from "./registerPearListener" +export { CodeActionProvider } from "./CodeActionProvider" diff --git a/src/activate/registerCodeActions.ts b/src/activate/registerCodeActions.ts index b1c15f19e43..7af28076a78 100644 --- a/src/activate/registerCodeActions.ts +++ b/src/activate/registerCodeActions.ts @@ -1,9 +1,10 @@ import * as vscode from "vscode" -import { type CodeActionName, type CodeActionId, COMMAND_IDS } from "../core/CodeActionProvider" -import { EditorUtils } from "../core/EditorUtils" +import { EditorUtils } from "../integrations/editor/EditorUtils" import { ClineProvider } from "../core/webview/ClineProvider" +import { type CodeActionName, type CodeActionId, COMMAND_IDS } from "./CodeActionProvider" + export const registerCodeActions = (context: vscode.ExtensionContext) => { registerCodeAction(context, COMMAND_IDS.EXPLAIN, "EXPLAIN") registerCodeAction(context, COMMAND_IDS.FIX, "FIX") diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index d3a16c8b618..bb46d2e010f 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -116,6 +116,8 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt telemetryService.captureTitleButtonClicked("settings") visibleProvider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" }) + // Also explicitly post the visibility message to trigger scroll reliably + visibleProvider.postMessageToWebview({ type: "action", action: "didBecomeVisible" }) }, "roo-cline.historyButtonClicked": () => { const visibleProvider = getVisibleProviderOrLog(outputChannel) @@ -128,11 +130,6 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt visibleProvider.postMessageToWebview({ type: "action", action: "historyButtonClicked" }) }, - "roo-cline.helpButtonClicked": () => { - telemetryService.captureTitleButtonClicked("help") - - vscode.env.openExternal(vscode.Uri.parse("https://docs.roocode.com")) - }, "roo-cline.showHumanRelayDialog": (params: { requestId: string; promptText: string }) => { const panel = getPanel() @@ -217,10 +214,26 @@ export const openClineInNewTab = async ({ context, outputChannel }: Omit { + const panel = e.webviewPanel + if (panel.visible) { + panel.webview.postMessage({ type: "action", action: "didBecomeVisible" }) // Use the same message type as in SettingsView.tsx + } + }, + null, // First null is for `thisArgs` + context.subscriptions, // Register listener for disposal + ) + // Handle panel closing events. - newPanel.onDidDispose(() => { - setPanel(undefined, "tab") - }) + newPanel.onDidDispose( + () => { + setPanel(undefined, "tab") + }, + null, + context.subscriptions, // Also register dispose listener + ) // Lock the editor group so clicking on files doesn't open them over the panel. await delay(100) diff --git a/src/activate/registerPearListener.ts b/src/activate/registerPearListener.ts index 053a984ecb7..6e81634c337 100644 --- a/src/activate/registerPearListener.ts +++ b/src/activate/registerPearListener.ts @@ -69,7 +69,7 @@ export const registerPearListener = async (provider: ClineProvider) => { // Initialize with task - await provider.initClineWithTask(msg.plan, undefined, undefined, undefined, creatorModeConfig); + await provider.initClineWithTask(msg.plan, msg.images, undefined, undefined, creatorModeConfig); }); // If there's a creator event in the cache after the extensions were refreshed, we need to get it! exports.pearAPI.creatorMode.triggerCachedCreatorEvent(true); diff --git a/src/api/index.ts b/src/api/index.ts index 0ee6fdc1ca1..4d431ca66d8 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,7 +1,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import { BetaThinkingConfigParam } from "@anthropic-ai/sdk/resources/beta/messages" -import { ApiConfiguration, ModelInfo, ApiHandlerOptions } from "../shared/api" +import { ProviderSettings, ModelInfo, ApiHandlerOptions } from "../shared/api" import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "./providers/constants" import { GlamaHandler } from "./providers/glama" import { AnthropicHandler } from "./providers/anthropic" @@ -24,13 +24,16 @@ import { PearAIHandler } from "./providers/pearai/pearai" import { HumanRelayHandler } from "./providers/human-relay" import { FakeAIHandler } from "./providers/fake-ai" import { XAIHandler } from "./providers/xai" +import { GroqHandler } from "./providers/groq" +import { ChutesHandler } from "./providers/chutes" +import { LiteLLMHandler } from "./providers/litellm" export interface SingleCompletionHandler { completePrompt(prompt: string): Promise } export interface ApiHandler { - createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], cacheKey?: string): ApiStream + createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream getModel(): { id: string; info: ModelInfo } @@ -45,7 +48,7 @@ export interface ApiHandler { countTokens(content: Array): Promise } -export function buildApiHandler(configuration: ApiConfiguration): ApiHandler { +export function buildApiHandler(configuration: ProviderSettings): ApiHandler { const { apiProvider, ...options } = configuration switch (apiProvider) { @@ -91,6 +94,12 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler { return new PearAIHandler(options) case "xai": return new XAIHandler(options) + case "groq": + return new GroqHandler(options) + case "chutes": + return new ChutesHandler(options) + case "litellm": + return new LiteLLMHandler(options) default: return new AnthropicHandler(options) } diff --git a/src/api/providers/__tests__/chutes.test.ts b/src/api/providers/__tests__/chutes.test.ts new file mode 100644 index 00000000000..63af600be79 --- /dev/null +++ b/src/api/providers/__tests__/chutes.test.ts @@ -0,0 +1,141 @@ +// npx jest src/api/providers/__tests__/chutes.test.ts + +import OpenAI from "openai" +import { Anthropic } from "@anthropic-ai/sdk" + +import { ChutesModelId, chutesDefaultModelId, chutesModels } from "../../../shared/api" + +import { ChutesHandler } from "../chutes" + +jest.mock("openai", () => { + const createMock = jest.fn() + return jest.fn(() => ({ chat: { completions: { create: createMock } } })) +}) + +describe("ChutesHandler", () => { + let handler: ChutesHandler + let mockCreate: jest.Mock + + beforeEach(() => { + jest.clearAllMocks() + mockCreate = (OpenAI as unknown as jest.Mock)().chat.completions.create + handler = new ChutesHandler({ chutesApiKey: "test-chutes-api-key" }) + }) + + test("should use the correct Chutes base URL", () => { + new ChutesHandler({ chutesApiKey: "test-chutes-api-key" }) + expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ baseURL: "https://llm.chutes.ai/v1" })) + }) + + test("should use the provided API key", () => { + const chutesApiKey = "test-chutes-api-key" + new ChutesHandler({ chutesApiKey }) + expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ apiKey: chutesApiKey })) + }) + + test("should return default model when no model is specified", () => { + const model = handler.getModel() + expect(model.id).toBe(chutesDefaultModelId) + expect(model.info).toEqual(chutesModels[chutesDefaultModelId]) + }) + + test("should return specified model when valid model is provided", () => { + const testModelId: ChutesModelId = "deepseek-ai/DeepSeek-R1" + const handlerWithModel = new ChutesHandler({ apiModelId: testModelId, chutesApiKey: "test-chutes-api-key" }) + const model = handlerWithModel.getModel() + expect(model.id).toBe(testModelId) + expect(model.info).toEqual(chutesModels[testModelId]) + }) + + test("completePrompt method should return text from Chutes API", async () => { + const expectedResponse = "This is a test response from Chutes" + mockCreate.mockResolvedValueOnce({ choices: [{ message: { content: expectedResponse } }] }) + const result = await handler.completePrompt("test prompt") + expect(result).toBe(expectedResponse) + }) + + test("should handle errors in completePrompt", async () => { + const errorMessage = "Chutes API error" + mockCreate.mockRejectedValueOnce(new Error(errorMessage)) + await expect(handler.completePrompt("test prompt")).rejects.toThrow(`Chutes completion error: ${errorMessage}`) + }) + + test("createMessage should yield text content from stream", async () => { + const testContent = "This is test content from Chutes stream" + + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: jest + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: testContent } }] }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + + const stream = handler.createMessage("system prompt", []) + const firstChunk = await stream.next() + + expect(firstChunk.done).toBe(false) + expect(firstChunk.value).toEqual({ type: "text", text: testContent }) + }) + + test("createMessage should yield usage data from stream", async () => { + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: jest + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: {} }], usage: { prompt_tokens: 10, completion_tokens: 20 } }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + + const stream = handler.createMessage("system prompt", []) + const firstChunk = await stream.next() + + expect(firstChunk.done).toBe(false) + expect(firstChunk.value).toEqual({ type: "usage", inputTokens: 10, outputTokens: 20 }) + }) + + test("createMessage should pass correct parameters to Chutes client", async () => { + const modelId: ChutesModelId = "deepseek-ai/DeepSeek-R1" + const modelInfo = chutesModels[modelId] + const handlerWithModel = new ChutesHandler({ apiModelId: modelId, chutesApiKey: "test-chutes-api-key" }) + + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + async next() { + return { done: true } + }, + }), + } + }) + + const systemPrompt = "Test system prompt for Chutes" + const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Test message for Chutes" }] + + const messageGenerator = handlerWithModel.createMessage(systemPrompt, messages) + await messageGenerator.next() + + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + model: modelId, + max_tokens: modelInfo.maxTokens, + temperature: 0.5, + messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), + stream: true, + stream_options: { include_usage: true }, + }), + ) + }) +}) diff --git a/src/api/providers/__tests__/gemini.test.ts b/src/api/providers/__tests__/gemini.test.ts index af32b1bebbf..97c757f8fbd 100644 --- a/src/api/providers/__tests__/gemini.test.ts +++ b/src/api/providers/__tests__/gemini.test.ts @@ -219,7 +219,7 @@ describe("GeminiHandler", () => { mockInfo.cacheWritesPrice! * (cacheWriteTokens / 1_000_000) * (CACHE_TTL / 60) const expectedCost = expectedInputCost + expectedOutputCost + expectedCacheWriteCost - const cost = handler.calculateCost({ info: mockInfo, inputTokens, outputTokens, cacheWriteTokens }) + const cost = handler.calculateCost({ info: mockInfo, inputTokens, outputTokens }) expect(cost).toBeCloseTo(expectedCost) }) diff --git a/src/api/providers/__tests__/glama.test.ts b/src/api/providers/__tests__/glama.test.ts index c44debddff0..58d9b6ab670 100644 --- a/src/api/providers/__tests__/glama.test.ts +++ b/src/api/providers/__tests__/glama.test.ts @@ -6,7 +6,7 @@ import { GlamaHandler } from "../glama" import { ApiHandlerOptions } from "../../../shared/api" // Mock dependencies -jest.mock("../fetchers/cache", () => ({ +jest.mock("../fetchers/modelCache", () => ({ getModels: jest.fn().mockImplementation(() => { return Promise.resolve({ "anthropic/claude-3-7-sonnet": { diff --git a/src/api/providers/__tests__/groq.test.ts b/src/api/providers/__tests__/groq.test.ts new file mode 100644 index 00000000000..068f7248fdd --- /dev/null +++ b/src/api/providers/__tests__/groq.test.ts @@ -0,0 +1,141 @@ +// npx jest src/api/providers/__tests__/groq.test.ts + +import OpenAI from "openai" +import { Anthropic } from "@anthropic-ai/sdk" + +import { GroqModelId, groqDefaultModelId, groqModels } from "../../../shared/api" + +import { GroqHandler } from "../groq" + +jest.mock("openai", () => { + const createMock = jest.fn() + return jest.fn(() => ({ chat: { completions: { create: createMock } } })) +}) + +describe("GroqHandler", () => { + let handler: GroqHandler + let mockCreate: jest.Mock + + beforeEach(() => { + jest.clearAllMocks() + mockCreate = (OpenAI as unknown as jest.Mock)().chat.completions.create + handler = new GroqHandler({ groqApiKey: "test-groq-api-key" }) + }) + + test("should use the correct Groq base URL", () => { + new GroqHandler({ groqApiKey: "test-groq-api-key" }) + expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ baseURL: "https://api.groq.com/openai/v1" })) + }) + + test("should use the provided API key", () => { + const groqApiKey = "test-groq-api-key" + new GroqHandler({ groqApiKey }) + expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ apiKey: groqApiKey })) + }) + + test("should return default model when no model is specified", () => { + const model = handler.getModel() + expect(model.id).toBe(groqDefaultModelId) + expect(model.info).toEqual(groqModels[groqDefaultModelId]) + }) + + test("should return specified model when valid model is provided", () => { + const testModelId: GroqModelId = "llama-3.3-70b-versatile" + const handlerWithModel = new GroqHandler({ apiModelId: testModelId, groqApiKey: "test-groq-api-key" }) + const model = handlerWithModel.getModel() + expect(model.id).toBe(testModelId) + expect(model.info).toEqual(groqModels[testModelId]) + }) + + test("completePrompt method should return text from Groq API", async () => { + const expectedResponse = "This is a test response from Groq" + mockCreate.mockResolvedValueOnce({ choices: [{ message: { content: expectedResponse } }] }) + const result = await handler.completePrompt("test prompt") + expect(result).toBe(expectedResponse) + }) + + test("should handle errors in completePrompt", async () => { + const errorMessage = "Groq API error" + mockCreate.mockRejectedValueOnce(new Error(errorMessage)) + await expect(handler.completePrompt("test prompt")).rejects.toThrow(`Groq completion error: ${errorMessage}`) + }) + + test("createMessage should yield text content from stream", async () => { + const testContent = "This is test content from Groq stream" + + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: jest + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: testContent } }] }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + + const stream = handler.createMessage("system prompt", []) + const firstChunk = await stream.next() + + expect(firstChunk.done).toBe(false) + expect(firstChunk.value).toEqual({ type: "text", text: testContent }) + }) + + test("createMessage should yield usage data from stream", async () => { + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: jest + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: {} }], usage: { prompt_tokens: 10, completion_tokens: 20 } }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + + const stream = handler.createMessage("system prompt", []) + const firstChunk = await stream.next() + + expect(firstChunk.done).toBe(false) + expect(firstChunk.value).toEqual({ type: "usage", inputTokens: 10, outputTokens: 20 }) + }) + + test("createMessage should pass correct parameters to Groq client", async () => { + const modelId: GroqModelId = "llama-3.1-8b-instant" + const modelInfo = groqModels[modelId] + const handlerWithModel = new GroqHandler({ apiModelId: modelId, groqApiKey: "test-groq-api-key" }) + + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + async next() { + return { done: true } + }, + }), + } + }) + + const systemPrompt = "Test system prompt for Groq" + const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Test message for Groq" }] + + const messageGenerator = handlerWithModel.createMessage(systemPrompt, messages) + await messageGenerator.next() + + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + model: modelId, + max_tokens: modelInfo.maxTokens, + temperature: 0.5, + messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), + stream: true, + stream_options: { include_usage: true }, + }), + ) + }) +}) diff --git a/src/api/providers/__tests__/openrouter.test.ts b/src/api/providers/__tests__/openrouter.test.ts index 76d27840af9..2225bfae7a4 100644 --- a/src/api/providers/__tests__/openrouter.test.ts +++ b/src/api/providers/__tests__/openrouter.test.ts @@ -9,7 +9,7 @@ import { ApiHandlerOptions } from "../../../shared/api" // Mock dependencies jest.mock("openai") jest.mock("delay", () => jest.fn(() => Promise.resolve())) -jest.mock("../fetchers/cache", () => ({ +jest.mock("../fetchers/modelCache", () => ({ getModels: jest.fn().mockImplementation(() => { return Promise.resolve({ "anthropic/claude-3.7-sonnet": { @@ -78,7 +78,6 @@ describe("OpenRouterHandler", () => { topP: undefined, promptCache: { supported: true, - optional: false, }, }) }) diff --git a/src/api/providers/__tests__/requesty.test.ts b/src/api/providers/__tests__/requesty.test.ts index 2b28fd61588..b999a01947e 100644 --- a/src/api/providers/__tests__/requesty.test.ts +++ b/src/api/providers/__tests__/requesty.test.ts @@ -2,338 +2,227 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" -import { ApiHandlerOptions, ModelInfo } from "../../../shared/api" + import { RequestyHandler } from "../requesty" -import { convertToOpenAiMessages } from "../../transform/openai-format" -import { convertToR1Format } from "../../transform/r1-format" +import { ApiHandlerOptions } from "../../../shared/api" -// Mock OpenAI and transform functions jest.mock("openai") -jest.mock("../../transform/openai-format") -jest.mock("../../transform/r1-format") -jest.mock("../fetchers/cache", () => ({ - getModels: jest.fn().mockResolvedValue({ - "test-model": { - maxTokens: 8192, - contextWindow: 200_000, - supportsImages: true, - supportsComputerUse: true, - supportsPromptCache: true, - inputPrice: 3.0, - outputPrice: 15.0, - cacheWritesPrice: 3.75, - cacheReadsPrice: 0.3, - description: "Test model description", - }, +jest.mock("delay", () => jest.fn(() => Promise.resolve())) +jest.mock("../fetchers/modelCache", () => ({ + getModels: jest.fn().mockImplementation(() => { + return Promise.resolve({ + "coding/claude-3-7-sonnet": { + maxTokens: 8192, + contextWindow: 200000, + supportsImages: true, + supportsPromptCache: true, + supportsComputerUse: true, + inputPrice: 3, + outputPrice: 15, + cacheWritesPrice: 3.75, + cacheReadsPrice: 0.3, + description: "Claude 3.7 Sonnet", + }, + }) }), })) describe("RequestyHandler", () => { - let handler: RequestyHandler - let mockCreate: jest.Mock - - const modelInfo: ModelInfo = { - maxTokens: 8192, - contextWindow: 200_000, - supportsImages: true, - supportsComputerUse: true, - supportsPromptCache: true, - inputPrice: 3.0, - outputPrice: 15.0, - cacheWritesPrice: 3.75, - cacheReadsPrice: 0.3, - description: - "Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)", - } - - const defaultOptions: ApiHandlerOptions = { + const mockOptions: ApiHandlerOptions = { requestyApiKey: "test-key", - requestyModelId: "test-model", - openAiStreamingEnabled: true, - includeMaxTokens: true, // Add this to match the implementation + requestyModelId: "coding/claude-3-7-sonnet", } - beforeEach(() => { - // Clear mocks - jest.clearAllMocks() + beforeEach(() => jest.clearAllMocks()) + + it("initializes with correct options", () => { + const handler = new RequestyHandler(mockOptions) + expect(handler).toBeInstanceOf(RequestyHandler) + + expect(OpenAI).toHaveBeenCalledWith({ + baseURL: "https://router.requesty.ai/v1", + apiKey: mockOptions.requestyApiKey, + defaultHeaders: { + "HTTP-Referer": "https://github.com/RooVetGit/Roo-Cline", + "X-Title": "Roo Code", + }, + }) + }) + + describe("fetchModel", () => { + it("returns correct model info when options are provided", async () => { + const handler = new RequestyHandler(mockOptions) + const result = await handler.fetchModel() + + expect(result).toMatchObject({ + id: mockOptions.requestyModelId, + info: { + maxTokens: 8192, + contextWindow: 200000, + supportsImages: true, + supportsPromptCache: true, + supportsComputerUse: true, + inputPrice: 3, + outputPrice: 15, + cacheWritesPrice: 3.75, + cacheReadsPrice: 0.3, + description: "Claude 3.7 Sonnet", + }, + }) + }) + + it("returns default model info when options are not provided", async () => { + const handler = new RequestyHandler({}) + const result = await handler.fetchModel() + + expect(result).toMatchObject({ + id: mockOptions.requestyModelId, + info: { + maxTokens: 8192, + contextWindow: 200000, + supportsImages: true, + supportsPromptCache: true, + supportsComputerUse: true, + inputPrice: 3, + outputPrice: 15, + cacheWritesPrice: 3.75, + cacheReadsPrice: 0.3, + description: "Claude 3.7 Sonnet", + }, + }) + }) + }) - // Setup mock create function that preserves params - mockCreate = jest.fn().mockImplementation((_params) => { - return { - [Symbol.asyncIterator]: async function* () { + describe("createMessage", () => { + it("generates correct stream chunks", async () => { + const handler = new RequestyHandler(mockOptions) + + const mockStream = { + async *[Symbol.asyncIterator]() { yield { - choices: [{ delta: { content: "Hello" } }], + id: mockOptions.requestyModelId, + choices: [{ delta: { content: "test response" } }], } yield { - choices: [{ delta: { content: " world" } }], + id: "test-id", + choices: [{ delta: {} }], usage: { - prompt_tokens: 30, - completion_tokens: 10, + prompt_tokens: 10, + completion_tokens: 20, prompt_tokens_details: { - cached_tokens: 15, caching_tokens: 5, + cached_tokens: 2, }, }, } }, } - }) - // Mock OpenAI constructor - ;(OpenAI as jest.MockedClass).mockImplementation( - () => - ({ - chat: { - completions: { - create: (params: any) => { - // Store params for verification - const result = mockCreate(params) - // Make params available for test assertions - ;(result as any).params = params - return result - }, - }, - }, - }) as unknown as OpenAI, - ) + // Mock OpenAI chat.completions.create + const mockCreate = jest.fn().mockResolvedValue(mockStream) - // Mock transform functions - ;(convertToOpenAiMessages as jest.Mock).mockImplementation((messages) => messages) - ;(convertToR1Format as jest.Mock).mockImplementation((messages) => messages) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any - // Create handler instance - handler = new RequestyHandler(defaultOptions) - }) + const systemPrompt = "test system prompt" + const messages: Anthropic.Messages.MessageParam[] = [{ role: "user" as const, content: "test message" }] - describe("constructor", () => { - it("should initialize with correct options", () => { - expect(OpenAI).toHaveBeenCalledWith({ - baseURL: "https://router.requesty.ai/v1", - apiKey: defaultOptions.requestyApiKey, - defaultHeaders: { - "HTTP-Referer": "https://github.com/RooVetGit/Roo-Cline", - "X-Title": "Agent", - }, - }) - }) - }) + const generator = handler.createMessage(systemPrompt, messages) + const chunks = [] - describe("createMessage", () => { - const systemPrompt = "You are a helpful assistant" - const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }] + for await (const chunk of generator) { + chunks.push(chunk) + } - describe("with streaming enabled", () => { - beforeEach(() => { - const stream = { - [Symbol.asyncIterator]: async function* () { - yield { - choices: [{ delta: { content: "Hello" } }], - } - yield { - choices: [{ delta: { content: " world" } }], - usage: { - prompt_tokens: 30, - completion_tokens: 10, - prompt_tokens_details: { - cached_tokens: 15, - caching_tokens: 5, - }, - }, - } - }, - } - mockCreate.mockResolvedValue(stream) + // Verify stream chunks + expect(chunks).toHaveLength(2) // One text chunk and one usage chunk + expect(chunks[0]).toEqual({ type: "text", text: "test response" }) + expect(chunks[1]).toEqual({ + type: "usage", + inputTokens: 10, + outputTokens: 20, + cacheWriteTokens: 5, + cacheReadTokens: 2, + totalCost: expect.any(Number), }) - it("should handle streaming response correctly", async () => { - const stream = handler.createMessage(systemPrompt, messages) - const results = [] - - for await (const chunk of stream) { - results.push(chunk) - } - - expect(results).toEqual([ - { type: "text", text: "Hello" }, - { type: "text", text: " world" }, - { - type: "usage", - inputTokens: 30, - outputTokens: 10, - cacheWriteTokens: 5, - cacheReadTokens: 15, - totalCost: 0.00020325000000000003, // (10 * 3 / 1,000,000) + (5 * 3.75 / 1,000,000) + (15 * 0.3 / 1,000,000) + (10 * 15 / 1,000,000) (the ...0 is a fp skew) - }, - ]) - - // Get the actual params that were passed - const calls = mockCreate.mock.calls - expect(calls.length).toBe(1) - const actualParams = calls[0][0] - - expect(actualParams).toEqual({ - model: defaultOptions.requestyModelId, - temperature: 0, + // Verify OpenAI client was called with correct parameters + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + max_tokens: undefined, messages: [ { role: "system", - content: [ - { - cache_control: { - type: "ephemeral", - }, - text: systemPrompt, - type: "text", - }, - ], + content: "test system prompt", }, { role: "user", - content: [ - { - cache_control: { - type: "ephemeral", - }, - text: "Hello", - type: "text", - }, - ], + content: "test message", }, ], + model: "coding/claude-3-7-sonnet", stream: true, stream_options: { include_usage: true }, - max_tokens: modelInfo.maxTokens, - }) - }) - - it("should not include max_tokens when includeMaxTokens is false", async () => { - handler = new RequestyHandler({ - ...defaultOptions, - includeMaxTokens: false, - }) - - await handler.createMessage(systemPrompt, messages).next() - - expect(mockCreate).toHaveBeenCalledWith( - expect.not.objectContaining({ - max_tokens: expect.any(Number), - }), - ) - }) - - it("should handle deepseek-reasoner model format", async () => { - handler = new RequestyHandler({ - ...defaultOptions, - requestyModelId: "deepseek-reasoner", - }) - - await handler.createMessage(systemPrompt, messages).next() - - expect(convertToR1Format).toHaveBeenCalledWith([{ role: "user", content: systemPrompt }, ...messages]) - }) + temperature: undefined, + }), + ) }) - describe("with streaming disabled", () => { - beforeEach(() => { - handler = new RequestyHandler({ - ...defaultOptions, - openAiStreamingEnabled: false, - }) - - mockCreate.mockResolvedValue({ - choices: [{ message: { content: "Hello world" } }], - usage: { - prompt_tokens: 10, - completion_tokens: 5, - }, - }) - }) - - it("should handle non-streaming response correctly", async () => { - const stream = handler.createMessage(systemPrompt, messages) - const results = [] + it("handles API errors", async () => { + const handler = new RequestyHandler(mockOptions) + const mockError = new Error("API Error") + const mockCreate = jest.fn().mockRejectedValue(mockError) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any - for await (const chunk of stream) { - results.push(chunk) - } - - expect(results).toEqual([ - { type: "text", text: "Hello world" }, - { - type: "usage", - inputTokens: 10, - outputTokens: 5, - cacheWriteTokens: 0, - cacheReadTokens: 0, - totalCost: 0.000105, // (10 * 3 / 1,000,000) + (5 * 15 / 1,000,000) - }, - ]) - - expect(mockCreate).toHaveBeenCalledWith({ - model: defaultOptions.requestyModelId, - messages: [ - { role: "user", content: systemPrompt }, - { - role: "user", - content: [ - { - cache_control: { - type: "ephemeral", - }, - text: "Hello", - type: "text", - }, - ], - }, - ], - }) - }) + const generator = handler.createMessage("test", []) + await expect(generator.next()).rejects.toThrow("API Error") }) }) - describe("getModel", () => { - it("should return correct model information", () => { - const result = handler.getModel() - expect(result).toEqual({ - id: defaultOptions.requestyModelId, - info: modelInfo, - }) - }) + describe("completePrompt", () => { + it("returns correct response", async () => { + const handler = new RequestyHandler(mockOptions) + const mockResponse = { choices: [{ message: { content: "test completion" } }] } - it("should use sane defaults when no model info provided", () => { - handler = new RequestyHandler(defaultOptions) - const result = handler.getModel() + const mockCreate = jest.fn().mockResolvedValue(mockResponse) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any - expect(result).toEqual({ - id: defaultOptions.requestyModelId, - info: modelInfo, - }) - }) - }) + const result = await handler.completePrompt("test prompt") - describe("completePrompt", () => { - beforeEach(() => { - mockCreate.mockResolvedValue({ - choices: [{ message: { content: "Completed response" } }], - }) - }) + expect(result).toBe("test completion") - it("should complete prompt successfully", async () => { - const result = await handler.completePrompt("Test prompt") - expect(result).toBe("Completed response") expect(mockCreate).toHaveBeenCalledWith({ - model: defaultOptions.requestyModelId, - messages: [{ role: "user", content: "Test prompt" }], + model: mockOptions.requestyModelId, + max_tokens: undefined, + messages: [{ role: "system", content: "test prompt" }], + temperature: undefined, }) }) - it("should handle errors correctly", async () => { - const errorMessage = "API error" - mockCreate.mockRejectedValue(new Error(errorMessage)) + it("handles API errors", async () => { + const handler = new RequestyHandler(mockOptions) + const mockError = new Error("API Error") + const mockCreate = jest.fn().mockRejectedValue(mockError) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any - await expect(handler.completePrompt("Test prompt")).rejects.toThrow( - `OpenAI completion error: ${errorMessage}`, - ) + await expect(handler.completePrompt("test prompt")).rejects.toThrow("API Error") + }) + + it("handles unexpected errors", async () => { + const handler = new RequestyHandler(mockOptions) + const mockCreate = jest.fn().mockRejectedValue(new Error("Unexpected error")) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any + + await expect(handler.completePrompt("test prompt")).rejects.toThrow("Unexpected error") }) }) }) diff --git a/src/api/providers/__tests__/unbound.test.ts b/src/api/providers/__tests__/unbound.test.ts index 3ceacf4d2e5..c01a2b53676 100644 --- a/src/api/providers/__tests__/unbound.test.ts +++ b/src/api/providers/__tests__/unbound.test.ts @@ -7,7 +7,7 @@ import { ApiHandlerOptions } from "../../../shared/api" import { UnboundHandler } from "../unbound" // Mock dependencies -jest.mock("../fetchers/cache", () => ({ +jest.mock("../fetchers/modelCache", () => ({ getModels: jest.fn().mockImplementation(() => { return Promise.resolve({ "anthropic/claude-3-5-sonnet-20241022": { diff --git a/src/api/providers/__tests__/vertex.test.ts b/src/api/providers/__tests__/vertex.test.ts index b15e8842c7c..99178c9ab7f 100644 --- a/src/api/providers/__tests__/vertex.test.ts +++ b/src/api/providers/__tests__/vertex.test.ts @@ -56,11 +56,7 @@ describe("VertexHandler", () => { yield { type: "usage", inputTokens: 0, outputTokens: 5 } }) - const mockCacheKey = "cacheKey" - // Since we're directly mocking createMessage, we don't need to spy on it - // We just need to call it and verify the results - - const stream = handler.createMessage(systemPrompt, mockMessages, mockCacheKey) + const stream = handler.createMessage(systemPrompt, mockMessages) const chunks: ApiStreamChunk[] = [] diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts new file mode 100644 index 00000000000..82eeb830331 --- /dev/null +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -0,0 +1,129 @@ +import { Anthropic } from "@anthropic-ai/sdk" +import OpenAI from "openai" + +import { ApiHandlerOptions, ModelInfo } from "../../shared/api" +import { ApiStream } from "../transform/stream" +import { convertToOpenAiMessages } from "../transform/openai-format" + +import { SingleCompletionHandler } from "../index" +import { DEFAULT_HEADERS } from "./constants" +import { BaseProvider } from "./base-provider" + +type BaseOpenAiCompatibleProviderOptions = ApiHandlerOptions & { + providerName: string + baseURL: string + defaultProviderModelId: ModelName + providerModels: Record + defaultTemperature?: number +} + +export abstract class BaseOpenAiCompatibleProvider + extends BaseProvider + implements SingleCompletionHandler +{ + protected readonly providerName: string + protected readonly baseURL: string + protected readonly defaultTemperature: number + protected readonly defaultProviderModelId: ModelName + protected readonly providerModels: Record + + protected readonly options: ApiHandlerOptions + + private client: OpenAI + + constructor({ + providerName, + baseURL, + defaultProviderModelId, + providerModels, + defaultTemperature, + ...options + }: BaseOpenAiCompatibleProviderOptions) { + super() + + this.providerName = providerName + this.baseURL = baseURL + this.defaultProviderModelId = defaultProviderModelId + this.providerModels = providerModels + this.defaultTemperature = defaultTemperature ?? 0 + + this.options = options + + if (!this.options.apiKey) { + throw new Error("API key is required") + } + + this.client = new OpenAI({ + baseURL, + apiKey: this.options.apiKey, + defaultHeaders: DEFAULT_HEADERS, + }) + } + + override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + const { + id: model, + info: { maxTokens: max_tokens }, + } = this.getModel() + + const temperature = this.options.modelTemperature ?? this.defaultTemperature + + const params: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { + model, + max_tokens, + temperature, + messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)], + stream: true, + stream_options: { include_usage: true }, + } + + const stream = await this.client.chat.completions.create(params) + + for await (const chunk of stream) { + const delta = chunk.choices[0]?.delta + + if (delta?.content) { + yield { + type: "text", + text: delta.content, + } + } + + if (chunk.usage) { + yield { + type: "usage", + inputTokens: chunk.usage.prompt_tokens || 0, + outputTokens: chunk.usage.completion_tokens || 0, + } + } + } + } + + async completePrompt(prompt: string): Promise { + const { id: modelId } = this.getModel() + + try { + const response = await this.client.chat.completions.create({ + model: modelId, + messages: [{ role: "user", content: prompt }], + }) + + return response.choices[0]?.message.content || "" + } catch (error) { + if (error instanceof Error) { + throw new Error(`${this.providerName} completion error: ${error.message}`) + } + + throw error + } + } + + override getModel() { + const id = + this.options.apiModelId && this.options.apiModelId in this.providerModels + ? (this.options.apiModelId as ModelName) + : this.defaultProviderModelId + + return { id, info: this.providerModels[id] } + } +} diff --git a/src/api/providers/chutes.ts b/src/api/providers/chutes.ts new file mode 100644 index 00000000000..6f7481f1809 --- /dev/null +++ b/src/api/providers/chutes.ts @@ -0,0 +1,17 @@ +import { ApiHandlerOptions, ChutesModelId, chutesDefaultModelId, chutesModels } from "../../shared/api" + +import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider" + +export class ChutesHandler extends BaseOpenAiCompatibleProvider { + constructor(options: ApiHandlerOptions) { + super({ + ...options, + providerName: "Chutes", + baseURL: "https://llm.chutes.ai/v1", + apiKey: options.chutesApiKey, + defaultProviderModelId: chutesDefaultModelId, + providerModels: chutesModels, + defaultTemperature: 0.5, + }) + } +} diff --git a/src/api/providers/fetchers/__tests__/fixtures/openrouter-model-endpoints.json b/src/api/providers/fetchers/__tests__/fixtures/openrouter-model-endpoints.json new file mode 100644 index 00000000000..10ea52f07fd --- /dev/null +++ b/src/api/providers/fetchers/__tests__/fixtures/openrouter-model-endpoints.json @@ -0,0 +1,25 @@ +[ + { + "scope": "https://openrouter.ai:443", + "method": "GET", + "path": "/api/v1/models/google/gemini-2.5-pro-preview/endpoints", + "body": "", + "status": 200, + "response": [ + "31441d002056aa5ad5de6cfba09eb44cd983cf558aa50307224fd48d88f0c0d12137eda7bef1c435891ecc325645bf9d4794cd227137c069a7450a3f6ea3541aeacce9727170159a489e4b07a179ae738dc1a983bd860cb018631c277e3ab29720d5dea2ad528e551ef3c67c0e83e03cc3e22da9c6d2dbbb03ed2d5afa96237dbbe0d4e5e379806d0ef657edc161db2c0d863cfc7525951860c1af95425fdef6f1e177a1a24eb98a9b4ab75cb9acf4e63df938f044074a6c06dac44cda2750e3aa6e1246437d1cde032d10d0fceac4d20b07958df4a4aeec4affaa012d9b3eb5d0e3c33fdd4ad849181f1ffe53efd2b0f7f70b17431cdc7a92309228d5154e736588069b1ce7714bce6952e85c744b1cb672c175e424fda500d2300b1b3041bffe4209e02917760c1a225f6c218da952e14c3eaba01868e2fc07a68969cda1df7a9777e56ff7021bc945ab34b99e29c5222ab6214868114c9f3ebfc91c1c358cbac63aba3c18cabc99b8570923ed7b493445434205c506e4261983e7a03ac145e5e4177400cabf2a713a933092e58c0b18a4ecdf48b9d73933ec3534ee38c815670864c1a091d593757a991836ccd364e0e3e026d14b58285fe813f16ee4eaa5f285b20969d68ece56b8c01e61f98b7837320c3632314e0ce2acf4b627b7061c86ca07350aecd135c00ba71b0a08efaa5e567b2d0cbc9adc95fbb8146c53ef1fb6072b8394a59730c25e23e5e893c2a25ed4755dd70db7e0d3c42101aeda3430c89cb7df048b5a2990a64ddbac6070ceebeefc16f4f805e51cdcd44502b278439ab5eb5dbfe52eb31b84c8552f1b9aaaf32ccab7a459896918a4f4096b035bdf1a6cccc99db59ac1e0d7ec82ca95d307726386bbe8b4243aff7b14d855db2e5b0ad032c82ac88aecad09dd4eab813d6282a8dd0d947de2ecb0656ea03175e91d885361ba221b03605034261814e6c1c060c0125d58114a23c9334aa543079846052706459dce45f590e0f827bf794f3f751e24c224c06e3106cccf5c5dea93db5b0303" + ], + "rawHeaders": { + "access-control-allow-origin": "*", + "cache-control": "s-maxage=300, stale-while-revalidate=600", + "cf-ray": "93ed496b8e0a0fb1-LAX", + "connection": "close", + "content-encoding": "br", + "content-type": "application/json", + "date": "Mon, 12 May 2025 22:17:32 GMT", + "server": "cloudflare", + "transfer-encoding": "chunked", + "vary": "Accept-Encoding" + }, + "responseIsBinary": false + } +] diff --git a/src/api/providers/fetchers/__tests__/fixtures/openrouter-models.json b/src/api/providers/fetchers/__tests__/fixtures/openrouter-models.json index a8fd47fe046..60c7556bc91 100644 --- a/src/api/providers/fetchers/__tests__/fixtures/openrouter-models.json +++ b/src/api/providers/fetchers/__tests__/fixtures/openrouter-models.json @@ -6,16 +6,16 @@ "body": "", "status": 200, "response": [ - "6300f8ffc388d4a41e008dd0e173de57a9daffe9da8597a45a0c84c420fe485ab2566b5256f426bd929b4003e8afc10c3cdd60b05ff0e1fa53ba5fecffff7ed9f787497a6287a87a84dce79e231e891710452224aa9288aa548b74ff39f7d64b755525bda692a14a6360a0995c48d020283236e921fa20cce8312a30086ad62842e1c6cb511933fa717b28561ad2720ae8c45ce6afb3792340662aa6a159b37de5a60744910068fb7bf097a846c568f1eb9788eb6811f55c052fbed1b31ed9d46242b668025114470e7b8a16518ef087b780cdf21a2e1ee03e839326109d467134cff2b5a98e16d9ac9c64e76596a67154cf80ffd31a94bd8b16d166796d2e1ecc7d062c8030785133fff22cd530c3f5bbc029f8062e888607a257739f41116acd3554395502f281c0f21a94b007f5c0fd10fc8e3eb448ef6bb2dfbefeb7c0b4138bf18e1c898077305aee027b84adf5d52bd5a07ee04aa0e6aedf2de43a7415bb165805041bd2230cc1376c2981d5c856c13b9847bd0eabe9e73550bc63d74233fd3f0d54f62e065660a7d4065412c8b2f415b435825875173e8e7634053cbc7d841a1585b4e18a5dc5247dedc257131c5a7b846a0ca85443dbf3552bf1dbd106755e83965bd79353903e64137814c957038114d9098806efdad0e663a87cfdb8175d0dd334addb88b73b762d5438347cbf87496231c5a393de34762d68fc46c0373004df0f2a92be6b028906ae946a6007f759f2ec9e5d54b02784e8201991cd3544d565c03be830f4d0b3722bd5b9604baeea7a0caf1270dd54460ce0ba2adf0fa4acbc23a8026eb340555d55d5596bed681a549457a9c8ec1419c3b048033604f799c966b329b003ed3b17f4a481abcacb06dbb853f269a4c0140ca9baebbc2460095fb125e080b734a32d982f84496f1af0a8d1e0d40a084d735cacf1a1a7001b7f09e51e142b553a06ead7241680341eb2d04c5bb4248c4250a190c45015d80faa66d742c76d6744f195046adf4785cfcf58758002965ab431f454738536061f0047f5cef77e1428bb212f4914478332f9a02f259b6c205a64d3625ea67114cccf42b4f812f5be46cb7a8c1691d241cddf2b1d348a2376c3a82f9f28629268f1ebfe52467f8c233fea6151f5afe4f833856811e91cba29451c7c065ef43850b4886aa241885e4dc8a23fc7d110b862d7468b2f51c4ec8a16513a283dfd600991cad2288e029594603ed2cf3db6f4bc774fdb9769e5ccc9a862f4128ff066f99fe348fdf092721d5ab4f822b0293d1e5e42e1bd0cdeb2122ddc686d1cb1bcf4be261d5f255a346885fe1c47038597700e79b1dcb37ea7fdcf31727f80adf7ada5b3967a766cf2646286e00dd65d83f6262d4c3e0171efea0ba9055cade7813c99c06df070fbc785e3bb5a9ee7659e4e5ddf4fb340978182cb89a292f18dd18e0c069c2610e2a901bd5a8f38e9aa88453af922605aa7544c4eb9e14a6f557d7bbf6e02dfbefe9f76ec5ed9b5dfbefe3f6815c8e1d6027a3d140a3f0ea5052403c18f6d97420c25813d6b07ff59aaa110d0b83a06a0cd8d6f181d08819d344ce4682d0184db28a07e30ca14900cb0dd3a5882306756e507d170103583c58a60f0c2caa90902bd0e82f56619c821d4f6f7c3d87a0c750c811a4b95b26b81b4d64113486a0c8fa881a42185ae86c9035b3aa0bdec2a2bfc654bcbf964365d7bdfe9d008f59e95ed8a8ba3862dc100c5c0c6d189f42281ee1727699aa669a60c24030601bc3bc60c0aaea8244d27d9141018ecd65161d5d14b20ac0311a75375b43e7466f681959084c74492e3d9994e26c564fd75d36eacfbb3d6f6e673668a7c4bffbe391ebf7fbad82ce06abd815f3228f2d56e57cbf3bccccaf97257eb8df9253345be32699995d71aa77522478b08ce79b5de98d214f92a862df7ba66e303d444034a94a28dc1faf60945f1d69acce2798888d23c0290e05bad61194e027450727500749358e38af4846974eedbd77f2be7a70fe5d5d86d3974359bb8d6801cf62c04fae699f4bcf5dbd77f63a52e24a038e81f1061c088d2006c456f7c3f6020a5d0fa295b14120421e97131fc92c1e4cbd7b747d87a2f2a6c0d14060a32690c24e1181d04677bb46400944c314529ddb859a002d30d6b8cdd5b9406811d23dfab05b3049879ac6f1a0a742e53f90e8bfa1a1fa006d985c6871e941e1c0c89c7503f28f7fc398d8847e151ab624b4eb982bd0faf8df5fb448180b38324c8d27941ea4fe764c7083fe3fdcd690c3f3cbcbf01f5de4285d632dd1dd2b023d3da1f20cd3e77a4d0703b06b259687c00615cedecc6f3e404ae2b2dc1412dab5048a0b0e532762dd454c6d834548a8f6a4474fab0eaa2c03b43a132cdfa672af2d974bebae0f7da518810761bbb18616dba8342c6fd2857632f572f333dcf260dca15ba136d6dbbeb9dbce9d89190591818e73b053f9033ad26163793e1580e253aa6f23581bbc02abedca10850b232237ab40445d9db4056f9b9bd836cf208ed69ceb69d712d91d163b85b91e9087747bb15d6ee727b1468c6a01d0508d46898aafd3ac8c95384921802fd13595bac8d41dcab8af86cd9b6793a5464518f467fcde1b2c11830283758a934402b234548520f02e220667293d9b5d8ed55f00c2c18ae1eae6e1f4de919a23801ca3f14262df2d2e254a5fe475bbab79d2979620ab5e3d0ed0a96d3406351ba6d72abe9d4de5a946efbfa66e5743aa32ec879b43f6cf6dafbf0daf920eadd110307811b6833195392a08268951976f01ca5fa6fc57364549911e8b4a6acbad05763f5c6cfeed9dd78a545499461ef1c114f6539b003dd7b2f615616404bc84ccb7a67780ab9bd8c5e9b180defe24424dc49cab534569676f4aaeecbe07b47805e7fa5bca304ae1b38fab102f930cfd8a3ca61ec8013bf71b97604cfd162b7e4730432360d1f4e637056865006e75a74182c57acf608b8f3cc3461de49a0c8674042cb3e7aa8c4c4d828e0e49c3a6145bca3ee78f1704815fb0b55e7bd1c1dc1353e59b8fedaaa2ea87b6a470ed855a3b8ba3a6e3bc9510f1a18d9d3d8555904b3da630cca20bc0b186b0c74125fa6c743a8c1c7c4de6a798e60663c851850c0d5ec6f3d8df36a4750fb6aecc92999fb844f3ad5411667677e2017fca81412e4b3da5772360a1922577396b0517f7b7ed3e3e1e9a0697c30e6de8ff3a9a14c98eac720be75fe3d376976e8693addfe03a5d3ec3c4f0236b1cd3b92ab286e9592830e3851865f8e729a32fc0e514cfecce10772c867be34eb291db75d22b0f2fb81dcf27a01be840d3b86ef3f61db00bb38cfcb3ccbe9db75d0e95a68eb4a56b0f7c0f5a0c0af1f3bc01f4fce82e39f02a988ace6a5a1272910cadee8eeb8ed3897a377c029b2619b2ad516433f8d66747a539c7ba3d891b09e064563a8bca8a90d4dbdfa3429df9cb29791114167b8be107f2370348ce7ca9c80f9211e4778e8da46c9e7b4b3851db9ba3c04c6dbeb34d54d76af32bc236863c97ccbebcd259c9c9f2793df09b2f8f6a89d77a701ec7af8f1d258ea5eec0da02e52b611f2b2e7aba922111fc01742ed4357438357f402154861d599115ee1779dec7b72aeeac2dcf17b82257bb49602087fa678140da343c75b56a1d3ca682a67070f8f971bcf845dfd05a74bfbc387134ada24860d6af781453186cd66f3741acba88c806a56e77796057ec1b4a03dc29eac3532b2d20678790cd47e15c3a82015390cec055a3e13d9b6d9c447f02f79d2c55415d80d3f2c81c70eddabfc16a34505e28be5be84c4d92b94c0b7e3c1cf41f0c30496d1f00a1d541db28b271d12fbb54621370e539a69deeec8fb0a39920675f7ecd8f97a0e8bc76f5fffad3d313976d090971a58d241071d95fc355e8e855b1ecaabedc5a1c35b9265abe7a12ce379990654e5af239bcb950f76d8f96ce2d6659127a1ff41f611c7f1c86918817eb402de472940fdc04539a1ef6dbe4057a8522af83137091e5434e57e4fa119ad5927050a1b06284eae452a80e0688f1ea41a1f82733f8ec827a8489a636c8032822a58ef452b1e086d4185133c54cc155ad887e68f6e702ad8cbde995d5f8261c83c09b1e3080625d2d2d48561d1c60bec471daf143782c313ea1416d8a4940ecea2ada6614672989304e46ee5b0faf9fd3b60aec4d89d4883fb6f86041e48611ca0a3400b88cd58de169e29aafefc60efa4b926f918c1c7afc56cfaf15322ffa000684c51185621027b41d2f3f2075ac3a2efa3c4437b4ac78206f9ecc2d771971b6bb1c72229cc2ce5bce779e8581096d70bf8fde6f990c3da628f502405ccd21590e0279e4dcaf9441894a43bc85a15112eebe37f603f541daac1868e23ada31388a39b7325cb6bf00e36a4a82dc130fa6694322b731d3743788b188a3768d0c79f497aa7e8f5915a771934db9c2bf1f5b83d0a5cb88840502d258347c44774798496a1a4e703f846e137b863a17abc61f1d20f6f1fed76f6e2f6bd763c105acf63a9371b88056a78e830d0d5ed23e54fac3d54c7be254a0c0d3e2c784b83c5237658e2dd8e82845b26be33a9871d4fb2fabb2981ddaa71ec1d67109695ee409694c908fb6477f308d7ec14c0a942d8b939bfea650bf820d40a0665428a5d2b5b9265b06596aee497294b080cc8cc9c55515e7da253bccefb6b87a9a9cec5566dc5de5b42cdd21f96b079841566b0e7e5fae75f9690a5a9a5bd807b7c1d6bf4e42508593e7f3796827353dbc6acd3b5017b48efbd76f6e8934572540be706a573efdd77d76ebddeccf6b287ab35bab6b8078506bc0f3a52ba20351f291615b0a782ef3d12d46558428f89c179a8b251b20c2cba2497abab4b096296e1c67c11e8c06a294ac1725cd5a1c6e9c7a3805c937bf9e388e5a43f2ccf2e6fbc8e11058c24b84f1ed657d7cdc088bced8a884f73e7dadbbd6ae2c3fcb42777f6694f2e4f26a6f2350533db9a59acb2e9d2bb3db905dcddf9b1f0e6d560b6826b877b98b928e7f314db74e261a6d9cc8c3730cda6d90aecb7135cf207fb65d551913960bb76aa173d2e88685809a7641e502c55bc1d5b68f800aa85abca8d9c7634c656d6e9c7b53f5208168ae4b5a727a7e8e40777ef6f2f6378d8f3d5fa2986fbcdc38d0f7ddcd67775b7848a2e8ad30d640ade43765049199840bc47f7cf787fa344dd03e8a001076fe039e4d5f2ae668493fa520c950fc3289e26e5c7a0785ab8a618e4e8b423e5ca5ba0d5a6e8a0f33f8616f700484179223cce49fd7614b5b9bc7f4a31f69904cf21aa9e118098f217517b1a71aac5fea5060c15627aaad34212c1ee566a0daa49bd007eb72bd35f087a11386b73aacffd8ba473f3d734587fecc96902f79e2ba811c2b5b423580e5875047992c2f693b6055375c6eff6e422d27f2c3930881bb7bae13242b07df16b389d8b6907356592617ed0f3e1461b4b38c974921573d5263d807895bd1a8bad743cb4a4e47647945ada5e591ef1984068cd49850ba160d31257532607fd34c711629ddc31471e7ab6d6adda8187750a68eea712fb3ad133d2188693666b1ae5394e266532fd1d55721be003056e98ea53ff8e7cca63c5e4c227f35952fe0eaedf5eeed0a6df353d06821605ddd42476ad4a92cd54384a19649f4489ce28a9b96924b619fd10c832a94dfd005e900f54a165b668be30e3cb13222731142477022bb961868a2486eb8bcbb45aa6babd97c4b5c359e891cad92f1208a481698796f1fdb19b3d55cd1a1cf1abdbc7b7446f6ffaf9db5863c8f1694f65f256cd5654f3c5fc8cc1e08e6c022a828f301a4f55166dd8d87ccfb511db40aead5093e55d0585956f5ad427a01ad61dcfa320e356149de256ea183f198d22a02b16350c44cd56e36741a97c2081729264bf2bbb7558acb9688756622826c9fc4e0b6f46abfca6436bc9b5d07ffbbcec02357f3f1977b01453a5f37bcf8c5756a5182ab3abc8926917f24bae2924f0c3066f8fadf56a0094b9a0b437de7122699b14ab915bb5ff54464d32ce3ad81e9ee1063d15d53b660230d73f27d8a9b475300915d1af5ff36cdada7faa644f2fa743e7fd9b7bb8c383673b3dcfbcf521a100e595aea977dbe1be7dae19c3d5ede3b7afff5c261938745e0252d2a02849c17f79d5110e2408975168b021219d067cbeb11c9d02ab8498002dd4915640881d1fb4a41f0eab9883baf8fd09f3748ecd66fd14c3244d8aabbaaf6eef968e37387f41a3550dd684f3e96f5fff951910a57007ba09e179ec590ac0943fa6a40cd20d7d0a930412e2ea74b54cec28da662286fed73c2dbd703568bfcdfb7f9790a5513b0ac867d652dfe3cb6c4bd20797f7c82faf17b07e7f1838dae7cc6591e7a13b3fc4c0cbae689de977532d81987aca1e05d646e3d78ea2e78daf09ae4ecf5650886548169d592fab1dc16df0be31b76cc9e4fde8c9d37495ab87c9186008f2021828b9bd1357a3c5608fa4611baaf68554c0c7fab1ed74506d414007ba32ab42e5fb6194bdb7a1a5881ac138aaf51011ed570b75a172acbd31f559fd9ba7e0fcfe98caf47c0ac2ed1b9e07e16bc556f59391a5b734c992f832bac3be486e9743db04aa87e0e5acf23511371628772d23de72cd7a6c46def261f9dbe06581cd1db75ee306f1b92678789cab6dbdd46c3e2bbd2507436f99196cc34c51e106d26aceb1c6cd124c8fa8e935a9e72eb9d705901e43c6d613b0d2057b89d26c59e16eedef7b0af11fc913c0fefc5facdbcbb78f60791b301c97dc158265e4b34ffb4fa6c8b7e6aa0b34610866b7bea9b996c1f2f27a0177fbbbf2da53b91fee61f77fc1e56432399f6b3f5277fbbb1a5beadc10733fdc9b1d8233aec86164e30d14a3885533f5dfedc99d7547c8395e09137c0f5234adb562fa4c56e3c4ab18439106afcb8ae44fe723b8bfdde021b46b6229e5d0ab83d31feae8eb507ecb09d6c8924c4e60740cae70370db647380fe91cbb5656a019b5a4ebffea060a50335adf8e043a06472e1857035981f64c7129a091f48258856ce328d0b4c7a433edcf6bde3f98bbf5fbfbe577e01dccdf812b9ca98b91a192508375a2db8c4566daea8ba0228263307706c0e0e40d4091bf3b5db4ae3200af3f59672de8ea0560550130456c410747940a1f31d02662feacad981abc48628ef8ca1015c66cdd21e454b0b1a0c3e311bea62aa8c3f737cf8a5626e3b9e57fefcc74ffa62c310f1a1fdab39a68a878e5b2722b1073c195b993b7d084e3bf2990952b6d5822dc9ab838cf33f75f100d6f9eb363651e054cfe7159895b0daa8255121cd36e7a6b9a0b16656bcddd9e9cc94a5c4b423ec2d69fbfbabf7dff5d9818b33e59e78ea68d34c046948876ef6a76d2da54814c6909fcd19caba17a6fa872f6ae6ce7a213d4e4dba769de68d89a77f4c6d7549e9f057693388d06b032b280ac2f4c26cfb56dbdd5e7535ed34e6f27340b6bd37dfc087bef9d745e91cf5eb967b3b3068bada2cf454c0a36f7c0ff8bbfe39ee1c31a96c50a1e557209f6c1131769392fd3b6a5efb867f361ad9d29586e3b9ded15c3860f3a0655e6d0cbc34041250bd9c8947fc07986621c955f39c1f9d63c99efbf34e042a0402a1a9d09aeb433b07f1836269712c6decf50d126e347f35606cdb258996521fa3dcd9dc220e79e6d26f4df0863c279cc6e6a393a70d4556587643846a6024bcceecd66f33475d64ccdaec03b34179013e947592d0c7b2d97e8b9ee9bffb036b3d5736ff88afa1e4d61b27c95d4c2ecb8816b443913e830293cd877e165b40cabc0c67bf7811f811cdc57ce2efdf316593acb93be3f2ddfdb77f022db741031cfc1209fb5c1bf1a0ef231664b8a535def50db2d04ff0a4511c25fddc4ce16cd92976936005bf774824014539645398b6d54686f390dc8a532e30de40f42d1ed46277b7f2cebdabfc44af257fdb490e4760a9c9e0649a8837a8875e54a25bf673c7888cc82e7dcadf6eedbd7ffce4383469715de51b15539d3647ce133c4bbb4c3eeacf4d3884e59273bda5f94f74a361094e30c81282f3f7efe6c89c1078c302c6de336761b023a1930905351f9ee1ab0228163e69e93b79612b8a00647ab1044325d529a5eac0c83f7194198118774c2c7ac67f702be804872630b788e3a6ebbe708fefcd16dc4438b8e3850166e31dd7b382caf815c7d38dc943ab3342ad9d5118e3b25828b2dc03321be551af66493a1afcb413ef85149176e59baf17388683cbd476715d2d7a0e84d70df3b57e800eb1a3eee6bf9057c01795b8a985ef4e78f31a8ff27f24816c6fa572276a2847502cf0ed236515ee48c5f05ff1a7d796a8adf29d209a62584b32448f6fca4d3b92932b558d4b2122ca7871d96d7095ceb1f6ed3c481cda263988854659a08a12cef30b077d7a8c8344399d8e1268dd1772f19fb1e037fa6a1e56fbd08792dc3cad1894953801d5cee85f74c31748456bb0a03c560b13a05811c8c0ba34bf32f4ab241fa409c3ef0f55c510855ae32eb37abf063e866fd646e8357b84ec0ca8cd1c08c3256ecdd4f314d7058547cbf0c4e927cf9e99f7d29753bae19cfea01f78a22c98ca3de6bf0ce3874deccb799d6dd6f3e5c5f5c2f17328c4c0637f74301fc1061be4abcee629617f35911ad5fc1236f1e7613ad12b06c4950993d573859af37a7f6989cc83866438a7f9073b464664e0a7619a439147bb28688ec5b1f6b1a9ad03243a070107657c1e0d8f62eb28a11dc0f27f7cb2b465bbf7a6f8d99621d504a9073ecdfa24517d1280f34e6999654c7181a0e5af49128ad7004efd40382b06b2d07e503187b0aa60d5813dc3ffe0457b74f7092b2bed26c29b2aa72fa17570571b835dba02459df8c6ff8c335890b9b98c0186bb80ef8d8cd4219c664bdfbe81e4126f1ec07eb38a547d06e64e1a712b825d2ae014204bf3e09b604f774523a7ebddca23f1a74add68d6dcbae6db0a2a4f2679bb459f5b52f1bd51a4f56039a7c52accc2efbcd290d84e658fb6f8b41579b51b025139ec93b85e27757ef03cdf07ea24d9968e731a77341d71315903e1dae29cfff3e51718b1fc6810294e7df5fafecbc3c2f9fa8081e5e85fa9b14b892107fadc9cb66c320e8180e35810bd47408e0a04e600ffdeb21dcd018d0420b9e104ed1693738b9593e9c2a7b1bc57e5964e18dc292e01ba69572213b20ab1ea0de87a3f01fe2073f317a94ab1f422c285a8a1dea2d61e059630157e52f98bfe61ad6e7f714471943dddaa4d6abdba7c92b0d7dff4502dfe7690a803a6e0bc7755dc5ffb1893c141243e0cb354927e789b6b77dfd9b1a54d432219794829b09feeabea97f4f9499329dfcaf2e14ca44e41c00490a0ada17d5c746848f41dc6a087d46745303058a41dc1f28ac01113b29a5dfa4e791527c1ac668e5b8ae4ad8d857a18353ff68481839880ee6070cfc9f982c4dc1f99a80f0a7e65a5fba6967afdd3d299a0f1834bc8da5e9714781abd78a5f9614b12f53c2e68609b0ee0d2fe6f36c9ee7d603d4653cb892932c9f5f9e7ae741e1cccdf9ac429c9347ca33556e57828e5e357dd5f0117482938dbf3cb5464316bb37cbe770bb63d0d59071ea8c70c155405ca9f1618fa1860145e0a4e498cea0684f6b616842c1ade2a42086ba0f95c1a80ec0a7083726ba0f345854aa62de51479663530d696ea74b02ef1d739c4552331397850f4539c5004ac10b59564a84952e9e292172abc3207ef97a54d15d59a4139aae98ea281bc19eac776e841803316fe3661494141a1fc071bb54d9959d534724a60913fab02db465d298d922d02a0f3603612c23f5eb7732b02145d3b2d1086ff9e47636c0ae85bfe439687801a110947412f5aead46f54d03ec6039b6a328e4695e8a59bf6aef603904b63089214ff389ba95770ce95ed906eb57e85865da35b02d508885d500b43d3e548c362762c27fa44a3824d0f56c871c906bb566c752d9c9ca0634bf0e93e907fe671443f7357563fb096379916d25001f1176ffa03468ff38667d0437f36f43cc3f946c1695a147d41ae1f7683a2f3fa0e1e8a601f45397ca8f1a8a9fe243e547fdf17c4db273bab7ebbf700ae9c9a26857ddc8ee134dd08e962dd44345e3b38ea4d59975f9183b5f18f806b2f49c4ae64617c2e109e0049dc986cb866303c0eac6a755f99a4e01b9fea2bd8fc02af5e8d2e1eca840bc0be205bafb2824904de714ee1108aa2f46d48a898509b2143a157a05ea48a88d630ef89732053b8077b4eadb089870a589a99005f744091e95ff4aa378eb7258e93883f63e21ec2f8e12a3492d98c406890a20c15cc3f41ab44752ca4a652a1cbab0d23c83866147d08a115287d8fb3d46d4a64fda348ba2630f081594d1d1c3012daf321c5dd5913d19e572eca1a1a29ae00fe6499607c9bcc8b6c247e06a7248dde4586a00f8ff0340ee7d9dff7dfdce76384732926c53b21552790f52809b2dc996b13d801eb2c6916408ebffff5efd9cc2260d837c40c306650506b07d0a784f7a40b2055a8a4b8ac769fe9e52cfb9efbe26eb7bc925592e69d53369f64f3aeb858581d6e0001cc2256b5a67090c0a8c8ab8a20ef161b0d3388e4152ba8f82fbe612f75de3faa0515739fbe187623eab1ce42a4ac2ef89cec923fa2642c6e3bff5a56b7b4cf73d2bdcaf7b3ca16f22acd66da5fbbf2bf4af73e7639d2b5dd8e4efbe7c56386083dc2c68590d7a9e41130fb66379d83af4a6aadb1bdf8c65be66032bf6ab277b9b38a7a1633d79c9e8f1dafa38a1fc5badd771d0625031165301502c82bd82098c6ec34af9a9a8b14ac0bfd421dc924f32f33b68acb83e6782808952572645155a843bc89e200237ba5bccc61529a8a1de2e8f680230ec34d48a4afe9890e0e8e7e5da6a730ba87018faa1f70231428557471e5f7f34a06875d64aae66eac053b26b25c2be2f3edbb6df3beb09a34c49abdb40fc45e4628419fed33d7481bb602961b8c2fc755919c158b9141b67f45b3cbf2edfb90991fcd2cebe0f52f4fbaee936da8b560e0747c697ecffb196241b695b55c9bd056ca202b233655904f4112d2f8bb8953354385e8dfd3b8c19fff0e2c88b6fe898cd1c06c4fd5a1b534037da1bf7a70774728a9892aefff7d10dac6f72224c17e6eeec2257ca34c1b97b910719e6a80d798e01f3738cbdbcd0d438bf2835b1ca6dd77311f955ae5d17ba5a70d40850db0a278dfec035b72dfb483ee67f84dbfa55728f4d9e192a4419ca0509a17b3ac23335101d6e695be2a029dcaad9ade7746cb90d01d49484e75c6b39506d9325c6d2247e333beab65f0eceda1d0438be48c436177b135b23b6c45ea56b02e76e096c0c7566f74461e43f2c70c53cbfe41482a4059036311cff5a3b30f6140368610d811c5c32425a4e457a5692d0c5d40bd70534d12d09a0abe5ecd7e3d473578247fa68a9361f7662033a0c71b9dd9fbf2e59ca055ca78ff15403dfb2ac0da39afd865c217fc095ed03ed950803eb6db32f0f86114c16c170d31ce066a0000c8f438fa313b8461ff89480cb0c37759517e2add69043e4fb9073575cc06fbd74d3198945f594afebe4a5af3a1b355877ceb9748750ad8134cf67376df47d78cf919f63629d679b8951a622deb020a15d0c36d542677cbb9663725768097dbccc5d18d1ae3fb071763d2982e06926838a44fadca4bb446c31548e0005e40bf491f6bda3e62636667933af94b0f0c7c6130d2df031b923dfef9c1f7b45123ac9c757b1bc1765d14306672fef6bd1a7601714dbd44536ddc520b94de6ea87d729b4f69b1220a9eb0b60f2d58d454242e394a1e5b2ea2e62648fc922ec8044463c083851747149848d00461dddc0351b190592713e9a2fcd3e6feb1f27d629ff1e8c4ecc6f4e7dc424cff9f0d78ed85cc114c84028d1197b52fe3aa237b3a26bae8a75a5d577d5e63f31cfb44cd7eec8ec8d9c42e2b776b7988229d65670dd79613d1b975aa749d6f34ccb8771ad2f347edfb23ba2e546116b6faa69ec403d600a69109bffa658cb20cf740ec4977a73a5e2b922f0990287441fac7f216eb0b8bf43ac0f41874d8a82080941308f04cd4e103ce44ddad11aa2a50878a18686629c4d9bf0784f22755f17f8f5eb41043bc04abcd8c4ebb92a60bd5bfd760560dab25bf245989deca3949b6bc8c8adf6b149ea12d38299351ab95a94ab275343c027b444f3090ae7c104384bad13b43c5dfc2a0f3ce098deafb678b3053bfad03ba5124638d38ff00db4948edcb0e3c3b54f3a20c9e09d47828f4438a87b8eae6515ed0f5b0cdff69c71388cb08a23536893e97ca28c51fb8f30d5b3e2cbf4d723fb9a7eed26c545d65e147a9ab4a023373f46eef013f5e3e127eac7d7504f94eb5529fe7bae57f23e9b288b973b3e1d040b8f232e34f4458bd6c4e58c5d351662e8c8f10831fbcba863636ae4e698d1ae71283145c6389d80be3948cdda8e85568066456a987ad4cbd9198cb42e6f52112a124d59b6bf91cc0c219b19be3f96b4434974d9860c2d768f1ef9b294932926698392986640cb85b7f6e9b05aada380379c334c7172bf0a2339ebc9902dc2e6ebcaa64ff5d36f0049ff87433b6ad9b52c27956c4ac80773e700fdff44d6ec5a86c902ee6a57ce562637053911dd15c6898967babc5f3705eb9d79160e4b98c3ab68c2d661b6ef39c07c99bd28be2770494c62eb81aff67bcd0210b0256bfbf2b64414598cdc55561be3934b647a75b5431ba49e1c7444815eded48d6be9dbf9e5c08554a57e706bd867eb54141fa310b8c9840c33f827ecc01df7d63780eee118d49cde2df3f63cb48f2305ea84ebd7ec446e7d14f671f4f9fed6e3f13e9fcc68bca7bc9ad25ecd144e6bd4b361062bc2e08375cb58896e083c31c3ab6fa2f7dcd3326d40e5f840c967faf2f074adecc9e1d16cab1c1f86b0f4ce3eae27655c1b1f6ef1ade3c8146a96e9892ec6439b4cafe623270b79a7002ef9c811bd7f72b2784cc624a55b12f51fea7dd20f928a8706b7abe5eb0b62329c7bd6ce76f26c93ecbfff69b2c3541f7521f37c3c044435b35fd081958a3dcd91ea38bb8e757711270150df7b070e990d06f6209e4a340fbac90c458390026466059ea7966dbc421d045c81d99ff31d42fb449f22a749de99c1dc398d3f9d08b1b825ebe13d97fce11ec960f4c82f35111f20be6a3b37d5456c733ad1eb19c521a864d44b856994c90068df7d9d0bded16022a17d08a200d23e2ddfb129d3b43771ae709ff93223072a586ac460beaa5c7cd0e248c60b614d4ffc1fd5fdf15daa6ba2067d4d3233f7071ef556260c514e5442b51777f893ba811f4bb99b6fb630315ca9e7ce95516a8c4ce6ee0cec4c9f77b6563b476f9d170f24c9e1b42d8ddcac025f6266e3760dc55362bd04a2b8c35f7585f04a1a18ddfbb1a386a47882cc1836f3c276c5d5a299ec0ac164b45285e3b00b24da068bdf4df4b6eb094abf20ef81bdbcc69cfce24e6c30716ef25b5456c9ef72b1e9683d5ca87acf5311c451ea7b77e91887316af446ea35f424d6e6cefb16dd1cc6eb236f391093b7682ac291eb3b6753c2fcd2070da509ad27e0ba9d809286522d38985a72a819079338e9f13e7bdb3c5732f562767e49677f3c67ec9684d671600954892936dbd10e1fb2cd6d20fdb2891e2aaad46c51f951e228b009ba0d9ff700e169e6d2ebec090fc72cb2b02aa3bafff598c132cd6da49475c8b3f6f5624c23d98f3511aba9162c22b53853e23cbd337da4f2265c96ad22c4e585b04c725661973dc1e8879d9dcc7f0939f1018ad18f785f41de0e14e1cd9fa28b12ab28f030083758730a7648d1e779efb70b79d811b6eb7850f7c8ea7f042742c32076cf3fd4cda7fff412b6bc9bf7b875795f328ed2e99533069bc135b8a49ddf43b99dbec363f27d55a113ebbf676c1603cfea2286d6b019e8d5dfeb8eebf7b7b9800da1bb24d75bdee602d6bbff7d95529dbd1f605c13a0a5eb2efff657ee03b45c594751a4a2d378d7dacdc132c6d17cb59d4232cd308a75a36e9c926d8863c08647359432682d551930bc1bbd8c33a13bb27f3181cb202466a455bf7c7273d9f7e689ce8a63bd19d5a936ded38ac7a3b16eba4ab705a8c90d0ce3b7a070263838ae50bc01a24e77a0681b8a51ec35f40de87af3eff488e7cb3c764a3ad2c9030ac306e8c23535af35b8a2127482b94f1bb15f9562ab45b2992056ffd505fccc81d00b9807ac6c2de0dd52f2d8feff7216b41e00443809d989a29f1ff8973b7dac9c7ee4d785bc6bb3834dc7beca2cff40237c559edd88b4fc77fa78b4e9a9af2050c7ab28be5fbfe676c5b3b45affdc7d5fad9f0847058b0205df44a353a7fa4591d7bf640c1f3bf687ab85378bfe421e56cbdbfbe7ed3dcc1f37f7f7ebfbe7ddbf9758bfb1ce4aa9b55ac8db23a651e5b81ab568fde8e1f2a5febca8c745c1fdeebe91c0cea77fce076ce67e8f50443aa0ef4e0d696842c913f8b6f21a18f2de89551036818b109ddbcc8c51b3cea6d0633d022a8ff445ad54af0d6909bb0c3d493d683126a0420d4f616772b7cc8fd7647e7cfd98693e9be6d3be7c5fd105057fcf8f0e3bc62133f2f2d938c0522ea3d008fb737c009309fd1fece204cfebb1b5f8948c64e4f0bddaa8f386f3f4c6874c79c2dd1ea8e683b7abb337d0778a1798a53c2fc15bd63b21bc112276eed059fed114d769f029ebb12bcd57aaeca498c1f10d25ec62fd62c9a99b10495e568df8082c1701d6f817b50df17a7d5f9bc0925e9ac1895dad1d86d643b4ed73928ae2295489f851d6ab1fe2e30b7d28b49e1d53f3bae4920415cf3a2d82bec68ce69aa24d4d1956e65126e6e5df5a9b69d770c90b19c668183f3334f6ff7ce207df868fe0cc991c21830ad78842786af1eae55ba172539cdb4c4d3129f950ec033c5000c2785a2e6445cd8ea2ad180b9ab6a87f65ee01e60ea3283e48af8571c069b9a174ba06ddf4c5af24c2360c818ee2dbea9aa8018ead7fbcbff8225a7e960656008e02a1e623c2e98248d024e5f3692995ebecb38eef6762b0f436ee63c638283c0416f073bf84e8d6dcffc6d3c14406aee709a09b5864da8b2053bfe6495b74812018b9ae5578a70fa33d613a527014e3e8eb42e174cd4c276fffe7a160a25af982a698ea72a6cc861e939b03bf90b39e3048a9329fbef9fbcfefb097034da0f44d563e230f1753b3bd5f62185eee53a5ce2b0ca20e3a198ad03fb1b5464719ac141683718a5e86514bd3143d21562f855555f48f8f1d51d37711067fd14aa9efa17033a73c51c35e24e07b4cbb0701f861bfe17c732be0899cc3ed050f0256d856d8b87d2bebf5ea872bac128d0ce90d2113b0ffce6816447b2147ae2d402eb0defc7bba2ef0b76d3191bb42aea2faa69dcd77b0995cb23906a29144f9396720553a43666041488e56d7ea2b5ae8ced0b20b0c6700c982f2ff0546f02926373d2590c12a522206c0fbb92b506f88886f0c292cb8cc46d69f38eaf4827c13af9d73b172127d4452422f97264caa5d435a895b2ccd33ac1f31348f4aebfb40e3a7bd40e1f4e4f9023d66449b9afcdfbbdb46654bd7e73181165a1a0d32f73f64f47852fa71b3ab388d3bd7876847ff2d91c48a406a3f9c29c2b5dcc99bddb4f4265b791bd729cf3498621e7bed269fcef28847f16d072b1efa8e7aa7d1cc67a13bdf0706a6547a2850802e64fd014cd17a40a5ffb900b8e158099ff24502108168402062c0259ce1d00ace760dbdb40521f33ead4d2c05423fe1fe2399a4a4ea7f85ff4f1e064ea7a2fb6570ffd5dbaee3969d464f41b9aacdf4d4d3a9592369c0dfcf37400266ea4290d84c60a998e5907aafc7e22181ea185393ac88bc9f1c05ec20cfb522811ed076818ee4233286347aebce602c465dbee97972cf4089cbeffc7e52fe3a135c80449a8f681531da526c4d2966e6511df665dec3b3b3072b110b44f6ff29534acdcc384835b909d4caa1aeca61ff7632e811bb6bdbd95a3a42cece11d2979b000b6c32f8eedc464d26c610bcba07301967477985aa3157243e8ef34246c4c5df72cb0d3d70a829ca5bdec55e895ecda989c0c2f3225a29a194628db743df6c41c82780c8a16c7e2fb0dd3d5dc1a2206f1d59be2c31fc6ebbe586f6af985071857a7a83a280fda984a675ded20fe555425962f7e53d693687e7e498c1815593195191931eabf0c6b75a690329dd3468a20ba55f42c538a60515f52a262092c1369e90aaa313eb0dd8c86bb91f77242fcbf5fc622632e347c644e4e2a9776ee4f22c97c2d1bf6a4019b49c28dd43bfc63611b9eda977ee06567996c3fd8dcf0d1b0d13e5987c2c359e8ca124d6f22c9777be9ddc683951d1ea694f5f4439eeaa923feef8c37b6ca9d1a6a2fb9eaa614e69b8d12028033d6c53029e238372dd24aea096b80f922f47d8ae9683b5f11747d3344b50b6528618b237285d4b1d147f3ba80dc4566054e3248a60bb8723c120e5bcfde575637e3e2c750c568e4ff6e60c725f2c071fa2dbfbc7e2b6891a07f7ddcb483c9b0a2c22ddb5db327c2eba34861b420031d39768c2f4d9c78b9d1c7a226bdbb327e7b33db01a7a0f73074155d9a067b7210e73ff2961c6fb2e37a9c21098bd3b918c89132b69fb1da8801425aad7a545c841bb0b17d4b3715e969b7592bb727e8a542c645149e31b3c5d9e80f25198b751cc0901e675ef4e0b70dc526edcede9289a1c182266af2e22d33554e5a8bbc221b484d0820089b14095aa0961d089e48a501a00ff27ce7792698ef687ec0bda089ed3ea6898c1036e961e0f7303fd1683f3a8808ec1530ed5472b16ff1e7c7cb714b9e36fce5b8ca2aa43935fbffe4b75e2ad2e3996fbc11519735756bc4eaa2cb512c9450daa40845a459bcf09566fc1d9caa0fb84cff5a243d22c7f2f97df2b9a9216c67151642a9aaddfa3c7c423be8381e33a91e2515f0a54313caaabbbfb31688d1efd826d85526763cc0c335dfce4f9d2e81bf809db0a41676358dd243855eca04532accd9746df1bf7c463842339688ff982c76b156cb3a1445e36c8850bdd62fd01b64944db551dd604eba5d6b6c11adb0af70c51dfed2ad048ca559c6310285bfea495f9d268d0ba8ce0a2cca626bddea0f6b1357d7d60cab19884fd86a29256ba6f34ceeab880d2c124b66eb2f21b65193a112d9a1dad1e59472a574334e2d05bfb69fbf26c27306415a1cfcd5f3c931e89308ba4e730f51d19e433a444f3b5466e0c3745917ce9a1e192620959b70610a91fb1f469aa765542dbd83d24e390d66d17a6c695b93b748b806d87febf741f02eea0d959e6b2806be0f2f509b8eb537d147009984ce3e65f08f40d3c5115e8d2298972323a839317d057ac4699a7c880dadce372b964688d6eca64c6d2561c18c034c1e3b72497697e7364195c9d4f6a5940327966d7e6031eabc8f7d5d59384ac1a4f0079bf41fbcae93a1bff76a55aeecc81e959b760e9df199435419637b4f72fcd0f766c143376e853507b65eddd1860d088fe2d16af13f8611c24532ddc423c51981a1f9e4afbb00d094c6a406e47710e2a70429f8f9a526fade8ad32a0df4c59a69e804bf2c83088655af0a7ef9f662d52e4f3537ef1676815f35e6b134b9bd02cee0f306311a18be59ac61f4d892f8ea8666a1ec8e840053057b9f7781f6b647b67be0c2f1a5a3bc04c7e3c9499fcf808562481c661fc107c74c5d893f044d45ea65ef2958e5bd0863b7f8751cd6d8bbe91e8e227b7ff6f7223f1e04cf40dcc67bf8f319d15f9ee59f42e5c1c5be33d483e4804adb590522a4b3b134c393e95630965ce1ef6d2b7b135b4dcee7d2614e29d74b2afca030a9828a604f458f6e977504a80d1b221887ac7b78621ebde07cf48e6bdb228ef76dc93b6af91cb2ebb2619f52d6cd61a9ba4a24d172714f2030ceafdfa683dc52837caa4fc486158ff8952f7d565ba7c8ed926d87c0cae229e6675b172a71c3b65e18134378b2a505be1e57464d3ed45364cc4b6211f444ff5542b15a1c7787540655f0e669386dfbdf71ce04215c47b3cd07ab83d62825b43432925bb1304bf8173cc67ee2b44dba0c74abff63a9dbb1a2aa3347186ced26a3456c7d9ff509745926dcec59eab1b9952caa8009899b9808f5f4c91d18e9ac97fb793a4f9dde0b33574a2e59ca8b766322b6681bb2fdea7681926f4c44ce5a7e77fc97cacf36fbe53a013a21d053ae1a61bc7e599f8cd0d9df066778d93e449f88f31d566cad4fb960d9df0cea88d659e1d119bb193204aca172287e2fa838da612f1a8fe830d9d608833023ae1e76d8196007257b42dc7bc84d08e58721cfeeca103e0c31072a38398263d5a7e9866da02602f5b069bd5cbcb908f880d53603351ced3b16c42b4a34775a1d8636264a70c37f1641cc1abf6cca90bd20455d8bb25be6c0b324ccf166abb6bf615f049621f1f107b27392009ae8e4989998d41c3175f16b14d3f2e00937a6cda49c4d0b2df8e84e024e7a9617e60c24384c1e7afffe4d97ffa5f3e7f3d844b4900c9a862f8eb50003da829462b836f41aeb6366d5144c1406bed99869112dcd5bd317d39805d06795b1cd91106aae44c1bab89d561a10d8b18f8ff0340ee55b5be6ae7e992f43e010104a32a87dd4cefc9b22eda5b8cc8a1883508d00028593f37c51545757eff4bebe54e049800131011313435ab5b55a0d52d106680341aa0991f42acaa7e92ba35f28e366b4348b38e380768804c488a879b4169d62903149061521117b6ed117b504aea3806c9bd25fd722f18f9b87cec3d8c5dd201a5e50fff472ef342e5b5fef2c8b4bc215dca60396952c6b94f904742a32582b8129dda7252aab27c67d325f70efc7dfafed7dfd27a3857aef04a6f73bf246b6cbbf4dc808c53b75e0c04309197d2fae8517f5569ee2d37847955f93860342a95cf1d07eafc3c8ee4d768e7830e919a6f2b457e6027d00e1bfb81de3cb4bb81eb1f28a4f00ea5c86fe057f40e3b964596a675d521c2e73294bd550bb75a6c52876fdef9fc7b2c79393054737eb947e4e207fe5c5d83970c2374e1010972977f63233a30b16b44906cb79e6292804c0bb18b88778a56b8c2f033c38553442804ca91614694d550433caed1e842ea98f424fd961244f4240f685867f56d79eee47cf478c980aa565d387dfb801dc5fb6aab8495b101c60c8f70f818c358cb0d17f42694091848e1f7cf19481a14f63110850b44b43c5de7b213b258351f21041a6699ed66dc6beac957d3d954c6d4b1ec06aad8440a094b71fd1eaaf760bbcd71fcf60d1a8c3ace1d1d71a4b03ee3a5474f3ea559af1407ac35f34c71829f79f3bc37bc5fdbc06f3fb009e65c296eda79dcb11c43151519a8b25721575ccf2980d44b0f6377228bd402c3b610a37d8c583180eb799c1c1e10ce6adff05159e7f5896c82c55a13f8e4491132c41c5b0a994d6c0450170a42267a5f6f74cb68a4df3acbcda01d801322b1b503df8a22f0167d78d77c78c7a2a88b9acf267107bc7c83806ee6600a7fad3b25ad437be444d06b718273b7cd84e4b899d2fe7503444c10cf08d1f94b9c7d216c0c8d61ae259bfae48f023d6a337bda25d63d57772301f326667a0e194236632f21b76245d2a55d865d5732c3cad093990203667affed0e1d4a52dad071fdb62cb15517f7372b8d0cc2f3a58f47741dc5819d5249a4f1f0b8bb0355dcc0af6987b1efbca899857bdeca299540e4caec63a4064144ce96e642421c765dc88cf3ee0b5bb5d77126a701c12167ac0ea3eb9f6b40a5a72c2b4076c3cb5fda24cf7298f00e110d8ef7e838046753a293db4c8366191b619687b4e1e475cfdcc076d090e52ea2674c0351c8772c555da41530de736f07cd5e02d1279822e49d0d41ce8b1b6d7064fa8b1c5264ff9be07b3d1d328c74629e9808a04213020d8ae3915397590bbcf410c2715f91b458d93ed1d2b574afa9cb4e5825d6a6016e131b302688b528211d3921958b173525ec46d6bf35c2c6600b1457c9fc64c86bc068bbc4f25fb349a9238158ea8a356ef353aa2735e6c57a763cd11b71fbcece2818c575bf77de51a1cee947997b4d5052e60aa3fa6093665007a6a454be7a1726830d14811a3647cf0b7734259b328fb30c3675c2a822d48bfb178ab3067cee114cb4ae9b0626942490b8cbb84f13d8846c57447f41520f22e3d5d1091edb435906ace7e13f674fbcc192fbc42364d91c5261b5685cddd3b43db27bca40252cb81d314baf91850a91c8251bd3a09925e5295baf389efb85b5881c93857262c79d3a68a18889ff3c6551e5a397f1e9fb7fdc89fc64e855c7cb3a388bd4e183e89ecded635fbb810767d1c36e53f60ddb709f3b964a2a250205019c3c5e627deb5d3114383fe94c807792297976f77b36870722784cf61605a4b8ac736de01b731a47bdfe2c5c74c2da59b0f80e2e6ecebc073f063b78c2e7ce9d2d5dfab2addf6468bb6583c9bb15bfd3c96ed311e9e80e93a75113b473b472ea00c006c98529a129b6ec9640f8a6eb613f5f775dcb5bb75fa95bd4aa8c064eda43bf4a5bd6d114077b3484fd61e303cce415a8e3e0f3ca85203b4b8b166e4806e29e12ad86b6b3f8fc70106c250534362a470ecb4f708a10f10c28a305dccbef8385e5434a2ed35145fd183b07c106ef52c635e060ab91c5ef058669fbe52dfe00d20c67ce2efd357b97e1ca5ccfef0f44cf2d4123f4048abef938d0360337526e674b866eb0735d476d8a01725134ef5ee7e69213cc109404a8b4c9ae01862e9ab8ec926af7320e5b33e3bc2bef144821635778d3387b8ca14c219125af1e3a068e410461789dcd938bd7b10287d63f0a52cebb81d1680ab18bbf199937fa61f835c3866c212efbf7774453e2f8e4e67f9b374413ec1e1c7504c79069a669287932c2034aa27a452c85eba647c6661b8bba1d0a0b580aec897912b9f232ec0f1cc0f62b145188262c4dfd7856cde53ca6b9fcbc9bec67d8051d0ad37ff488461f6e705dc7505981970106d0118e190d6a412667bc6c32580b2e9640f4e122eef10a330f713475659a412866082b88b0cee00982d6ecbfa76c522b81c3842737cdc6f6b41ec0f2f1612b164b46f2a7cacb562b78b20c3e5a63e05a84cd6ee8b06dd95611224bd79773887cf6e80afe01b7dd973e66230e14b86a983b094079186839a5bceefad9ca5d72fcceb67ab2eccc22c474c598b5530ef4d700d141eb6c37b711e875c0390cb1e40cccb99d6887c20a643aa5e8eb237c9fafa510e2b536395c43cb82d640ef8f66453d084c5b282bc423df0b2ef34226b474a032763a7fb24c68a646f0b24e87a86d1bb98e55e1b357e03649efcc81bdf50326ac2c278ef6d1e124a35a030b1f51406595dcd96a6f15e7bc8e01b95d36c09630d23ceff662e0207339c241c7fccec49711513a57f6c1af87e3edcf3038907cf8686735ce244d4a12bacec4e15898d506d29666a462277141594d42fc88642dca563f3a929b6a2defd6e80fe485580a29f7775a49a722a857d712fbd5d83fc9bb18599254c9a657fac0ec24fc49de99ea5e3dadcb4c516d90dc4c03b693ec05a8a9b2a29081af744e7a543560a1c5fc2c49a6ad288ec409b839025fc87110f932a3476dd15f98cf31159a000619b539a0779a74c638e66f1d850968d299d0f0338412f4344f23fa670d61a420539198e3179c7dd724b05382db3d45001bf4e63630552b2d57553169bc140403a0a3d9ee4a6093631de37769919d829f271f22e96ab4eb8c7c93f70a8016b1353918c97dec506af6196ce646fdaa4290b73c27ece4d7046d6d2f01d9932cdf7eb71fcad3ca9676209c586c77fc1af71c4b3103b1b029d818a5c3562f31ad25d5e994f77fee58a6a2aa646fd0bf3fdfbb909b0b2df43a5597414a681216b76626d436899f7cd39ab5e0584500c89e1d12b83fdf27656cc975a9840a134d02b42ebc809b252a1b0eed11e5a752731ad7ae3f86b6d0b9b30dd197230b594d6d6509884d2fe749a59cc6c57de86e426ff728757a1e69019b164cbaad84553755ca49b2e177779f350e801dbd472f2450440cdb43f4127014884bb54bbf6bd9103301dac0bf98c5cbf9a50d4e9f8d27f911e3fe96d2fc7ef9d8ee4631c1b3dcbf419c4d75b45c0e06a30b3b935319d9090e46e72c45b80193c581a3a0ecb248729ebd73df8abb670e4016374c0535454cb9b7db5d8852d695006a6a57e0ac37ec5a01679b9545e2d2d1407af478b5ecaa6fcd71e6ca19124cd763d617c2337f4f03692b62cf4e1414d03158628d2f84823b6dd1537470bbdb331d64dd8bd3af99c8c3bbe6e7ad8e4783e939bc14f02fe1b9728b88858ceb1d15f6fdb70e41288ca7da4c310e2cdecc67dcdd29658d8e0411402d07ac5b5b0d8c543529bb47ca63b136d35a6fd152d7d75b80e4a22952f5a5380de55d9e9ae64ea653428811bbf4a3ef642b9ee5bc6e63172ddcc8fa3eebe6a005a21cbdaf19c88f149832533d2056910addf8e2e6402834fbf0bc1550b8f50e062a6aa1efad2865adca545fcffe6a59c2354e3690046aad3a3383e3d530b9230998ddf30f17f8e2e6a01a4473b3b5a123e01ed2a2588b653e373770ed9ed7fd65763b5b8688a3c43294f9499c2b490bc8966e9a6f92718820fa9ddca9a5fba169ee208fd21b93a766e6568ad6e3320ebf117c706d8893ba5acf654d451ddad6e94a9f96c0b7b93b26bf44ff11bb90c988ba7487c67ba0df34777d8a6e385596eb694f0b6de32cbe1ffd6991d04734e84d693bcd0eaac2f77a71b4cfa86b2261f4f6cf4c20bae3d190259e809c6dcaa790974ee8d8357b8ddf5629637886eaef4f1eb66193e7b03c03e39ee05c455556c1b9c3a7424b2e3d95208cee82eb43c3b6ae3f3ac1308bc04060a827748579ae36b8dcedadedd0bae2cd5415dae63e59242d4c7e6f72f762282626ed853937075b5936ba60f3574047ec68dbbd587e937ce8088dfa8a1b0cf72826583140539abdc067f363608c3bc336227d1a659fc93c79a28c507a1dc55c38b7e1041f9b689002f337eac498031760c0c18545c0d94c95207620ee004025d6e5f016ae3be3c3adc1c76987e6c85675488a5b8bda7cc167a33070eb89ace2a9a346bba8f00cbe5e35a03173abedae9f695d942529f1065f37f2b093f6a3c3f9e4f06c3f3f3a18c84c1381722ed3a9d60dd16bfdb8df5bd5e3d43b4e26a99282410cd6c170aad2447b4fdb792a0a85c6a1de9311180a5aee7cf454b009798010d04059a279198b08124998c992cf7c0c7b8a7560fb81b6471664e5d6b07cbd5a042059dc3885a3af12c014a555deb4926fe57f042e45a5ca3ae1ec01551993612645ca9d14474247de7877a3425c6f168f181d89c565e1019e7519068aa45cd405eaa88d8e31e1af217251a4f5212a7fba04169c846a488995ded263800e881aa465ba332cf7fb77fb5553cd1f5f1fa58ada81e4f9d7e57afb1086655df2fc36f844431aa9fa18c0946230c2b66e684e1880c85b91a365d9efe4e049bf0fd01860982971e5a576851d345b42f38a09b66d8b08c67f89aaa994d4c4a617093117c9ad66181629e30b97f60b441b07ef26ddae5b8373474cf19205672d65dc2dae1fd8ddc0ed0f6f038a97f0e05bce39b23495e24cfc54fa5ab15663076430b68f7ace3809a5e2c0bb4657653f86873ac4e436d148e7fa7a6ac542673c7c1808121a5949a0e05e82a0f26a1e27dd09fc39953d254bb899f7d8e162c4c6261b82090947aef1aaff747a143f15199791997d855460922df6c8c7ed2d3f9a6174f12c4688929fa49f8d612162fb0c393f4fd65842a897e13f577ea5a4e275e641e63615d5c84c8fb752106b971c6451b64c08ca2080a4c1d18deab6440451b9cc4234b682d6a6706866ab083ac91c447a74ad07dea4806799296f70cea42ac36802762889c318115180726943e43d70bfa573684c6f61a532c7b2231f4073ded38bae71d4f680ff065c88ac923a908bd83d446bf387dfb65f15eb86d1942c58dfaa35da1a41279b58abb05f7bd3df0684c387e86b1f0f647ab6127f9e64d5e34ab0e571fc669d651abe08a35b2f7ce3d676121ee005d7ab0b91d5291e4b7e026639ecfca6357883865ef9a09a722c6162c37d6c6b9e092dd9e03c7c2b887cc4ec5bcb08943b271857ee52a2e3de24eb03538055d7a9a1766a59a84280148bc23658d741b3b9012e8ae410dfd61e87a174b234f7979d8b71a2253995f0d835d81f4d4b57a7d282d16a466f8bae7531b74270cb3165ff8be75b2fcc07d2a75bf16a4ff4284458ad8ca656d28b69412cf23e1d2ba1889082c53681e7a441c4c165599d8f0eeedd3feda7971bfd4c460fce0513329f252af048ebba13d199ea6a137d8175cf3cc6e1f1ed09e6751b7670eb4dc7f45d6d3bcbe6c938ec02c7ef4dc96b1f8ddad9f5a3be25eba2ccdb4aa9b413b2cf6bd5b5b5a864d9e7ebf7fa4bfaebcb6d3ee0f997e3f5fb4f85fb60904ff6b80a7ee684e8aa2b9c81b9b65a4c517aebd19a0172b92390dfdec93d3ec7e171f6dcd65276b136ee6bb3d99ff5cde5654a1fcaf0f997d3ede96326765f02d5068107fde8048f37c61d611b1121f088ea0df321128201c4bda9ed9135fc71cda40c29678f4c9721e0d1b658d89e2e914ff3f62d34bd8beacf5c86e568160f2967987a4d6f030f8baca261e7aaae844a157e568d7fd4c30376dc7ba599215ba4cd4d2c8b6316eb44fbecae33046fb1ba32fa6125b8035c078d49d975200151790eaadda431952ef60a4327b4bd6cb2c5976de7260edaaab321d07a9e8eb6eadc1f4631068a1103eb333fda4ea365a503312963e7fefeff5f89a737f6386aef8f8f7987062f6870fcfbff7f25592428c0480ccbaca2f46ed6edad63b49ee096e351f40353072b5dcf98784610272516fccc283b0274cfe7c73338ea35597526ff6f3a654a47d74b17748a7902ede9858497e626f637210a939445c52ac2184f674f84c8ed8b8675e7cc3468abb8605eb283bacf342b1858f3cd63d76127c9182561b6dab9aab322cd6acc33fc4a39fcc0666b475a7a75f4659c7df20463af267db837529fe1106dd57e6082e854a6905aab18c97315c05014508148d8b1f54e94b135db9a4ac2aad0680cd304f2b22e6b15d859acee6449bc541815da38df76ef3c28a0c9b54300ea8bd27d9c5e09f13cbce3223a286e6dde7754675d445284f91aefd24dd463d8fec9b8d1e6956517ca1e884413ecd21c88edbcea76c0f87ebb4fb4ce52da439a0759dfa2afdf54edf1b89ac2139b016324b6382208e9a7e537cb94604559e2212bfb527565df174599a9baac51ade0b63fae62d16e7efe0e17787ceb750b1fd0c7def96e453a343ceafb7695c0e30d5978af632480f0b2833e86e8fcf894f79d7173a04fb3bf98873144eae2b6fd9117866c6dea7e2a6baebb4227bb5d3f7be3c69343f73c7debf03f0ffff316827ea9fb7aeff0b3ac727aff36fe5eed8fd2f043b2d478cfd19bc5ec461f3ecee83bf706133a00ee0b0fbf9fd1774e0c71577576219504bb153c9334797913defb3692553730d52a96059428402e5dfda820cea8889b2f29d31c8787c6af86c662d2aa919ab243e1334852e4d7685e5e3bd76447fa4d7317bdb3b13ce251f99a90a67f6f6d1d15fa0196c3cbf02934f74dcc46329cfce936cd5d419d2aba9e241a34140a44ba1843a329c9aa9aa1829d0f231cf5c9ba8ea2c5800c32debca1cb1cfd5a701e661bb0e7a6dfecff57aeab8cb8667480a72fbbc0d4b4790027edf4a124ed05479c4a09d734b6e283803d9d2635289175efaeb925f9a7163be49ef99a01ff89be0b50e26c92a65d917c0489054eb2a3292e27b55cfc74da1cbee637c386f0159c512257aa5137e6cc82faddd93225f07945c834bad4ce73354ca3d4906c662218845b99904f355c197aff4e045e9691a91a325417649d2587cf031379004f2c4e99525e20616dd17d32b8d8854925e17ca7d4b554adca1497179e5d839756984e13b0249c6f57bd1b15bf08a642a2726e466ab5c0c5936a90a9ef0163c3c86ab049598f57b4adcf88c323cdf9bd300beda67395cff7c78b832702ecbecdc15ad1bc296f94deb95c8f0165e6994a6a34c1cf9518555989f488abb5436c8375842b398dd7516da959d013221c4991b1eab8196bdfe104d52ff2d641d68a3b98b3a6199168769703cf005f30e6553a3e941979c6ebe052ac98ece7c50f7228d985c32d8ec0f53c0f41d53f4051c131cbfb1d7c731ec87be74341922b2fa105f5139f81c8142507440a0c5e24a7ef068c1823804d1aa2dd40366683f115274fb3baad178c7d139daa61bb722adc27a805edf7feda3e3da5fa65dbb3b0d8596e2111f686a3af4ee242d8426e7f3e23cc0f0946c82329055769c4a00eebca57045ae28eeda8007dae6d80c9412888a5cdf750d5aa2ce5654abfc71432af4292b5a3e1f144975496523591cbd1bf2ed7b23209b2869e40b1aafe21774ea87dc9d0b1949c954c49807d5e794839954826b719893980a1b79ba714327ae925cb66a62b545e530269f831270ce14a6e20173ce34af2fcebfd07960bb1f0ad55cda5c19dd8821d47f31d6c40a622ef4373932dab1ed268d5faaa265d063bf953e8ccad322121324ebdcd6ccb9d934618a08309936dc76127db0460f487366dcc6ac743ee398dd5cbc8e174c558c24ba7beac2ac98e1bf5fa45aeb502807ff77e7f632afd7eaa46aab467ee70c2e574d9f7bf23d9c8795a3183ca7b771ca265e087657ccc86772f0b3bb67394459de5c3e667a248bdfd948b2cabf70e4e65a195b8f8bcb0f3865a0656c6f41b3ead9c58487a39b29938d8480f0db2f16c700cb9b124e3a5de826798c5e3dd5baddbb106f92257fc0e961345a02e4f490801175348d40e95490aede1b72a31425ea6c2e22635f454703ae493f1d791e6ce1f538ab28c73af731264a58274e235d37a8df16190506a231f819d0c9bcc1c02734a18b087c9c2af4d14f2b89338869229d6f9d7dc9fc9fe14e0ad9f13d2d9357ecd092305c60732a723393c38a22a8c5d30bce20a8158e20ec1b84c71b8b847d9266701f46473cd492096b4f46be9eab5b2f95cef10aec20501c4a5c7a9ebdc659e4682cf8009d83481246d45573ca3ceb3d8b8b1599910b48af0f92b314612fe26f4aa096cd935f76fb9ccbdb0b6e59e43c37469833c8842fdf00f94165575f5c59e4cf9f5b0c9dc8f0ab00bbb87b0feaf39c94580db6fe75a3bdbe0216ce07ab91f419ca3a8cbbc1ab4f20c8c16e0d388a10197c7c473104f92b8052ee9c4c88ac328459c3a05582994c2ab950f6712808b4597589ba5962fb32f41db41a4763000f8ff0340fed4965fb5ef930b662ca1a7c3c75639c965efc1129a253311468006231149c6902a4df1cbfffb66fdf8a9254f05249054d658876101f4cdcc07926ce02dc0d7bb37a5cf1f7d59b27dbd7bd1d5821b0b4061a4560825ef55876d585061090a003836a459efda6ee562107458a60724cbf1c317c11867292b238c171e61503622f672f05b903bf3c177d63ba8f5fa4dbaa9ce46ad7a04a096d2e7b3ca19b9cbc77141d0e2b422927d19389e2c11548e3e9290193eaca3382cc5acdef2cc90e16a3d0185378be016e118de68d52d9d88ef2e59ffea11933a2e5a3334ce0d80347a9f5929223d647369d6ceb6ed3bd1c8941a11a1bf45c3ac8f268b2708937b46e54cc8cd4604b723671370f02b8dceea686d95cea803c13bf69b3d9d4f96e3f6f3a477d1c2d25dc41f67dd2161265c6f8ee46178208f87eb029179b121b42803ebd294a7540cc8351a3b89f06a9630e03b725880b1bf3b507e1f0b27ab73603c854d7b5fc4fc1e90321897ef27d35faf9c79c6d7ec202c92110870200b16d4f51896948a9757a55e0917e3ba9b37e6f7591737c9bb5e3e8d33982b66ee9577b36e07c9bd0ed11e69602d275f74d81c40d4b49044acf81d2940e51c8acf256210fd372476aa42a5d608d7e9fa3950d4c23473d8231101a08d2426201ccfb6dc318f22df0ee79975c03b5c0d937e1d02fc558eddc827baf6eba82fefa2e6bffda5e2794a21059324175f896616a6280c37f7341562edb6a8508cf9f8e0d6c2981815696cdd9d369e2aece25988e891a437c89986091e858f48a13f66e4f79b23113541ceabd757d822fe4fa28c21e3908d9494a2518c81e3a63eac7d331d258bff35bebc3ab2aaa0eb14b25d0dce9329ce5643e3d8b834a3451b94e8b89f46919f481996f0522a1ae2e3b2dce5d6e3c964c32d6f138d185c251a0cf3696cebc7e4202a62f51af7a5d4ec274e776b631559b53dad02edb1871092d0f9316e666324a5584f30af29f9b3e133f3e85dea588a9c8db6d088de3466b0b6611333d85af7c767b02e9a364dfa235d149818e9aeb20464aa1ed952e046907b23822b680d296660db7471a554204c32acd057df2767a02e2badbc56ca03644cb55bf820a076ff7c9cc9b261b7a34d4fdd1af3867337b38367711006e6ccfd510cd9199433f354a857db05419f7a924f4dc9aa9a7ea4ad0bc8b3a94b56ba9020d8b5e37b95b5c2de047eff48fc48c1c7ccb7981b67eb061a5091ee1109330c108e433eabd120b6e15040af1825582204514ac7218dbe408b7b75cecb6a9f8dbeac4179b6209d9578cd5a90b892fd0d1ca4328e84baa9f535e3cff142b973e225c6b3eda5ed10b249457886339cd0edd1f84feb5e6538c22f9903a4faba73506cb05490c0707c531360d38ba670e1d189dd03b3c72f8e741b986767f4488d6475bd933befdbce8cd9ac757b80f1bcfe329b64435fb3f18329c62bdcc0fd9f003642ef0ece0f2e0a8c5c083f54eb23b5bb4d03b9e44be4f7c590475683bb258a6cb224fa088505d21f861432f1baab2b21ec3241a75b74f2bebba7bdc17690459d0bbf760fc4511a270bcc90d80bee830b32588ddcac3556a3b8e35a57a3ce0fab918dc0d8270aedd77d01b230acd885218464ec04fec1cd6d3c68442d9037e63e6f6a50132c67a3d28d38afd8f53bf825491eff1209a12377a69ea91f42178aa9e7edf9a2d360141e1052f5151e37f78e547ded2378c92a9ba14594530e43fba76d919ee6555b9353953627ee48d8fea60243a0852ad2e864c47e93bd97a3e6455ff36c0210b171e75d57d978a1ae48b38db7d5fba99e3edf2f407a600a859e0e878a7a2dceb8244c111413287d67e8b4870f9cfc9001af1a68e9a5ecfeb2e24998a4f5517849abfffd72b4dec5b7b9bc01c5692184bd1741ab21b7926f070259979b6d02eefd9547036303905996e36618db78b1b376f33276eb43b862e542c275ed9cba4258c127c89592cc2e145f7fc3ec4f63a5ea45efe34db52c78c51ce4b1775f69bf0d9e0752d999a14c0335600feb13d32a3c451a2adac586d48a5e7077f3c01e1a8b23176afc6e25beb248cfb6d70e6f9afac738d2a1d3bed47e82055ef41e9dcc75522bd11208ef33b4dbc894a2fe366c361f79bfc363729234f7dc8906853b41db3e6ff60eccefde9e070563092224268375e47849996336f3e0b11cef70c3db1bd5d67214258c3714a6c64db88d6ebcd126a53d680c803df3fc7465db3352641c246ed71a1d98a208dca61806c8cb4a77b0d59e012323b99292379bace4534896a0d2eb9cbb28b192a5701ea469829483b58f6e51ef7686e004cbae149c54f12910e2fe8dbd893ec44d8172d7fbfde2e670f121ccf000088a14d2ea1047aa9bb0a354fa179ce6e4c8a42a49853ee8df535ee1ea4dd582320f9082ca4ea7d8e44935a892f0105ef9992ced08d28ec1e376875163406c5767ff2a8b8d30dbb68381b551ea75c87384f219a27c8144259f1f94cf10a57ba021b604e287073d651a6db627af58a50f5601eb93694a96af2a97abf7c07dcd398d978e1392da3bec81a51d63caed718389ac4dc244f1a70154c34b8e6ee9b49f242bb63eb548098d1fd2a676b625504bd7ba221138eea9e0e6caeb361850d7b144aad124f3928103a78297e442944c01e0cb52c5a280d0272035575b5726492515b12d0a8fa0d9bbd68313db499eb9f7b118798d1f8fa6f1e5a4650988a8ef8f0179f1aa5dbd21c8b28ae54e5762132be3fa9d9fbfd2ed3798bd21b40f98347e1f86a28c98936239dc8a8d884fabd21a4699a124167c6205a6c8db1f6f3efffd17bf95fa365b07152f620f1d6d44ad859855e2df1355145f951d36973677361c46e4a04068fc751858aa9d101202e9e41049dc1e84b1e9016efcfad0a63e1076df3332c883ec06dd57d8c24b192beaa7eeecefde6ea6ddf6488af56254ffd739de403d6b202afd731745c9f2b24c89e37df5ac21c5fb0ceacdf39eb589895847ec535b7ced110a97b7dba3576382f02c96642b5e44d1909c1e44cc5c3dd577a3701faa29a03bc1c32a40e913396db3e38ceb593329a787ab96110566adda3a76b167757e409cd7a5a181bb006cad550a82d415601d573d6c179e0c421e319eac8e636a1696c081aa02a3918e9f7bd888685502c825281f7ddc999494d3826118e88b7f26d5f6338116abd6b8758814b3af755bbf7cabdc6e57d0ac7591dabd4ec76ee916d6a9ed8f4377b4374e2f567c5a1fd1a39f54f450613fd12830fa265e5199b24230f95bddd878f360feaa91220e65986b043d972402760f6549c49041e8ba1008541dc2324b21d5247134663e8159f171e6fcf696f5e9a9537ac1624cbcdf5b22a812320f02fa85f691094daa555852f92aaf941aeda68f0aaa14833b836e08d46080bf53d718710171be42ca90df0ff0389f0e89976381a83bc5e4b14f7dbae853e3c39ebf4aa54f1cd326e96cf464670593acc4f614eceb718a3c1758390a4ddea71dad8dcc60322964e2bd1c607f18f5dd71c239b9ed31292e46e50d104faa60946d213d35354d09f18afd7856870edd64b5155f7bcca8c6965159c3376ec358d19661e2b680f1a1729ad09bbab6f01db6ecff6d22b79783779303160d7d44890ca47cef756c83b6eeb87103ee476b24d8a06de6513d65b6c7c9492ea32129a33458a08dac005dd3ac5f1a46cb112f6bd719686cdcbd7abc46664ed1c8f3f5c992108be58955517bdcc0992172c590338cd5c6bf2ec0daaddf8cc09bbd06b461bf48fbc7c03618d35dffe95b958b39555683d3e9289b79cb1a766aaf4d670a588adfc2ef403c0305d9f2ba5fb8d853861e251d2e8fc9e913467fdafb783d9e74da1baba7c1ef7a43828f86201388a976e3624fd1bc281a3f92b98f069089f77066944f5a7db05268d6431fc450f02600262d4574cfd227fa9697a388cfd1296408397476a83f1033081da316ad8f9e0d9920c899cd6118762fd0fa49a962f0c27a0117666edc3af851ea6db8ff2b626b9ca93c28218b10f97a4f16bad422c6bd8b9ecc3bbb6fbc305e92a335333111eaf4d8b4a67cc7b6561d362f8bd96e2b013923b9ceb404ed0ec6858ccce302adbc3110d5860d2852e5d0b31bcb79a95aee1e85719141700f27b737bf0cd691984cd7e9b03e3172e613134ef4c2f69db9f099d9ecc28f144d381a01c6280948978a2d779f8cffdb9c6ddb3b00054a0a7b1d158a6cf432fdf3e684cdc5131beb6ca32f70a3d69a0b7853f239e7152a9943f1adae43b2068e509483b52ecc251186acd66ec94b3559a6a7838c2deb4bf6b000e3629bf5fcf73a825439328cb0c2d34f2a5a5dc28b61d50b5c95ad97a8bc031492c60e97f36e12ffdd1a2526b5612c757679eb40d23c728eec6bba2766796d77fbd4a16bef2a4375b50c1efd361df5e59fb5c97e1bc24f62d9d113e5df895f27ed0d3cfffd6f6917a4a9ef495d37cf7fffaf3e1796399d162a6955a461fd98e861225601097acc61301807253ac087a68a3cb7701806dac028ce42152b84b4362ec6db36095f71fc9519638889553edbd961fcf5fb0377bfdc7318440ccb65f2193b92a81a3abff51bf3f7204dd0da870b3d0cfe28d2a7ac9402f33c793ac2ec9fe3e9adb54cde44687b9b2b110120d65212a7a0be687450b75b8f48c162e1a075c70961c584400c526bab6de1ac8db6d8ae23d6e952c9f90dd502e3d9d766e25d0af5d58a0386892015f648268e64bc17ad204b24a5eecd6c08ab43751753fbd178567b4d7bc83f8ae71c2564b7b9293fae8fe8141501724a9eb6124ea47e0e475e3f60a001395f23b303bca62cabb2107c02250e5bf405fe8f68b207db29258cc692e7a9a27681e98553e5362e731c69f81a48d28372ad25d34398436b5d229b04faffa80ff1ff01a17e1da36c5307836cabc8611cff5d029453334325ada79d9a2e881b3aa57893334a15460983a1381558be4a0c2e52b867323be6e069941c0eea71a50093ecab77de1938a9aabc28b374c4a8b23d82a7414289419e9ff58dd33ba9ff1e3ba197680449f22b1aaaba669c55687816253b503764813a261dcc80cc843c84322851a7fa0293d1ae732ec068b8e09af9d869b3bd70acac989aaca2aeafd2d891d3545a7995e9f547b07ff912e5e0294faeddeba49fe4f695d767696fe582292e5170dde8596429e4a252cac76103b44538b2420c6cb43fd924d1fd436325a85c58a97a138bfb6a3b0883dd29dc1b4f58e01d38a8e6b7810a58735a6132a0de9e51b495c19c8db653afcc8d782a9124396273d4c8761ecad2a2ea4a978e1e8ceaa184e51d322772ddf3ec671b6d8227ff45fbb714007b9fe3757a2e5f375b0af0fa8d4100f077671630cbc2039c04729e5735758070c383c783fb724478cbf354ba554340efcfd2f16f7beaf142338419609b0eca6a0cc8956d3f417b6487295238e7d487abeecca7336a8fecfb8dad9ef8d345d3844d8e23955015aa4072a55a6de9a760533249b679036dcbbbc229913808ee45e1b0dc5c70f064cbcfa10740860858d25a25f97a98dfc2d3ad2311cb89674ef9cff1b463f211b69c760f3e60aa1a1cc2101009ca06c56bd8ba750aa062ef364dd52ab57667aadadcb13d672955555506e10b8b698976830e0fdeed96a4042e03da211dfea819782479e56c93a240e5c8cbb9d87a76a700197981db6c6381eae29be0b08734e3b33e58c962abf0eb17a0f222d6030b4f994a6cc4f8f16e9092b8366c94516459ce72b60fac9392795efdb8f8d4906004c95a993b0b417dd4bcb8cea80be17eff114022cf569892a4429a05cdfd35fa049193506a664c2143cfb3afadeffcee0ab02befb065587f521425c8846f7b3c26b1d78e202d4aeb522487ccdd83144f8361fbdaa8ecbd10d9a93ecceff3c2f8f0c798b326bdb389309c9ab344e13188a042cc56bb7d5abeb328d7534c51af5b148560c2bc6eb2564d95a9c8ed154b35b684a12be4ab4c42b18ef753399ee58a8221cd8f303949af318f055f8fd28cc293c03652f94e56bc2a642ec0bbbf40319593080276fb133e98cec49456ec08a3f19bc9a1e20c2f05f4eae4941e3a2138c97caa1a246489dcbe79d296baefde6435da76faecc36a4479936d0da7dc4bc28e0e34942d078deb2a19036488fd8703651de5381507b967190239ef283ff4d8546581463831c4df24c8eb48c69d521518a8c96179a187c1ef997db82804368929d7958890e3e8749fd12a1c7df7557082a6f05a483ad3025d4c6a43abd947dd48d40dbe321efb35381f0ab3e08f004a24b0a543e50414f06554f816f8d4b294cec1c612f51d4ecd410a6f3bbfbb8246fac1d7274270972e44ec15311913485d1a92dcde431dcafb0f37b688f341fb4d42051b8cd42f39640157cffdf5a3fbaa02ef0be6eb467b0063bff174b1bb13401c2eb8506d13d144d3e88f1c39c6256d50552aef8c9c47b19042765543b1df0386062512302eabce92d7afb9bfa675e8809edc9de7e06400f8ff03402ed355af6f972f311ecb58928d817b3161234c4acbd6566337a01b2bac24132ef9d1d2de00b1013144ce0c18d0f7aa0a2801ed3a29385edcfd1763754dcf4c8fa45da79491efd8a7e0be013bc84672d61d01219bc8b4ba5efe0321595c9055473ac91612374a5ce9489c6348dde900873e9dc60db2bb69a16067b6d70471731fb329e474671d1d6418bd7c9f570ba1a2f0ccc908d264873677db8e42bb668bd80d17895b721ef76958aa716dd81e5133c2a8e3c77cbd625cd84a7067738856c799bbfa0c06b70c97335e77c9ab31af927c52bd39159a405730953a8883ddba0e611b8e4fbb5631ae8ca16902d539c0b48aa4e8867def6ef63183a4cd8a3f906a28475dcc4330b42e23227c7693b557837b7bee3a4acb1bbafff1233a5b3968ee10dc8f2bff08e97579f68ded54e3981d68ff4cc43bc29195b4671fe18698bbb9f89cf8feee11ef61b6f13470f8cdbf1f1003ab2c828c5583ad9359e3d370da497ba377ef19f3c07c378dfbd4ec6a88afb5a73b28cbdd9cfba38353719817ce443d485cdf8094135957c1894a6810dcd3678c7ee8f430248be62837772f690774e226bf1d2dfdab5a445e514f98336af049d22954c37f0d6bdd2f20ab62c431159c3d6c299079bfbf486d4a951a75a60c68a4c232dc20da48380ff4f268bcdbc15499b0ed993f8a6f28338c50518944ba7b5c15fbe3cd97a63b7e09e3da4a35be5852f81c46c9ab3085a2fe1ffc011919bcc71e1edd04585903fb80f7b95a1d701b681b73afdaf621fa23311683bfe00b5c52c6568aa9ca19c55b48b119d22ae909401a7abe4fcab42afeebf9c316bd721d7ed3b8739b73f412d33324c4e2c13c7a8f89affc47dceeacf2871c2eae2f2ee73fce677717b976783e6a053b439ebd0e8d683b7b8afcd9407bc6a1ebc97b37d0437ccea06bd8218a2e4edeade1e6a9aeda0be2caa1c545dc3f841affb66668ec0e231f4ab09918cc6ef01353b8b23b8c58a8c263a62b2e4b59cb91c4e6c13baa1a85fdc59b3abb67807f167ac6cce11a8bbdb20770f579f604e879f3566647166ce96106f72ccff23246595fc60454ea14022e62c99db807e7e57c1086142cc6529cee36e4a5fe6e15b210e6274877f628fc163ff5350e033a534218c04548679b02725cfcc3aa2ca4e8efe3fa2862f42d59145f59a2340c05f87a2021d55ce93a3ca0e1dc293ce73a9445329fc17789a706e12d686633704fd504e1931796607b3bd37fb9afec0e138a30c1bdc968a9b5dc4cfb0a4d46767d2673daeb335f7151bc830840e657401a9ef3758dc04b336911b639150383f7d30f9b60b435b574882c6f60110654436b4e0e405807923b98ac918292b1f0d6187b055a291e42a3b098baf9f4ae802c32a0d105d3fa49f04985fea8543cb00911f3d828d529aed5318d7569ce9c25ccb495c6e5db6e863100739c05368a810f1393291b18e7a289aa5576bd46629373858897723710df5a6a9c1bca1ffba07cd58f76b9b1fb400a53d500ba6ec40e91b4c9cecd875113ae68fec8e9f9f9f8d4e01c132953309a32dcd151b3c6b32a386641678282a2bfc02836cb61c6cd9f92c9313c9981c2581dff0aa77ae336ef34d9f5ba65a301f442f01b83073ac4a7db190d9ba35ae050d14f0c9e660f972fba2dc2a7afb642023fab80a557391e85958ad82918972e0e65841300f875c27cf6212b33323be08aee6fa6e9d5f5c3659a4e212196515ee00a9c8439fe08f386f14f45549a46e7563c34da93e28738c781a06d7372c7a93ec6307862ab24824ac19d3724eaaaac4a176390a1d231db9ed1e27488686f011e4aafe0b87a6f3dfbbc04956ae3509dfca6af797c8b823adafb0403fc33c9ee30cf1156a2c6683e989e0a0c52a6ee839479577f7dae441aad963d92000061c4e7593c8734f54b11e1468f0116cc05bbdfa76de2de693a554a39d3784ca1b791d0c0468fdb77a006a16ea5a1ba41a6dd51f7bb60c7add3160562f9e3f3a51139a4e91d35bd87e6c04546fe02736b5d38069818e842dd55dea6c39cf4695d824c32192360c4f218b389dbf99286b78d4c1d2d9aae0704611af64a725f45e690a6f7ee48cf2ee336ad09d3730b0afad03f8bc191493c8a09070b7722d1968029c6d77f3233ad0c461af1753a35f8cef1815100b93af3d34a079941838e3b32c985346d4d96b5f7c897a6cc214d752fdd0b4d6b1cff56303f0182c397b95aa9e22891e9d1c1aa0fe5e5266fad9ef825829b87cac87a3d1a3d611ee8c402789459960ea01b6e0299a9a75e1be7c16a6fd3346ce75a61aac1f8d69ed45465c75ba7cffdbc6d67bb1fa45f3a8da93ebd5462e76b53175613604ef5a84e330ebebc7592392c275081f2b4cc17c001987ffd52083eaaaae4f5ac9af5c922610a067f320be8fde4f173f09a99717d5672c7fe1f97c56454bcda97c4f5204f590e91a53d9e8750f204aef03d47e415ab0d407ff3f269062ec9adae4d3ebf8010bc18d53849b4e16983c4c949fe7827c70534a577aaa84825d7f8ceb074dc80718b3b31493ae4bdc01df352e3d16db4912a6f641ecd39133eef5270d210402b06b78bfbdc27e0a2ae3226f9a8872dc1862ad00fd8f1b85bf33a1b7efc3f8b5ad63b49624b9d71b90a74bcd48e739619804740724f3317a14937f6b24f12b1d3631a82df614ab01c63c3d53f172b7152c7304388840bc992402e13f87253a061a1f98f00c31ec0ef116f2de3d6c22d263348cc55f4b93c5a267699ac3efcda32b1daa8651269b3595f17b956751f367581b9bbc2b5a650ce2b8bfad1db8b040fa9f236d28e5582ed129f780e8047370fac046f209d8875e2ae0e7d885428b499da9169dca7bc62188c68cdd0b4903721e42f2d0a0846c83ccecf22f853282f7779e1745b1dfe0159f53a634df66ddcd503a260d549b310801345b1d53d5003eb1f8d12431b8f0db7bff8af25fdd1e9e6d8afe964a45294f434780ab86bac9aace5aa8e1af1614954dcf9a8c2286f7d6f517241728d248a0e6bcbff4fe6872fc027f57804da52b42d3e0ac1d44902acfa76cb44b5607064e313e267d1ba784ecde1f7b951873bbb6fc9f99e86ee26fef18d4e20a490e1228afa58116356776fc62e24eef0781f5be6457d6f72f4f67a51d427fa4eb302c3a5c955bed8433665295987f2183fba48fb40d3a78652167325ef8364be20ca224f3d69a039b3857d307ad79e96a1ec7326b3e551eb5af47a4d59e526c8ecca552a78790353de8685bb0c8151c33b848166a0757016dbfc3a01264e90702b18b31d49c60586285c22fe11d4cec9d25029b325fab9b61ea6119a6ead526c901be25ac7f823e30e7a3234762c037d35bad1de946608e29061b059cdcca710900aafaf96689a078748ed6f925aafe9be40b870ecb396e3e2948e5e7948ac0c87b364f0057798c17906e7a7a7ff6570dfc17215e0e00cf1215a11ba11454187d91e557cfe935553c9d93e16aaf1924e174bff3a96f41f15235b3c6adfc11446c78ae1fbfe6b39063d3ad81617e4f7e6a2f41f7c9829f1d34c1dc77c1096fa21d15bddd050cf413a3166f50d15008dea3be78ddd12bf3a0bdb810b784ce545963bdd9931074f7abf92b4b734845a0496d563182ceee636616d4d17d3e583b6673c1aa3fca1c4b8afdd6e719e623fdd1d6dfb1a6d4e789b0822091bb50b8072043110eb7e242cbacdc23258038db4b77c0972d26d4393fd9fca8b428c9de0ef6dcb75092716c51059097416f50e15a9277e33249fd4a3911028f75bde0c2a804a8294346bebaa6ca1b80e174711cd22c846c7738c883d6c2b3f6a2b694ccebacadbbc7adf274ed49ee21d016b4570a7fea6a136cc76d163ac679c1e3bf97c33e7f6d7d115690bfff30eac8706f40d485e8eca12066fbf1109b4347c32b5eb5edea8b5180180cd25fc82847a8d034d6b8a563442540099989a91ce3ad5852340bc50a917f45e5152e2d1f7d136eaefc2c457fd6621733ea98438d98938642a461507df02903ec246454a36f99fa671637acd7625e362b5dfa00c36f2b65bd46285734ca61d155138b33d4b67793db25e7ed45a39bb34e36d21d6f6544f8f73a0d615d6439dac854cc4eb934907ab5f80cbd1b82cc421d995d44f43a0efd985fb47f62acd48155ccde13c3de8c4af0a502846c8e21c7206f76ee7b6a5db3d99931c7ef00e8f575e99cd9d3aedd02931029aa4751f0b9e57363d83204442f2c5eda529cee2cb34cdfe87f594d3346def727bd6f2c32c4df3107dc633ea66822751a4aca7f03034a45564a04bdd3f91a6e7b6a584d9fc7448d31caeac6419b900a8fa6175cb0772b8eae36c4b04d561bddd407190d5780958a1035165d20314daa8da24d717842ad498ecfcee179f7a52dd41e64a104e8ce91f21b053d7bb6df833ad3376f29291ff961aaa15ea51f7d3c21a51dce56cc59a505f4d9dd7ea6a6af7329ff7eb5e6456936bb3620f541d8ecc7ada587efef37854eb8e86c1fa863cf94eb3f1415c0d8ceeffef05f305c433ff27e045557834e2b78c62a2bffdbb9b893cc93f9af6336431f0e3e676cf3c9a77e0236838c7f39a22102f3dcf620d7c7b567fa36fe70b268eaefc6e54673adcdfe0e64e33f1a048dfbd8a413139599780eb8a0cc80d498191c56d5c7fb80b93524994becbd23078523bb5e49d9acbbc898c576901c525f42c38848a9ee23a63938f19e82e9c190a3b1c5fe21764973f2380b5ecd879abeddd901d844cd9afdd1e0d6ad598e1ac6a825be7165fe685451df38cbc1edfa854fbf4f528dba03c73bd1bf0f3b8a32e955c2b9be1fd97e3d245a468c32ee8e2fddb506633648efa8b40bedab6cbeed606a84f317993f4800f9e6b6f42679d216d19a56af2f8ff2a69524fc00b59163812b7033d6bb42b110d3ff5d5189c26605605bba519844fe0ce36caa08994f981e4a063345e2928e91230077d76b6d626175c3ba35b6e82d693a78a6f3bfa650998af77afabf0bb786b2bb645f5de43caf01154f009d57b0fef9e5016a39ae013efcaf90845163d03362ab4c70de179d350e764834708d6f5ab421f2887aa9ba589c1391d9ac4104ebcc28cc40f1b9115144d01818140f31c768a111631ab8f2b5c25e98c2490d287fa9c5229e2b0599564728dc72fa0c9982738dd87e8d855b16c0ae9a0cb08d638e837804652b87cecd40a18beb04eb4ac32e7a245a6360435a1306f0812a68515440c537334883d8f64d53a395a5d7e1464d266dd11b2d32692836bbe9d2d64cdfc000513e2e4fb6078069ecfa07ef541f042e8826ec209f4de0a19f30c5e9d4cf6d8b5374f3cee815dc06c8e3a12ae93db4430579c98939eeb186ef82a8d715a91b3a27e77f439f3dea7114edfcc2f361c9dd67456e4cfb87e4d91a487e234405ca5454d753cdfbbea3aa8b5177657a922f004583268492124e2acf80e695ca20d491f26329a33ed9d662f11a00850a291de2aed3452a27127424e04079bac9fb28bd9b4303d94ca3ced03a995d4e4335b8c2bcbc92400452ed7ddd1aab99e14f4600f24e60949e73b63ea69a69a56b4e542a0cf90ea548b23b20e0c0638f412a068bd5847b5e502741455aa1a5744f919d20a11b248652a3ab9476095fc9e691d11980fbb27ff86d9cc42b24a3522106e64a4f7c7e4591fe4193feb9d6bd9ecbbe1deee8b0a3ab74604b6927ff028df0f1d91d338ff0970d6f55a6954ade9bf3f7e453bf2f71d919bc2fc5603702527f686ec0d8849555582b120de948b0fabd716fc90b19b0755176741570736fab07cf66b08cbe4d3e3c7cb42e89841ef1af5b9048ab057819609189f488ee5d7780ef79d75aed26e5c18febfbd274fe07abf0988bf6ecef2868b0247389e350db9806eeee0319872c10339ca35450495148f6b200b9589965c32d6c7b76e09a245e5ca79aedc7e5dc4eb7ca6bff6624fdb147938dd6c60b0f85f0f00bb527141563b01b1106b728ed586d3d0d4050c0bed1303f7b39cb781809ea2c8bf5e69c92ea00bd281504bd06aecd4ace2500da73cc380ed0c2e289e9e6bd787ed8f99757d10a24a853082757b15ecd005cd5bd7c21dffe010038ecb24ecb7ddd0805a5bbfdb71dffe867821caf9344fb0fa6483fab8fc6435b107baa2fdfe8ca64818c6ce91c52d1a76edfa20b64e1772f52682fa0e3413df6fc8a0160905101e41414bf1e191e2c731407b7e7a636d0b1819d806e773f4f914ee6b38e649fd5f4e7ecbdc74b92dc5165ef91e84e884080a40860b12a60b6b698e5065ce0a1dc38f8506d76bebed0560c15c624413d5f329cc32d4300a75fe55b6371016fc6fa9407fa291b408d0373e44168579349595d16cf65843a2e2aa5845153190376614720f2761450da7737530ffe8a2d0379ca738ef47f48608db50565d3076c0f774fa8872d01d753b9f69bc5aa9c00f692e69e8797378ff059475f3e15f3d760d5012fd33d85943cb4c392ab198878553e82aec96cc92ac2313fbfc026d2e91647266a072ca2f48ba07184b052e272bbc0c9258d28b23632e026b103ddc10c22e64cd8b5f04e4aa3abddd3d65fff483b54fcf0812ec441be83fbfd8a3bb6c441f8d2bca8e8043c77dab908c7d213191632115619b9c1288c45639af2f6351d463620657aec3593403cc041f2a809c4600f68f5df1c0617a3fee225be123517e5a6ec99e039754e844f5e55303a2e03dfafa35f9289edb279ac3362879199abf379a2cf21712132ee4609f6c0576e00730300eebbc5c03d92fde7b9f58a5625c74f7e20c274e09773a83d48ba7f099c3f9336b00dd0c6f21da44da84673f7c4244ff205373ae1562dc8a23553874e410299bd8f7568c1767659fd73d261bfc98fc9a9a969bba9ae6feeda21c1c995bd6a9c70195adac01bd673d7add967a854ca3695b62dcbc86cd977e35b5cb202f5f799e0cdcf4e6d0115832ad9186754bcc9601f8ff0340aea5daebbb49f744c8807a7ea5e9d2ea5dc7d2dada33020f203bf2eb97a50009138744a8097c1dc0dcce28ec2a47e68854eaeeff27ec6a74e590331274282ec88cd1ce5d5ea7486d646c9391ebfff7aa3e6f9500216552ce175ebdc942ab9bf73f024b4cb85066e23286c2ca6961f90de2e4a58caba74ad8b17ac0a07cc608a8f5510a9b545e299896ec9e35156a78c256f780d67e3483a508c78a307bb536b6f2706ccc85ccd19027b188717b4d33fd78d7d23a490bd29f42563cc9cbadab7d8c4086dfa8aef67b52565555f7463451a17e93d6418551bd60fe39d629afbdc24aad09b949d62e875d3e17666915da33ece9767027d9c49c93814b81dacff00d9be59f60ac792f9b0fd26455f7e4e2b41901674af4c97be030e129da76228f3489a25fa787331bdd7b1d62bc8820c2d191ef2325fbe0f61e34d27c3367ddffdbcbc37d0c77207f8e90a6321ca30bd0c0b5e5330fe28f319b13789d45a3d6a85bc0459be91b34d6b3a3ccdf8f58749f58a98884015fbda9db8a0971b1680dbdf34eeb8e36cab8ba1e21490cfa73242a3dad9cfd832a22eadc4e65c2cd4566a0f5a65527b1e2335a8dcb39e7b0e241b6ac3dc796cf56e524aa0153335a572bda5778ed141524e8706f5d04042b53d1665748d5e198c60f82b4d7b04ac0b798ab6ff2a9fa861ee45c56e7790a8f0b40d077961d6ae3ebcc5674714258917dab9ef7cde8e2100864aceb5fadac326e91861ef7311df8efa1c31ecc01812aaea58162c396dddecd9897d87fe9361630d876335f208ba361915d5b2acd2d48961f62fe8653f00a0557a524d6dda6b9bbdc4e3c5031542f0bb16a81e9e48e47eaa82fcbaee850afe68336c96ce10ba3d78fb4946cbaeca55aec48baa924523dc4f7efa6afbaebc80604e910680b84e91a93a31b1c57a0158402c35c2fce2d1ba0019ad22c395997464784699fe7b36e112484456ed2c652654ca64acc4fb166faaf4759c22c604faf1be8a9aaeff19063f877e480d7dfc5939567cb79ba47cd7b88042619bbf96452a7ca7726fbb38f32b718a35c75b51e62dd0a464bb651853a0cee692151e95c4f7d464c4e551d27b1e1fb37680218eccb30e46c3ec3be95cd10dcf8910a853dfa79edae1b24f048bf0fa7177e2f4d62f4e9fcbcdc7970d6cc8ba6b8d64c4d082c665ace6e7d92db3fb433b476e082f792e7d48398e2f3d112b427da9bf9a6a83dfe969d46dd12150de0e11ede6c4ff9f948abc507ac28563461ebbabdf8a9996a9666d1fa4423e161111011da47939bed1a038c6edd41a53331f3435ab446959fb9060f51a6e7a030cd3df3234116f190339fd3ce5957b0f149b0b9d14557811c8e80bac76732f89ca57c09f6f63ff908bccdb4fb473fd216bcba516a45a5a6a4be8bcc74c187da6d3a868dbe9835abd117a003d364e0fa5ea288529c5b896e258c0545eea6a84492f39d15af17a7d52a0ba6870445fed066ec3902b515e282451a93a94010bf09f9b897860b9a3844a4ba410768287602800f86143272fd39a3921894566136cea17eee2a30d45a86447b91939073be513a0c6aa019b6b63308402d03fbe97ab7791574eb278bbe3b13ad1ee138ab6862197baf9f679a9a65dc2f041820ee7400d9d09b5daf09ae702906837666a6eda22c11f2ea97a2fb0d2f2f678c3c33760349f1295a8f8e95d0ee8247068f6e8df46c875fe4603d4d6f9d7d073e09bd7b5e1184f1d3e6f0568fb589a267f6b6b8c5f5f4d3532c63098d8970c05af28d3bdf65c6debc038d135299164fc752c1dd26320cf20fe73c2bd3dc64ab31523acb78d3cc3e9f1acc4bb88b783759e4596a93dadfcde3a147817c4565c29b8f454d680aca88f3d8fa38eef65ea81c2c1f46e733492da8c38b6303aba3af7ad2980e1e25c913967aa3d9bd5cd54ce3ccc6ba16ef62609fb5d3989d44e67d6004d6f8ab608bb3641db0cb3e0d9ae8e4a15a753cca1e1f1233971a4a2b2baffc96c905907dd59e78660fe503e4d125d307bd3999f3c62f2f033739d3ab258af97e84d5a442c70e8d10cd128be9cb945e916995c6e9e23e973121f96c12c113618fd8505a43750c8a8e499480f0e3d44e25a4ab867ea3cc0517a3c6e804f6b54866c7baa026c42d1cc3f605ed4ffe18ad3c51a002fa50508d1c06a58047cab2f62d91e003f6e931b000f50b7eeaf0f97172dcb9e8e391d727f961d127df7574488653293e618aaf906232fc22a4fc14eeecc570a33b5fb7030f298f93d35c49d1394baf38ecc9c27fbfc74fca814092094ba6f97ca4e354bbd06105365588c09388ec664cacedb25335dd6d2d491ebebdbcc319cd6b3bedbd19c96113ab1246590fc4ff868ad3784905bca196ed82614e6a791973462d68bb2e0d2dd948eeab88d0ebb00c1def85cfd0c228e8fcabea700cb7b458470e78464c4523d757b602418052ef714948a6fcabca424a151dff2824613d6285ffb2f80d1e4f3fc0d70b6b4fa4d9311614077ad26cbda0db6d6bbaffc0f42562469243a365951ea0c466259d6ce096bef1120255080a22560ec0bdb849a20288a8e621014a8462e0433c7c325ba91d3ad3457cfaa83469338ebfd7600d5478fefd8b40f37b858697e40d104142a6a961e6d7453286898bba513512d9a631e6376d07d0b983aa9719c4d6ee2856d815322646508a757b38e54f9e54489373e5070a6cb7eed2b31fe5cfde20e38f7a4b34401c6f4a6ef3f80cfd172639cbaf95e3af78cb3108e2da5feeadf932ce2375ad4f58d78552d415579a2077f9ba5d38644a9618c8127b6c51a0204d6897472c616f5fb143dc9c0828ac0bec42935052e9249920e90be1ca15cb55ebe720004628606989a6f9d510c6b860a2247282a31b6e864b410840ae7872024e3aa4eda67dc780a9d6db484f9ca070d845e81d16bbf8c35ac8ea1d12cd7e4a639d0e00a9e388533025c8542f23bad663116f140cc99346ca25806936947509dfdb45ecfdd011b323f7d093fa244877e0127293fb04e0a3a6fa482f2e1f0a9b2e2385ade78033c076e351436bab9ef9bd5a4c4587b92815f9aa0152ccfac4721d3895d47566de778b407c37672cd2a3159f85775173437fb4e2a7215b34b8b137089aedc3e8fbb2528d1df19a22a8f88ebfdba3f9f5ceee280d3868455653be34c67ea42c3e6c95036a0bff130c32ea5b1acc4a39a535ea14b2637ef45d0834638dc7d7544a96b6e39ad478dc9bdaa8c447ab11472bf5086b0558984d48070b4a61d45217cc4a7f0ebb5dbb7d81f691efab5b3a445deac28cd4ab453e81c8b6800fb6832eceec5f0e7c9cd9196351deb667948f99d0be668f7452db9cf239e3d74fa990718bbdfb566e8ea7608fcdc84b318ea7ae024eaa13de6039a4ff47ffbed72ff5c3fdc2746e6dcab5c23765229c05aaac2f6fc2854b31e722a1d12b3625be6b216aedf82b31e25bddf99b47ab280591dd1e3a350e887a896256f22992253438962258b81682ad1bc165db7823153217e9304bdc09cd22ca7b350042edfe3c3573d44a623d3874080bb5c20da9eb6bc723348fa7ef8e3884b44b0a7ae5b14332492e0115b6ed8c9532b2c8051b5e514c8c1cf9a59950a6fa42d78378bdb40e1e9a42d924e981af5ba0178fd722c9ba563eea44999f65e1db1368b6c71340af53c138a6d4657c0f3fa94fb68ca7a7b2c9df2fdc1bdfcf9a084eea02e2c5bee288412d0862d700e9f522da6629b4e757a111c2dc1c4ddec88018f3c2f9b1f1f447fadf39041aebee27aea82cd8b7cdf86dcf324864ccaf26d29d7a09443c2df38237a8442089e01e64031c7b7eede9b26412f71a747d630f82cedcef5c63390ba022f173b3786f20619ab79c7d8632cf45917fcdd9d60cb9daabc7beac997eb3a08d5867301f7eace43d99a3e7fcb046754aa8de1ec6a34d6830daf5b4e21314f1d370d3992459461422d5d522c6b279a6f08b9710e0f883d868bbc7a6011de1b9656465e8e91303279f85950518a90e68ce7f479e15a5a67e0d77e7aeefb6327f89bd2a3d6b9e64e00cc37b928c4d8eaff36b2a6d43da96945afd383c1fdce2b1551e70df2a06c54550a8f6b67740a7d913c9ac6796ffff9e2bf06cbcaa9c8c0e2a38687142be0a0bd9d9344da43b6e379870a630ec024ea566806a476c6aa27a15dc6ed3bc21bd1135c20ca143b5263d82530199cc7856b6a286663d6e18c14137759c233bdfa433e206a282aee30f8dd72581847f2368cee3610fa4519f31e31513920230a41bdcf607c513729093b656fd9feb81e6fe45c753915a67d0ff07f22c4bf2af6a4dfe300fba0ee98f551d31c0c4e401e27b73aeea7a5c90e127acbabe96ac92b9461a652e0ca743057b11c9e81c0bfb8d8355dea2890b51f0b1a7732e6762e91b789620fbd04218cd4a754a41b02b5a93a598e26b116c3ea41bf14185c14f4599244556ca321379af9e3c6a92af551b38d09c5b294576f2def8db3853af32f73b9467f86f94a69974620d2e557ba658c3b8c49162ffd7c378c9fe3c720487370cbd0c1f4eab02186df0eecdf85266422422d0ac3001891b466ffac9327717f5dfdfa3c7385d14c006fe8734d4c6d365b697ff5d38cd090abbe7b33162432287a0dd116b2285f2b34bc495ca8ee473d426de4d20cd5471f9d3480813bbcd64a580c228c5af6c3a6516a8ede275f9e60506cf1cce1dfa0e75f66a4830acde63231fb6cf913ad3537b5191cf6cc6128fc6dcf1ded2770d8cf4ccef788cb3b05a60d070e63c3ae1665ac6e21ec15a0d8301f8ff0340aebdfaf6f5bb49d78c1603ea49aed84ebacb5df4aefa5dc11296779e0005b015fbfd2feb27cc470bd03870ee56d513d33d43d840985dd2ee92d50f8a5055ef75cf9bf053080a7051e1403ab498ee1f93501c9f84b29888abec3d6171311bb37024773668e25ac4ba80c3a462834a48435c18c14d0c614c545e82ec32036b577726c51835413c22151b28aeb7e8c1d7fa0775acae4895ee75cf2f7f47c89f263af2f94648a02a89f5da6b5e73d33af8665681cbd97b0b123a2886c7b2a943e50dfbeb3bbff16b04c5c6d4228d99dce8d5d0dedda2a6a16bf87c70cd6f3ca04be3724985b1be5d278303e83cbb32819fd5306f548bb7cf8bc40580ab64b201946b961b1defb83a8ff670ca6a11a454df9a7425bec34ec8fb3c08147f246a753d04f5f7e9644ed5ec6a01dfa8e194e65b0a906cb86f63c031ed11a9b560de646114e40d3e45539e25673c9533d0e1c7e60c7248a9ed00b4937a5634b6c8f238cdbc35771cd59efec9868396b2cc3040db9f75b6bdbc5c2c8e2258014154399bccb34a341ecef5a48687ef38b32f027422eaace2f4b1ed6c1f0af567d123ca53d152336d9b4fe693dc693a21033c91d2232ff9a5ab32005469f5407730001c86d50bcd048d7bd3a02a488a081937274418369befa5d3750e787bc28b5358311a72522f3349a687dea8265a830666401c42f29fe7b4cf4a59e03608e57a1c03213c5e0ec83c71cdc156d2b09e41f212899d4f16368f813c1c4c14189a73670673387cd925b203e6950d3aa7ecdcce8844a77394e8f5c651deb978451ca28412bf979b8bd4f71d57cc21f1c9575b7e92e245c8da20a67695e4f3a3f35fe7686048471958cf99a76594e8d178ec8fb15bfe09b4722eea4af3b953e8096db17224bced28c450ed83086857c6572c43a2a7939a26c990268bb182ad0630b6d41968f467b6eeaa4768db2ddee9653296f90d8d0a3068364075e0f754f115064e1181f94ab07e388ed7c3d54092fb93f7fcfa87720c299f806e3d2b0a593f48631f228ce7e6306bc3213e2a69ee74aba271df4d6e2333b4a85a87c29874454e17d29285a989af0ffa370b7015ee40f842d9ba5c5aa12bb5ef23166f39741e2020ada0d84b6669928a2a7f4c057d39f09b32e28285c2960bb926fe1e0d672ba42d9d4181c07253322721176c9c2388067e44965bcd446f8b755382a57ba6d74e64c11537a3cead349d04913d8c9d18103ab4d88d01b847f4d0141d96f7e2c73e11d96c002e8051a553ff9b7f584d81a7c105f15dcf5b8b1c4aac8672c2b077c91fe484b0c683b3a8f81eed17d021081b12cb93c4ac69878284a9131ece5c4e033d20e6a1e8c94c9dd917646906e213e561457029067f9cb585adc09ce9a0bee93a15f60943939ec15ba097f60a9d79cc3c2539d93c057752c975852d0d944954724ef059fd7938bbb99bb7e5474d14e2a96bd4d49049a10018387d1d4227736b224b24c576e0474ad227bc7fd399388f6b27ed6c0eddc6e054ebdbd06b6fe1acc3ae4d2613a969bdce2ed8dadd8fc1d14e351169af5993f12384abe7947228a32962d244c9056858acdc31accf94871bd20098d927f2a5759780cfa627e34378b9ea88fde412228a10935b318ed208979c4f618f42b3dd38a2bf1e8b3cba581161b51f9969af4c1261538081c18a52639db840f6beef94170f100e55309b49938fada440279f062865848cd803481aef6e5e424c5b6627e70c7184751d1d1586e061da9a9d665861d512207c58dc6c8330b2c54bd021a508fa8880f2d448022d93654822921814673d9667566a6c1c427b104e1576840fc0e9910188e4a87b19f03f944bcf4a105defbc8c86406113af1f2351e831620f2e3640b82baa2ab47036efd6f5e91b92e6d47b00fae5de520f9ef1910711f7b830593c58b95eadf09d05204f634db98f392f3eee63ce8b8f5bad4ad68d81bf717d7b2f95d51e8e1868f904c973e2ec9a3875862da80742c5879b71bfdfc8a8cd6cd3e5d03bfb8d1692e759bd6432ae93e196490c185babe2e559be2a7f13b699e918782fcf5a3d6817553b5913632a91cb0ff4359f0b6657e9267912f31cc0eb6fcc0dff45775671c065806457a87a56786339ee947bc4dcd5ae5b2d1a2b8b88cd55c255d987dc9db04b369285b08c9c9c079c237cf999e85c7912f1ab2cc6c1acc5313204a862e68eb7605842e50432e71ad23148aadc5c801d0fa3dc9cdb6df9cba7953d8221200581801997b0a9cfa9989dc5daa48495af9b8fef317c0ba1248fb842ddbd6241885e171f968ecdda06dcb8c30220b229987f37ba10071d1addc8a46afe078e50f4f173a4d135c90f995196cb9f6f9eba711470f278e9ac61b838bd0b750360694da1f55f0f61377a51b91fd190d163dc78c95a600850f40f971f2f56790a22de3c0a902fa05ec1e6a27e59dd1aaf283ff80a41b49b726cc9345adab0af1ec5842a10a3f1fd076f1a09c49885813a279367dca947d39bd2e7c95130c39afc85b22460af107af6d84f2919c875ce46a9d718cf14eb3a692f70bc813fe8d14b113684c349c22d952a3a9f50de08ad5772e7b45c4e7355bf75d51eda78d6d6c13563b2a964524f6b28517c6ea8cb08af61217bf04a75f612f2ad980fa46aa9f93cebc1362aeba4fff4a6f618832f8160f948f7fb1ab2ff4dc0996877c955a416f8518cffb10a3d4c226157432c2c22079b5246390334b008f1cffcf599c80e0b633144e537e8f708cb8b9e2d2ffade2b683fc558d2534ab6733dc9f19218e916cd47ae387f03af1e51be756c8d84517d65a78d43def9771dac8b67b20351f05a8d0b63b1f372b3839e624fbeb88cf60a9b0b52e73026fde24a515b9c5d4b7443e60713f28857c14491ae7465f6731b78a9a9a9f5530e779fdeb5d584e724f737335e322e2a5ec72a0873267683346270a0ed0a64739818725d027f932a49899c30502392e31f161c90ca5def291b8f3e72aa577a16617be8488fe073eda65127e77718a1cc5c558ef0c17f12b2ca8966bf166e3a5e6a49fba35bb2c0d6e9985316a78d782fbd69eec5a40903d8888fdfddf38533deb64f0aaedd5391d7c863d12c5d740b99062ee71a19be4fb2e1aa94d51563fd6dd6681c9a45b380be15b29378825fae22a34748fbbf122ed334de409901b94ee8ceda28395024ceee4dfddf6d6f1a59629279fcb65a124c24f2f9ead1bc808282ec829a08f6044196e34606221aaabbc3fc269817e3d5383d3c21457b80f5f05d4fdaefb18eb220ed8f1eee1f93413506870a07e77a1ca628d1f8c54dee1f77f1de8c738460715e7c7b43cabcd7fd258844f95fa36a382ff3a6cc9ab2cc2a2e79933c1e033d4049d1da880a9cde63ce0bacd41df31d7822f31abc3caaa013ebec5f5116056b11e4da8220cc1773d3e6e1e57d6489301f548d36c301296964669e831bbf62aa2113512295e931536b22f6c2120c50cc1846621df375113d66c99c8c4095643049b1457c465c522194ca73cccb6ed3d361c08de60f9de274ddbfcac84530901d2ac1e0ff15982d1206bf296cdcf0a388ee1af83c9bb012b48fd6d908b7fa9d9d462cfb4c5e72770ecee0bcb4825bbdccde382dac35c82d6bfcdbe46c2ea3f5a1f5491fe2ebe2aba70f9097593cfeaca1911d5e6aa073262aaa4c4ab284b9355e04d8e9eddd0b5163c1e218d68d0eb64b86aa061a4345bbf2b5afbaef29c21fee4e535a93500f18762086d9babe2cbe944055380a6d89c0262ab2cbb1dbd49a2a3a4c351e69d7beff83b201f51092acd7e4620a6331cef657722a314fd2a7f987f8284a69734cf492efb6efff3880b8579d803af99c7eb8c53e7d927beb44923b8797fbfd3a59a6cdd228f76bd332840205b444dabbf721429e5925aa9ce452363a36bd350208a19b91bd083363c50fdef9b9f6768742aef0ecfb027f17ac096202abffa1ad56b3a98a249b7884e5f48a7f343bffe0d4501ab4e40f8ad249131d928eded8a7a04c5701570e11c69eeaec4856509209d285ea6c502eb5ccd5ab0debe7565cfb9a575cfdbedcc410a5ba8b9a5ca01694e8eca130bf087091538c8a16c943363bd02df54c1fdd06c40cded2aa38c5e97ebfaa927840414f7b24b0354a8a182b1c08d504440dc7f3e3eeac2267afa6fe6047f4b0d993c81efbafa47099303a4c4c56f870d3575dd4d61f2c4eef3d797d22e3c77b4445b3458b74e965ad1606f5968e0b46ea49022e979a0bdc7367c53fea037a5dfa962416f8be40fc88cf704d568bfe7307ce3ad37f135c10faeb88b291e3e7f97a25a7863be45262729ad296d6ee3e200ea59f7614b35e2d42fc4c0f3c4d2bdd5df978e2ab8a548cc1616c52a940572fcb376a0b71d3469e1b63f38dfbbd893de2a6702fa4afd5f3819d9a52bddf392f3e4f9ab32ea4fc2cef0c8843066f7d1ea9a3695a19295c1e56a3e86593b0222c3e82f516c93a035950d1bbb5ee4c72c7e953b4a4af6af4c195f1cd06709ab2b9b56c6a732c4c9720e75762c7c1e9e72b4865f7379bedaaf094d647388cd2718cd4488e822ccbdf6b90d31ceadc529ca6c535995794a5f8add9de79dbfb29aaccda635abf86b724d7e21bc2143079266e6edaaab2edaef887543ed566754fe0040d7795daa746b8feeab9dc2bb3a1077750d2b40ddd0748e97c4a25960a86c0de8d326993a0be97db4bd972bed52ca7453e7c09150bdfcdc8ade82fc50c00bb48227a3d48132d89f42d36a9150af5d839453e212c4391e551cbceebe81cf877267b224b516feb8bfebdc58c80c3d1d49da12bb3bd9bef2d66a1bb030c8793da33b2d5a2bbdd3730efe593c325cbf71601f2be328c6fb0bd060008b4270ac21708c27391098706b2fc4322294f7a89031e96bdde3d795dbd0da7bb9a86a634cd162c8b95e0760ddc40927f32fc5fef783fea906817dc71867a86960275ba33ecd16a931d3fbc16eea1db943009985488c789a02873df7c8c48714c7afa60c1c5ee6f903bc7847a366e3563c99557cef97d5eb0ca74bb941b1542168db86007bad8262366c8048393c67e42252c389d38d96709d2c19e7469f683fa8638c76f8f3223bc42640ee3ad61e3a4b3242e1df3d994661882dfeb6dc6345c158a513e94d35388aa949f1b3405e7cda68e60711b7ee33e64524231dd2d3cb4b37cf092e6b23b0d58d58318331a1e05ca2b3bd5fe38dc29e350a9fad1842cb92f5a7425e4349e45a96f17ab67f209c9193ee0c8b7d9de0844b30f6c2325ec1bd82958b24a0c9d0441b2471624bc96a9863a0852124ed7320ce342bcb6884d8bb81fd8c1262f7e6eb4b08cabe2f24b257f069baf8583fc0cccfcf63563c5e7858c159f4730de93c3dbfdddf2e370ca4a32265895e5b19068fe190e781aab21594359a8e5b3e942d9982d38c01c6314c8899eb10f79f02d4f875e398a20a99107b710e010ff4a9d37bf7918e8fe88d756b1d1845bc8e36cd205ed108dd5634002311165c3e95ba0e0960ed9d5a6d7fe378be84bd5c88558cc8a4eecb73b8b49aab5ec157048db803c45f1e489c735c94f811919f131739db62398f38634c5bf5961d03d136b3f5129fff7ea0a459d548575ca1c69b537a5f142e73e9c9a78d00e9a1dc52890e92a7756c96b5977fed49516e078cb823a56d5a18406f3fc4290eb83cd8a6545eff462065505bf71a8cea5d2c1ca41a8aa0c7da9fe4fdf7e703eac484a3a9e005d0e328d47cdb9206fa9244645972146c624021ef7cd5831cf1e773bf3b981983b3a709f8c5f16f5ecb59b659c0d0125be66e316be080e8186922d19c58367bf6dbf511f90c475a2a5245621344d384992bb6435a4c15cc317ffe8b0a5f46d0608014870b28a456f44a1ff887c99e56536183a3c7753efeb1505090d3f1cf5035c6a8d84211eafdc9781b7575cdf37a7bc0dccc7b249f2799386a8efa6063269d6e48ee0a921b97f129b0c2b7d15129a73154d91c986744cf3417c96617412994cc593760ad97440dc26b61fb414f3c928603a2921ef2be01f02c87bd08c39d96347482d9bb693dbcaa118060303f8ff03402e9d56d5ee858c842444b03bec0b29ad411c9a237858e1d0745f745f34df17eff7bff6a50c6945c6e544b80857b7ea89862f7639c0745fbdd7303d7f861783a4d6052c90b0e677cf6c885c5606484587a8f5773f77671a8f788010b867a12f100dcab15b0c68d50b7a588613da326cf59e5436c83f8599600ce2b7037dd8535837c112b80b54f2faf9c0f84bdaeeac094ca6ffeca47ffa549ad726c54c759823e934c1e1494e2ed0ddd4f9f7262443eb2f2d53cff57e85e4e9c9105eac62ae979503150bd9bdeabe34e7d2f4bc7e8eb5dfcb264bb86ceddd884dc6b7216e42b5de0b6d930f51e4a203573d16e4d2082e63d7b09e2264221b10224889ea717a55f3ae740d78d3b2678563d02e28090e47d14440f2844eca547fa475645e814d3e226055798f803c26aec6e70709c9d1b207ea765655356b4f6baa0f6b74835a57ff25d21da6cca4c2eea6d87050895a464447b4d16b86a2abec4427e70d9e184131daf3c23d42529e049fe81de73821af2cb6dc901d2c4c4142008d6d1ad73aae2639dc54bda625447a8f33ac9d0c4d8ec1bd4b3a94a16e979fc1f8c8b08695985f0c03d77f86ecd722ec8004167d1c6674ec23e10f762d206821d8147f8ce6bb9e17c45ede3838a8d5a320ab934c8f48090d1c5f111fe1887978a1075e16002231182a9c3f10a3d87bd30c81dae0f0018960b52d9d8708b7d2891db40a12cbcdca20762f8f8cab0db6d9a211e96a3d2f0c9d4470e155967e8f25005824027f1c4188e94c255fb2ad1d7b7051726b4999b72edc8ae3da0296b5e3d30aa5f3aa090de3cbf9c470be710ca458dc1838cd64114880fabc762252824e20a0577b5511538282b04b5d26899691f258331564d9ac4ffbd8f61132c9f690b7aadc14b7a104039b0cc4959c203cde72e0bfc775e1147719952dbf84bb7464d854874626e88c329bf145ac746d0c76287ff477eb340b7521f501665c83b11dc68ccf22bdb456014462fd8aac5602bfdd48c66d16276f5ee5d56cf90b5e72e1d575ca6fd8dd4ed4370d2f3f85d9f42ef2910b902f2b93281a78e29e139a70d0cfcbcaecd626033d02303ae548e11f0e6f284421fbfcffdf091b746dbd049f3c4d634b534e7a5d9ae374399690fe4044709899f30d5afd4a3884174d1185cfae57b5fc675cd4c0a3428acbaad68de750b3c06fa7b99d9aeb2c4fa8aab3d5922926d320f63b4d83c83701b53b7738d3737b52eb91f885d749d7d3fc592cdf5435fbd31e1fa2166639b7293ee02ad63a8454a1f7b6506a04fffffef9d74cbd759cb0cac097340886ae5b4b806710443fd3f0eb8456ea1007f9ebb1ade6dc4e6ba5a3a1f234dd35bedb014a1486cd33f084ad23cdc6d0c639d23d56cd67c36d1afd2e1c293d087cc8b8447771aec8095de3eebd213434eb5c1bb83974b7fa31e116ad3ca655cc06693c4fc8733eb084a85ed94530f4fab6586a6d40695ca23778b55220fd8ab6543f7959b6a9b30f6f6f3b308eec7f31d1b578e0865e8251157cd0432817023989080d901615691045beab12eaf6d889e3032aea3087129f6174ca3a3b53b8aac7eb3dd8c19be96c161b3f921f93cf85d5b4206a856b787aab8cbca035b0e6bce154ee3721bc93b6a0e4f2220dbe1e8337f9ad6bdd89aace1a7245e3db04c997d8e99ed521f0b4712cb9ec0ba3be901d318e8378827bdf2d5c230eb65ec67d472acead57cc11b10c127d52418a108a998af7b605d01c58bc6de62844ccf20ecbb6edb29bb3c7d48d0b0a4ff226c256476f19cdba159731b974baec2667a697008759cb582c08ea7ad29521d683af10c76755f980175bfb81438ca6d2bc76c226878c3c33f96906aa5731aa409c470c42aed1bcfb46f41f5d2fa3381ab0ef1c9d6c96420855cf3c0fbcccd8481d4fc486e43c2a403e025701d03c7df23b48bfa9dc48725876f736ef2b7142feba6925d2567b30351b9102dd8ba8340d5f24a77df60cc07757f7759d62bdccea389546537bfd027302f6ef851004a3339b33b80fd22d97deb777e7766aad096e4fa88f63899a9f71eee827b09988cdcf792ff6f60a4e3e3179783f555d6c3b77c420bd7747e65087c7cbd2f6a9154ff38e493b40456c284eeb83d8de62c2292434a72f8ba384b62993a972e32c80fb1d38fe4f576e8400fdbef1eafe60885850d2bd2f5d02cbd824603cb0e17ddf91f1049ac4c7522827b598c4ca48ebf98d976d4f217e1780b214ca6d2d25137cba390bec5bc92a97e28bc0d0698b86f1751ef88189139584189e9db5f6f0f22baad340debe964c27ab0c5ec465ae3410e0ede71e1eb271274b0182a185c9040c1908198697d5873b7f7f2c8b48c2d5b8b8588c766a388b4dfebb2b07dbb0e78a96f4c4a0e990c8d7427bc5f68492ebf58d208805b36ddbc528e186608ea24a9983e109189472bfbdac8ca264c40f5f82b4ad06e01b0e94256dd68865f605783759ad1b21af419b833df8b119687d2e6fcf02c4710e5a2d0e3aaddef02321dc3074845e4c3312fa406910a283f62836613085e2485b9efb461d2ecad9d2c91f94d0c45241e2ba051167179e74f366388f22cf99a344769bbae9691aeb40e6c1a212055a6169b790b53c42169cadcddb3b410915ace49e33448e4e145916e1143b3c90483e8e2b8461623b190076c2fcef5b8662ea3d766241fea517e4e40427979aa18eda15c90fca1325e9f5c5d2e042145c92606059e28ea7c8dada103c432cd428fdfdb8588c948cf375d6bc05d6efe53ab507ae5bc25bb6b6419f1d2da4f7d3251a6bd8d4e73659a27fa7ff5bc27aacc6349942403ff14deafb0cfbe965981850f782fbd200e1a6d02120f3e70f64bf6818ec1a0448955168ca851e50edf933df61f6f3f577aeb33ae60396103312f732737b9d531c777bed31651a4f94427be51e32a81f0b0f3b03fb675f3c8513d6492f7a54eb56fbfd29c0ba2d02470b9b6421c99d648a54e8ceb96ce9417ef6866a095a8447932d0b16f4aa5438e842efa3ea5ad17d05c1ff739dc1dc39988986fe8b9403a075c3d037f847d94c22b04becc3539e97c67eef6c09ed473284312b3599d66123e31653903f4f7111bec4b1d847dda61b8f5a9fcc115257374f780691d6827e1c80af52e170814401228f969306b873c009dea1d1815ae33793b2979b59bb3dec4d674c89e091863d1c3180245b19e725f549a591650648dd8d3af0cb7233a5cc938133e86de8a1e17d6274a659bccd8b239e1db4251283cae2bf42372f3f757deda7d2ead7e4f68bab6162987278d680502b955d27f122fef5c4794ea08c48a83edc03b54b72aee770d5a06fd66a92ced9c61506b98f418c394953a95d8433eb9e515f87d57e490f9d236552e65289aae0e3c8f1bbe38b598a88dba079a7014115a44448c80e97754d0d8f1c434043b8ef51574141e418e93d8e733fdda573e7657708cd36791026cd780d85412facb5c859221ad046d7a06db2fdb001940bf84ae89904970999582b179dcce397f9d72e644825512c3e92c6a038e222dd5ad5f2a1497e2a1c72d716c748d3fca585069e8912b8d836bf5c3b0b582371c6510c076115f9e8c3740eb21fb64ff5bdb19dcc649ee8ddfc493d655c30d1d2d1e1d70dfe99e227e6c9ceaf586b5b7114c264092b40b3aa1dd22d418132864682134b24764551ae462c3805c4881ccbf3b79df6e44088cc908d9698fedbcdcb047dc57797ce73398b271cd1dd2fc306f2b68a83912e921904b2ba1d9936331e1c64b65142889345415c494fed65b1e1e9524592c3d4446c9309650c82bc531242e91796156cc4d20f6ab2c77238292037a6d68651509893edb58ee132a9e2f05389d94a5d70932560a5698cd03fb827031dceb785244b5b2630ebbefdddf0007d2ff23aac8dce2b0de6ad54d292f5db9f5e9f705d7863577b3919cd5d4d81acb46f66c1585ebb6b0d8fab588ab0eb12854c067f1e0b990caa5959876bdf20e14bb26dbc95c3b1ec9eb3ad92c9f1597e0fda3693d101dbd3ea38d18cd45a5fda77b0c9bbff04a5c338aa60acb8fa3a09d626ebd6cbc93bba7b6df0b59d3ce334e68c42a59d7c4061eb495fcd8ceb187c6126ee6787ff2d04bbc84bd4ccfbb441148ec0a9c6b23e0c80961d625cd5b5bdf9f9b46cbfb55f1b6bec778f75e348b037f733e0e5c7c14f3b8e4f84fc16cbe3f9b0f0102401db9a54beac9fd484f9536b0496f4b058bff6396420ada69821b41d3c3a4a24c82b49157218dc30b2dd3719710305def64665dcc8116a6240c8c28d16f11b1e571ae058c95c734e4f2f83c0be84e650111607c38a1362773b22a0083bc906b255a8eab0730565d33780180fb676727296dfb7d0dc3baf73021e8c5125f4dc6fbbe55ff3de7de3eb7798f13c170ac7e2c29ce6464732c1f252bb2e5f35ef0513acafdc2258d7ea6132c83a28646b39b398f92a697051ce8d87feeb87f6640882419804734f253d778dbc506a39b2506749cfd8938c9164855722b4ace70257c4f1569005e34bcd90a1041b55435d393ce857bc374c20be3ecc2631ec23b723fdbbf3de3028aa9e89366d0d27c40b41577fa40043156598fe2a361e47bb6153ea605d70bdaad39efd86c65b555c86f6b46b758b154d4df31e4f8aee7656f5c8055fe9a6cb69ac5e84b9644373ef0051b5bc78fadfd9b95381dc03ca422909700b6af79cf0a39b5e23be3baaf2715528ef2465c668255908dc7c2656649cda7222f0dfc3a1686cb77c4483f88d5855b0e2e013a6b7bcb89d9a06af9115f58efa0e6a536f3d53eab250b7c7926d13b40771bff2071b4f5d0a6dee09a18b247e0144f8a5d31237f5758ef8d32ac79f882aee015a3b6908e5ef38bd2a162b3fa12a04e17505ae02d8e4f0ffa8ce2b55f290d0ae94626159312731834584b4cb29cd4d5f832477252395c94923692521664c8266e8da0571882c6369de547ee2cdca36c7fa73425ce6d3c2cbe855b8e9b6cbe907eeae7875e647a9b7b5a9b12b353b6af03a60ae5f5eaa4801dfb0d9268a908c0678a3b00ba3428cccac6032f08d6fe203580b5466698c7120399a5c7ad59a156435989004053cabc48f0f8c4da8798bf786c13d1d82e19f751d4f0bc02147a03c021e3a5e538e8817e9ba889769764f48704700fd731f282ac689f6a627c6b448d6b1c0d376de7e17dec4871b6cc9997f16c5d0fdab58db6ae2195b884a5fb0811ead1d0963df1c5e4de03824cc2e98f3d6a97bf6cbff13174244613cce236ccc9c44fe5348e44c1d8c81a2a6ec18d9051048b490bc99110c3e3feab068cc022b58ce1414e3504c6b1405759a944d50f042f24f5ddb40eee2ad41235234bfb81ad27ed4dff718fb5a05724116774fed20ec0532f6d1fcc9680bc60980cdbcc9d93f9393735b990f02637f3c53fba9a3c57751fce0de0ebaa54c0480b08be12bab06f8c51e4811e6a0c1f6615865aeb35f952d68f11599ce5a0f263b098b1437f5b5e9b48da2cb303c97d4ecc7ca3d1737991c8e72fb02168f44c5ab873ac96b69cda41ecb82dbeb8c8f866936c671ae8567291689f14b9057636b8a9fd8b22929c4bcd9c83eb31a4e717324e306f404b77026b73b8c07cc9847be0a166ae5ea63c5dd0b3bdfdbf618c398d27b5746bea7bfbe5a26342300e142f5c54a25e4e46401f8ff0340ee7d9df5f53bd721cf32928cdb75ca76c892ba05f22e634bc13a6c8d4f92f172476b953e89763963a36264418b217160f602c8d5d5d543b747b8ff010405a8e37c58e838b933f71bd80fa14d847259c616c5677a7a450eb6c609af17352a52cee98cb281697290231f6c11d97633c6334a80324f2a04f550dba5e94235161e4bc5f64afecd3a2df70c4e6b25796e37a287dae10b75069e5f9b19525c9e43d63688e176b51b262732005360213f5c7b70e410ae59a3d550a3b7c2477445d26175c1e0cefc88513f8052f6f132bd45b86f32c81dc7318c0684c610a0057af7b4d713a2e071a083dd42812778411750f6ca930059fabcb5f6f5ea7c07f64f6abc1963b0cc7bf71e722881d82eb81fda217841c11f389c55357daeb5ab2768ef75d8fdd00e8c390cac9ccf73cf22e6786f6e1107b93b8b328e909992466bac1d62819462ba03621a6e33a1ea0d328e2a72e4476f3a2e82273e7496467e7583374ac56f6bde6b95fb489ee40617e6d700177249c3d9e7b744d95d21bea3818c60ea8527436b1a4d4522edd85032109a9f91b661844e750d4abd34a0c161883a104ff33cf4d2a38671e85a26aaa8ce3491b5eb85b988e3d260b6768ccbcb3f73ecb65dbc71923225d0728a9699b48c03449048f62b43d6c1f9b4d66f4ee763ca30ccc19f6341b4fee26f11e1790a34d2b702d5ad450b386486a03ad70901b08d98af7de92de6631e8d63db18ea21e04780692cfaea2bd2df3eeca0af08a6f809974e2b502c28075a63bc23a968d23c0f97661d09b5661fedf7d73858abd1cdab92113ccba2e7ccc28df3cf70f19620facdb38180eea883ad801bb18c3ac1993dab858e94521ed3438dc3618cf4e4b723e4b8c69810308be1835e4214a6f35361cb71e3239e027ac62c36b34d5ce5ad52687b85be3e50ea075f8f81ee3111315c7c9fbc4c4b176428c7d16a3130b983a3faeeb298374f4f0640f135df05a8083d2c26ebed0124b35433a23b9147b35a116bcce77ed981cc47ea8c1f626ab561c4c7e1f38fdf0af97e1982122790eaec99b775d4872361488c0750f667822b8f5555b1a1300fb1dd33965ea6c2ca8d24dea0d9db60ff67a654004e6b6b09e4459081a42642d12523b9531ba4c4f3b05d6d2710fb5d73e102cb417b0581939fe622584095188f5b441301a9573fadafb1d54ab4a0ef0b7c6e3cfee1ce621aabbe1b0ad6119d1527ca72deaa84597a47c91366c4a3934dee9397c9dc180fb4f3e4e9e844003fa9a595f48f505783c6b780c0f709615d66f7fcf57977c0dd2ac75be1cde71b03df7e2f7d9e7ee88562c386b31d39546322413c0db252632cbe39b1b58f88f1ec9b137cfbd38609b54a3b38f38c18f1fde299980b562f0ee27178605ccfa24302d50cd35443738a2ea18dc74f4a6f608dac072f0870c203a35cd45c38c98bd297805e3ace0a59a9f5b9dfe91e2497da6d27f368aaea4e5aae829b8989a36467c8807d0bd39700ce5420c21542b8538638573025df33ee1b1ddc0f7820aca09aa39ae8dc24d1640a72d8526b2258ce8c45bba4102006c6c14ed7bad60ad8c64c3a17bc9b10aa10970a5007932bbbcbacbfba328f8c0b4224a1e83ed28e5c30f66ef5c37cecefc21bdcab2eb5ea2aead3f99209a806af601a0f49fe43a478df7a179ef44ccf2a5fc27af7b1e937324d326bfcf86206e8c7611d8141f8696e0f36ae031f9f3289e3a29a9d595334f737e985d8d22bd86cb644750292246921b0046a7e42f86c2751086da7d632d0b034b632b3603790e0bc812d5831ad5fd032bbbe2404c86a4b51d36811034ec2063b9290b9307d1b5f7ca2c53b68d12a5f63af23790fecee81b3cf36d3601b539f70a0a89d6c38fabd21ec4709121fcd28eb469b2a4294880a8dee8c5d2fd9094336ea816ff9769b07ebdc7c5ef02c0d77c37c0d9a6bd18a3958ab34c59bec9745b180f2d8b6e08e059c2c00561e3a5a8d77e6370482ada49018f1deaa7751a0a2741a217f220fbbbc0eebb9a916a7f1c8495dc349ab75fe0a1422815749d9a9cdbf824eab01084376b729367943f4826c2ee5c2f4f40f797d38cb79cc4bbcbc14418b893595eeb99d51cc720fcc793b9feccffaf78e98ee6777e112ab3b7a1a8b73fb101023fc158002f34f5160af2219728545a43052374cd02640240e088011d61b2eaa0cf97400c0cc2085a8af791c9d596ca30d9129c9f695a4e189c785248f04aa05b42421a215517bf27b493038b743ad51d5c669682329bef57dae35ea0578fc917dbd8f60ece57d7abf25f66bd0b04ba80abe38f7f84164dcd662e22be0fd6d08a33918ed120dc77b68077b47f98950c5929ec29b2daa9164e51a855d3565e5467766c0f2a5b83894d4bdd5f17b2ec2e17ba105f71dbe07893e50a90f7e3476225f127bba063bf3584c57aecf7c6f6e4951af6adff34672a72a1830ffdc5004c015cf850a831f6558cdfdd6c01bbbcd84b83ea8aa0b66e3976050bf0400a3e7fcdf6f2940ed86da609b416d0c9e7f7bc8f48796c8da65f56a930949a3c7ffd83cc9a5c0bb9d46cd9b63d81cf7c82c5b48837460224f50ecbb4fce1ebc5b383016f2312c8d8d0fa15eb3d0e880f007081d9d91e75d13dc79e1f0a44f2e9cc07a2d7b954287d456014e7bf952929c8a22a70981215b32b04740e6c8c7cb1321b2fe1acae4c8f98265c859ab81a03008c91809fe506c994e433d586bccaa2152270b2d3c771ea82e4126a161a15c59a568d678b92dc872e7d70d1ea8d2ca46a37cfe02b82598002dd6270feac18a5283c01cd390e3b147ca93125e6db575efbf0fd05d441bca8b2abae58d501aa7ef1cd14fd3a39865349773eac64edf0a31beeeb074b81e965c395b0c1546d367a8a76bb8d95b450d3e3a9f468f060f7137638fe0052f047b42e498eeed8c35a37ba6fe378b1f2ca7a0c269467b6a958e26cd7714c59d40c2b93159ab870820963f77258b5bd1e3d2618d504d653cc0a7a20a2c810dc05039539fd15c0abbfb8477c7496d32f27e249588e2eebc43b5047832a81129cc37b77867363d37eccc6044b2f6ebbe18de754cb3c2a6e1b7f33079d15ab1e5e8cca72f807c9a837909292f395fb2cf90955d7f00ea26d1e6f0bac8bf7e310c2594d280350941835b2df9a233f54e85bc6f6e4fbdb6758361f60f9f4cf72d3ef6daae1a0c527eab6bf1e8b1b810b2ac66dc8ee8cdc94f09a135954377018d89dde40a6ce8f821440e8dcb773a7cbf62b79bd7f73059d9097e38ed52b9daeb98ad7dd8f4f03d3003cd342f2014d64edeb0a64924585486823b4689c87eadd8f01c472d8a94d44c9e28e030bb246c39dbb80751be496bb3accf97b263cb474f912c447d96b6338daf04796b0e537c9f797162b1f7607fd09f27d486ec1a831b638ee67b12f5adf3a4bbc794d5bb3497f527b4e4a843a39814768ab61c089267e8adf0d23c11a89ba59336ea16ef4ee8cc5a9f2b9ff31fac32a2bc653a422c7c9b135ffb7617c1abdbdd2c13a69be8a47ac2a48634da223a2c9de4a654114f449b69a93b320fd89ed8aa332cb58e79737432167e4006b9c363b0ba47f34c6004e0516ad293bdc47d05a680c2fa45de405f80d50aee8d12c4f7350cfca39ce9b05cc8c0fe71201584eaf90bafbd80f30b005c3336b21c9fc646b3662bda220777426b0db58d3abfd2012dff56fc4a8428084f67f4a9ee14b51561cf58dc51de91e65277c884a0f4ed43db343fe191c4ab416c5eae2cd427883e46a4f70999e2fcd319ac32653a0bd4f71716c99ac9bdd7d45bb7f09cfbceca322512faba2c6034ad821c0317ba6f858f212004e6d4803fd9c0ea711cb1f6cff91e00f7e368ec8029ce70a4eb9d4c880ff6e40700ffcd2365e9af83e5c5200fef87cfae69f06c85fda542872c5f1a3c0582455d1a50e54ddfce67ffebbeb9389657be6416d29a49cb82b6369784e1fa8fd6ea8eac690dce16be6936a6c3db1d03280e58be54a9e4ab19da135a3d1392b3355e3ca31776478a3ab6a1dbc50e2754e9c3d3c247b9466ffa1e99f4435f3ac73eb0bc62ffb5e4b04b8c855f1fc05f4a9011a0f3dfb1ce18bb893606a52e45038a5c80b72108e08b35f7e14caef3369b2d74236806183925655f1809f087d2749c9ef3654e84d5883220abaf1773132ce21a8c68c1d9cfe372cac9951545011c33bb72eacd0c2050e0b5bff2df7583ee484a45a99566955379391c4c20954c71e6db5a0d6799b8bb8de44e9e8d03988f602d783a837d292006fca43cfbd7ab8f4226b38202c19f73fbbf7395eda661a6c3e18d0b1addf6cec6069686007502e66f750f870a65b67f5e26e636a8a88c7cfc916569c6c88c4c9e3ffeb5dcd09956f8bb980afe6589732fdfdd67380439032170bd312494d58cb6e99c4f563be30b0ddaaa64e33a23a649938ccf001646c0c40a139e51a40c5adc2c6be9054d9bf29e7776c3708e8993561b40b761f359018030f42021cede5344e961442d63f648c7180a58505134eb658b900b38ef6932c3e80b48a6d169973b74f4e439632d3c83dde28808b51c934fe09ef37fc91fbc2a841c675140ee23894ae9ce43b62d625c29f100350d1ec827dc5f5380b5c18e46f16f45b58f2d05b571314643d5fa360d8dcc52605f533634a99a4ffcad6870b05754fba7c9ae70e573cc2d506de44693deb6fa0b7fcfc473a6e80658eb5a17cdbdd48a2421d5e32818cf9e62caf8f1158c67d39db791b260340e5d6b4ba762b57e04ed32fe6136aa1b9bc68977fa667fe7b085f0c501c217699b1e44de38fda7b61d7f0628363925d06214d5321e0208399bd4dc626ab4e10300614443a03480f7f30dbba2c55e839bf08686d9a7da02582cb49b33acaa35bce361576f3e03fc4b1294f0a2f2307411d341005f25b8fbb95677748339e8864408ed0de7c25013af4917af42330b627cc408180fe71b20c4f4cdbf60e011e0e9209e4808da6311bc18de32953503302d1cfd34996d33ec16da634bd3ef61a69dfae06a557e4222c704da19c6f4f2593c28e07cdcb16ba5cbf25a90a1384542f0ec0c919d48516c2884c5c9ee3b2b4ba9e6f5e2c202fd0ae58858617e9747a47e33c989ffc4224b8a0ff4789505a22c0524ef352f20a94a373d84922c939c283bb1f605e35e5c0eee55feec7cea3cacd08ba056c96c8fe203c02aa07db9ca78b22c91a7e214d53abec5afb0c3cd16a47a50295cc84b95238b329b44463be2fc317fa29854985d2946eb1d034d69524d488384a3e027e15077bf7373e2ff7d77b8d9167e11d31251f6992abe6a89ec4e21a513da5d404aa736094a932eabf1c94a922913cf6d74db178cb0d6b7d98710500e4e0bb3e8430f9d89eb5dfe25ab0657193b83a61dc05a6965e67c0167d3457f3fd5ac2cd28403e4af9082bd0354961423d3acf69a4c420ec35d11b9800439e9162b5f4e0895db8db7db069435ee763993b18ca328549cf1a9168e3a12ebb6a7c0808e38e4da9d599fd502446d680d1d411b1ad3918770bf86f37d54dfa6f6eeb25e4a3f82b448ddbee021e43750e012a593b35918749f5b8fedf8f2227546733f28ddb920f5317fcbeebc4c8339adbc127452fb458bb4b82d97605e29d3893cc809829654fc6ad1ef26240d9b16422b2092e8d20b158390468a27264fb629ae6745627ee00db05dd4e72b396a243405beaff1e9815ce2fbe4484f425441d96b68dad579c1e516fde5d8616b68f9820f2ea1f9f75e2692aa3fdf5decf45f61fbfe4010c970edc82413c608a82d16581c53ac73073d8789a4ea897c8ced2534b4c56f310c381ab09501408ee9725aaf26e439886f1515fe43fe8923697d5d3a0df5abb54fedcfdf2990e675cf1ca3fae742a0a8a7a77739cc2e156154a2cdee0650c521cb08a16363dff82aefbe5e24c6e28088b07d752cd0ddfaffcd406417b563b07b761be46ce01c9e6b58dea8f453076dd9f48a6ed816047192f513417cadd43ab7429b43932f3d1347d114c326f55272def8cc819d67d49d7000f4e253e655f8b9f16f2154944f8b16786861209a6cda6b49e006b4e02a3c6c56f88611ae8c8552c166ec11ce4aa13e994b3852e50a5891526f0a6c530af507cd0ffb49fbef9e99beab225941f36f90545d447a686bfb548c54e09a02c5186162fd684b369f9a24f47730713b62316cb28c8d8409117f66e07e6e9c6e6e5bf84ea10621744accbf496549eefc019036abfdefc75914461786a8a4797eb07bf0ec4765e55e09e5e81718a192fcc72d8efe52f1b1a98dc6b07b09eb62878e5e8e74adafc8fc7828a062cd7dd0da888084403a1dcdb7704acb41d568ae4c61606bdf4b5936aaaffbe7476dd5ed97aa2a61c524c08b9baa91da899c22bd28417607632c391a775018bfe8d1be0c98d5b78f19bc4bf1b6042f26c3f1a337ada770737b0a2c9ec7c0d4e1fc2bd384c371c79bfc27ddac3bb725ebf8309ed791b361975c900aa317e41929aed7360b4ef7467e84a9f3cb9422d408268f735e51ef797024d5308eb7f6e4e4211e677303df228ad03f8d568e6dd3a79fe141241f284609b23dc7906641a30b85d49003b3b081165f4c414e6aada7702fbd5003f2cdd146adfc91d0bc316d77e84d53b6419cb1bc7040380c7e46a40e714001c8d5e74c7adfce41245a27ff7f5af939ef3d19fe39d9bc5eec3533747fc82a3a50cd2858845e4e024a55cf8c4f8aeaf8dd342ee2c4127fcf14a5aa115f8897ad0ec7c0f923e4bd007f4e7539094e7579e44fd8d1ff04b0b8797ab254fd1bb97d24c8622b737a496b7f6ddf284504fda6fda0770e131af810d67f423d8eba10511394e5e27202eb33439b8756d4e8c9e083cc796bc3dc77d237999159c90dbbedd1ae302daca84d44d8d55277038cd9d16322527c344d79a988edb3ace77d33b2ae89ada4e94875f0f500cee5d1115c1c6e9d9fdecdc7898dcab14beee562e63224d81c4b91f84d8e74e319fb924346d69147ba13d0820e4e404df96bd003d9aae4328c5d30dc513b74b55976971af6ca83fb442fb56207d8a7925575ace384a156cf9102f5bd5109c32e5a373fd1efabe9a41542fa8ce4e5e07ce9d15883a17cefc6118637f4fb6a1e496d339fdd448ee5dfbeadc547abe786da632f678e99238fe4690a00e39fd376627157e7b0591be35d14017abee3b4f53e22614a783dcbd8bc7cd9aad21acc9c2971d666f185e123ec925f9e150d1938fce6e6b78a76c17e7a872c0e3275e135d09b1606b4818708095b14abbbae2262885c8afab95f6d26a12763eb483aa125b5046a3939cfec8bcc52160d0f24ea6cd96ccd7ffe395565c8a08e5c0e21a2c316d1fce544789fcd2a4494c2a6bd1a8f6c0f3942f4ae25468e13d151fbe4c0df4df4719b29e74baee3363b037bf155048e21e4df1364d33fead7b8365faa91954d1f7679d9b0e05ac72e608387f15e815bc29cda88202822284c0b3b6d0c288542665b67f70b3eb98c4a7a564f0fea7be79a69ddccc5809924ad142e05e8dcf5338b77255a2bfda9091a74e8dd7c614f81326433b2cca41b81199ac85713f214944c5d8ce2bb0fda5db5d23679e4a9479761386019b97e877dc9e6b7636e4a181607fd8fd34a0c95e6e25fae853ad2f55c7d20b8770918f6e344858026229eae645136f2815bcada7ebbd4a2aee056a4585f85d912650273a550704415afd9a141e8496667890cb59033e9687da1dc28b33d5c44dc1c99a93c57a3be7261c5c36b5c1b1f176849ef55cef8944998926b43399b2331cbd258a4ab84ae8adf9108ff4752046153465f7dfd624cb0ddb0184fd49a950b3fdb48e4b50964d10a5734f773e8137bebf84f56c6e7310eade856663299b9f0a04c88c61c0a6d0ef12a264ce9342ff377fc7debe231ec04c728feefcca90af6fb4602fad438f7f46b0003" + "837d3f8c484dea01d0081d3ee7edff97aa5fabed3d9824ff06209220459a13250759d3a42dc74eeaa37e443d026f58a8c2af572005ff0e98b81e4e0a8bb5378b5eede03fbfd44fbe152ddb00982538adc18f16a63440cf7df78119cd80916430630b48b681ecb491b4458e7cdf1b491ee937d969b6b769fbff5d5bebd83fc53fa534c2ed9f74542170d9ed6c13409285e9368656df72b5c5823ae0e4b3e0aa999697565f997393bd2009408e6415995237e70fa0340d9eb20c7c833f24862225eb5ffc908849d689f39d06760ff7035c1ae6b6e6d0b06645d688c640369bcd77d9059b7fb57c5aef03739226755755e2aac73d95fc98d4656f7da71f2e7bc9dcbeb9787be94d765f9fa489a386771d58639f57a0c02b319bdfe00cb8d807e667499a5c42fa176693aca7cbf9d5aa983c9fccd3c45cdcff03b651bc4bd64982fbbbd88e7e0651f8e7d597973e5fbccb62e7d88ce12f84c61bb6d8f578eb3b45dd7c7437ae57f96343f6b998e2bc972d18ec7d405953741e773f28c95a715548aedc8029ac247f4416bbe010866b495c1562ab1141ea176305c17464b3c61b864f3dd3b7f103445f55961561b5368b8b9d443972f61eba8fa3b9525537d505361887ff33cced43380eff5fe3148295d5b1ca16c9e6b77e1b7cd346cdf15a1c0b2af32804231ac55aaaa18e91f58a1fa62924c2515e8fe69be9bbd87611177ffdf1dd5be93acf4a664a280eed5da42701d650c52e66f236ce62b943cd1fdc836b619de334e1bbefaaa50484c29a7599feeebb1401d87e33e22a74ca41113df424b1aca579e49e34a632b53c160dfb9298a4c8691b2ed228d23f6c8e9fd4ec4065942345363849acaf5802cfe70e394feca5dc645f74d21fa71e687a6b46f58287a4ddb2622dee20ae7a48befbb165e3f06fae01ff90ae023f4576860dca9ac42996e439c277551d11556cdc401c7ef56729457ff167971f5ff1578854297c637ecf86ad1c3988ab40d88b230b727ae290e3c13db88f1de3fcfb2daff133df81028350c4df0ed777297adfa1a11e9d3200f2e6ac77d516449f4a96ed517aa76238488d4fb5b3c99ea4caca4836f4104529fb760a3e131c7ef85d6bb62d4adf30a207c1e176a98c387fca83830b7c1f5546630efdcc7770a4b188dd0231a77adf8592d3f0620f8d9b238bc63b6f7dd5313c5d28617bd8f09b50e003b1668773de285d5d98311fc66f0fe2fc25518cc81f5c9226d7c7c1caa7f8e86aa6355917b3e5d52a4d5c65049107ddcf2159ff9034de9095d827eb24f253ccfe22f2534cd2445cdbc5c79f88096bb2fec57d25f9659a0cd5767e60e3e80fece42b87649dbc8b35870469a07fed8fb16f3959bbcedadfa4491ba4145725eb1f12042927eb6452fac9a6b58cd30d93244d027bab505f9995862afe6ee5c4bb47d4e5572569ff11ba904efd6fd224faf6519ab72824eb1f6434d4d0d363219cc71ab95aeca634117d6cbc19d89fcf24eb3d59e5dfa449cbe1b1985778b4d248dc4721cf32368fa64198ac7f91914abd394d22372d076a145554d49beb504c93b3007b1ebfdea4d1b7499aec33ff1d5cd93fb6ecc8c63e499335830e96fc30a9cc2683fa4392268db88cca2d47a9acf8f939d6576df03bfdb45ee2e34e4813d0df1fffe56f52cb40e9ff7192cbcf1e7ad6b091aec98a67a1d49576f4d66a32c076df255128f0f7f36ab62ce653eb13d4f548d452d5992543f8782edb20ca5915c8b0f6d60a19f00260ebc1125aa685431616f3304a6aafdd853b125650c4e9a125db232ae3121b985271b228bd461dfd87df912557b2422345cefc3e8b356714220415c829798ca91e54002036b4fafd7f2254e7b54aaf11a56f5a0a859e8d818ce42b57e04a152b690ac3ba07266e155a92fdc2a60db7d6f70dbba80856595ea313aea648a8d5643a8d41c80274a52465ed9afaa47d329f4ab60a7130be016c9eb984a537b6ccd0c74fafb67604219b6897148c5ec84cabeab3d5fd2e8849e15dd6066ef6bd13e2b22ff72fcabfbe3310856fa334f2d5ae9a1117b96a101f9137b3bbdd738d930f87bdf527cdfb5d1c7ebfe7a012c6b09838329cfd96ca1ea7ef46885ed4b9b0f988d1f967f5c25267181fbd731c51e48bcb225fa6d8586a08736ce9c841ca433db6f0c2370d39830f3f5aa7cd72cb58c96b40d21f7444cf28484851fa9dd737d1c8d2facec046ff589a23fd3d362da693e56cbb077f240d550cf88cb85d037e65f51ebae439c8276ffacf55007df37931d3042cc3edff5889de5b742d79fbe8f9cb4db450270aa28b0a804d05e7115b9c98069bdd173c7aaa2704b67f6395f795e5cb8a1b7192cdf245d6063fc3ebe4d5b9dd3f5ce3766bc12c5fe03ef8799a15e3dfcfc572b59816b6bf912852bea671f86f48cd5fdfc19f6582a527511284189ce42c9846674c4b61174dee6a243d282889b0bd3fa01887ffb35c578ec3ffab85fa14ec686721d78c2d6311e9428a8e0cbeabeaa901203d6257974b434c0d3903d71556f5490f2447df5c8ec61589b14545f46d168583d2c87ba74e7e8fad65ecd8957543e1a0ffe369a3bd048d596ba964b45e3548595c85426f82b1d95e077684a0c1b3779e824911786fb99c3f30f7d5839514a5323aa20ab2526113ea44e3c77bb2ceab997a7b5478ac4c9301ec7fd664be5a2cafb65398267bb1404692effac3b598ce16db014e7fdfabf2c96431bd4233ae78b9a4b2b60d53a610ab4d8ae9cf1c710a1279bbf5d3abd9020fb949dab18bffcf5c2d16c522ddda004d8588c0b06e209e80ba0d2994cc19c96549d672c84ca94eacd7aceb5032e3fa6e8d173b5e828d096defe77cb55c5d3d375eca10457ac53f514aa54134e221213dc6c7e14c0a653d24f8b8d962d789c599b80f65cd1aa7baccda39d02980942f787d7fa739ee9c462603bf8fa0205bbe8aabe0f8298ec37976a381a89882264c813e36e819d0324a618ca5683d4cdbb5b7421fc66c380e67be63327a6f27f2da2a55525b8210b2544416ba0f7cd837183806e123d97138535735ec221bb01767f22e45f03b1fa59cb63782bae81b9a1367188a340ee7b6b35957e2ce47241f13c7b7c61584e03b40abd44c86289b67b814c38a38e9377941cea0f6a767582378cdd11f1854a111470edae9fc3f85f0fc23dba77301a41c276bbb521cb1335592b50e3c99af39421464afe313b01747eea83cec22e6a7aeb5c60bef4c57461ffea48a68aec05b2e6720c532034a121124bea3fc14e885dfc6c514dc48549496c9f166a14a256ec5f2556b7246554d18bc2d4215de954ca5a0cd586595218ab20ee688261f4cd81dd69da2221fb9b1772dbbebbb162ff596a93ad173a7f0f9a7e0c8c39a9c1252c00052cda7e956fd389cb524cb6a7dac1ce6ad9aa4ef6a240619ad1fcf64a109fed9984c568b84d6d809f62cb3186db11782b2103c0711656c0127c1ffa3475b1fad5475bc4b7fdccbf672b9d3c562a65f88a898b31c87f34eac1d2f5a13567014fdb29da7180a72642fc51bbd3fb11b876fb37c310ee72f1b0bb2762f8ec7e17c786262d7a3b038cae0c6ccc386aa9f9bc34f1155f0dd8814d6ae9a04cf2314b371f876d012ca50ebc11ad1a5c17ba320654d5e082fb7406457a1952f40cbc530aee9617cd9f10e6037807d35018d0f5cc629cdf1491589637420db2720ceda747cc1d6d1aee180dbfbcf3a972e06be0935060ab940ad85bb388583f0701cfeb95f93f03592d31c206e84aa40cd37af40b657510db928c78134d103d6865a1f158e29da9e6ec06109f1a0405b5ef731dfbbd63e6a8ab2a610153ee0f31d1a5f1ec6e1dcb50ac75454b063549f096de07820094706eedff6395e51b0bd75bf01b4f627484443b1ac6df96a80ef22a43a9ea207377444c097cd96a0f926371bfa723d0ee7697e8569310edf6ee01da24829e3cbfbeb9e357cffeefe95697ffcc81a159d79e2bcc0a63b59c5cf9b14acdfa6e90adf7dce87df9d300fff67ae168b629932d03b1d98efa721d6187c46e37405a0ddedde6f8a0f560f40bd9f59766e5fd448d1e7ef2d555a4b0b7c003c7b6bccecbd01738036d1864bf8fd2f5ac2055c83e30d5fdebf037224cb2de370f6fb71388b5ec1870d8eb76ae4761ccebb3ebd6eb0be92321fd30d13c2abd48c41678501cbd7b5226cbc5427738d8cadf63a1b812d93324e6218b5fb31e0af89e8319dadc6e1db01d72322ce46c6773bcb8a9654c7e15c87c4bdc3f6fad39b88d472fb713b0ee7d58f49ce26b2ea61654f221c517ac3c88d24ccaeb88b2ab6dbfed3832d825de9bb40158bc5c58744151c1fffe95fd4c79687043150c9f167861c769c2b1fc3c0078c7f2be1965a48a1059111e6f8249bff0102396d29b02b7bb42441f1787670cae65067248ec3b954124c798e196131211504b733d44c36d625050e050173e095cfab80afb4ac96628d8662e450b7596b013fbda85da90a405c0d93b5fd2875709d50c2997b529c578cc3f989103491b888ef3bde4b143cc674589a3e6eb68a9def0e7e81f8e40e99e41d00e607f23ce97e29288adfff5415b3c964f2a6a78b737d7af5e44aeecba2edfc79bbcbd56ab5e0bfab8fc399628945df8ec39933254ec1b164c15942eb95b1d96c41114b7816013397427546f488541e2c438e89c6e1ac7899d4723eb04e149e82b0a536b16be8fd758e2125c8a7c0905f4e36e8f0b60a1253588262065e7591f9308c59cb80ba149663a49f33c555ac113bef0fca7587bc23326720c025f7dbefc58aab14a7da5b56b29c76e777d61bb2f597cced47e6c3387cfb3075f1088099e1d69735257ca3a328d721dc026c0eb9df97f7ef2e3f6cdebcce2737ad912a4ed10b5b93b5dbe8ec8d6eee6ec7e17cc3aeacc7e1fc868fe60083daa762bdabc00e4d95f596d9581e87f39d5ba6a1d748e52179f55f9d105027fd71c9b81407ff300fc99eacdd51797848b00b240ee2a89916b4d2b215c718e9e9f8109e9e6d6fa982f52794deedc544153960874f2351a1f295534cd4a849558e8c1f7f214c26ca9a654c5542550ecc2d5faf8b31dfce7e69b0c7b48dae60fd691ccea37948613e4a7f44efb0fafd7f8ec3b737d3c904ce1b5683c70863921a249b1144e37016c73fd8fa1387390dbdc0e701e1809614cc0bc2544efc6beb6a1e11aee7d21b9efde3f3e6f3db5faeaeae8ac01b52510782e43cbdeaf77b6dab64ebcfdc9dec43f4b09833548c1df65d78b923ada2da5c0cb4abf1c8b61f87b365ba09b712df74bb74f7793463f9b7ac8d7d5ddabb58739412bbae1a87f35e9ec04ff7e6268863118ad92127976a2343b1bb673f78cc6b8a03807f6ed6c87e0f5f6c963810545c65999426ce434f4e8a47fb91a12826e3706e6de704910fae83e803a2a2947ac9ae4f6d246e46f4f8d4b7fc31109753dc7a46ea1c7de21068ef4393df88bcbfa52d3216e3f0afab71f8d6464639a6f0470e6d06ebc4ec389c8bf969dbc27d1f6befe01de6a75b7875b4a818768db56ebaeab53c414b1f32dd823b90cc83811f68469f7208b5285be8d456039f28b0e151d38a2f86080783b027525d2a2175fcd45a7238ada79105171f0a9b33ecac2f0f8a5d8f1bd0b7e7c489d8746655a5db401c5575c622d26aedab2b7d2bd64768c98e8278cdf1c2c7858c9328a7f8e42b8e416b5edfa10d52b242e2caf4385ebeeb36f83608470a3dc4955db3631735857a44a6464b2562df817601956d4a42b907fe7e65e8dcf82e79702ebb3e41df4eb9f0fc98b03e3b869edf4d8b056521fcd8c7d917bdcc455f5e75db662d41ef794df832ab27f6755ecc691b478dc3f9380b28cccf93c79dff88f4393c1f0ba64619d51a1a0e151bd10df024028f072950a6c56b10b4a6d0724849f2b3eda98c1d59685796ac6a449e8a35a39b8af5784979fc6a2253839ac29135b2c16fa7f994f3c5ee19cc0e3cf4a0570afb4e8aa8341c2afd19e101641553c220785acb1a6da7351bf4adb4a4b1eeda601cce6ffd91c6e1db6c329b2b15a87dce9feb88133207b57933dbede6f3389cef834fc9248df96339261ad79f8a23ecbc48a967bfefc8c53c1a7fa6bd538cdba8e9e6f5f40ac69f0297331761e211b7b79f5f3fabc0b459531b3edd3ef091e190ff199aaae39c4895f3edfde73cbb9ae353df66d6da2aa94c37860515bd3d9a40aaa29170583c6c2f2cabebc6a2982b465d7704109527897da0894c202e8db40579a952c623663f17ec38a6fec33f88b5ecce0e87ecce3e406cdd438136a1f749ecacc4af684d30cfef6ca7ec89bccfddaccb96e35fee643a99c885e8f75b9ecd4fc2b05386690008996a445fad1ebc2c40bf0f3c248fc4ac6aa87f487a6c91a343617f8ecae8b0b51d1f3ec97b71e9a6a20a661083347fc7bbc8b4f8e803732bae625b948f42b24a05f5c739afed5c59b36274543792a8782e323960a78d1899beb97903a33869ad4813473662c6e1fc00d5a2965d03f90aaf2a71b5c3ac9203ac59b77238466d177b748de47064c523b771cb2c550c909214d14aed83366a9d764d4301a9dd9465296bf4a1ec00b303d11a903754b36d2b8971dba7570c4c4ad4428ab9e14380dfedc0036bb39958336aa6a370889b385d8da2703ea2676494e373f7afa8e6d0f569bd8a231c5318873389c7fd696b29e2b7e33ffe2b3da3d89a889cc9cc77fbb4f9c0e640fcb795e5e2cf6fd34819bcfa7dbc6c6bc9e63ae90966adedb4fbad58f6b5dd0b3dd57e67e7d16bdcd782b92e7a4bee6da71d6d8f3d2d26cfaffaea246e0dd09d7276e8c1e99c7a57c88dcc4def8c9372ccaa032e923aac5bc972bee9096329f9b9cf339c1543de6b345e07deb9c0ddd4fa3d5e728a723d469e130e1980ce47c59aa1d47084c91ba6b4f410d5ce4120a77b1f1a0e71244fbabb7aec70cad018b0f2d73ac16f2e7dd306aed9a5b2945699e403f94ba005af96edfaeea40eb4cf1cdc40a23be6f4837b702364f3c5e3733fd2b4c11fd9b0329cbf8385b19d31ee8082c6a01e2f59d777db5729de354eb66d6826ebe1bded148d1eed10bb2c30ee3030fddbc5e48f8e9f4c35a0491cd77dabc881ea06792edcfcc6e3aea3f64e53775f4420570894a94c3b514465fb89b6b25a73287fd538de10e9c08a5b1850a3f00bf12e425b66d3ed6637c4723b84efc5905b1cc3eed6b99201fb7568b727c63f8d16dc552afe714d96c4f68bb2224917c7b0e0709fcc785bc96df1a13a50da81a2e09d4af66e5bee9d0bf576ecf57765369d2d171461b1c8f14ea4a438a16940d13bb3e02dd778023f208760a73cae8d3a4b21ae73042c47f06555f83d0c25977dca7b577d4485a28a41842082d7869c5129f1149142c5c2ab3b1e828aa124a46ca17c866ba96afcba608d62e355613f12251c0b5be470e7c693cf5a0a1151b9fe6b4d41d4315404c0f01ba62eb195eeeb425285bd215b9ccabe542d5acf576d22d0e3898aa26923477ee10d83759cd4b0a6b2541acaa55c5f38bf84b8a76a1b537141cf0aa0aa52d548764c6eef3e21cd8ec746e953ce38091fec942140193fc33faec226d5334615bd8af65069526025136c6a6305b52d7ee0d3e46e356cd029551ce36f51bca8296e3714481893e42748b154a83dcba5c2a8617fda8453f197f1fb13bbcbef4fec8a6c925fedb2c93c9bad544fc3fb13bbcbf7aba46e144eecfd89dd1afff73cb4a366decfc9bc58ccaec44649c3a0348a62dab52926f915763fa5eb60636d511838da077d9eb16e6a08bb52a49e6fbda4f47121de54425ab419e03332420c4ced1dae2abc6db2aa79170c001fb874de65ffa1f7995760c3fb390d6119507f6901f700a1a38ca731c589c4de41314b9757ab8cacc20fb85a64256ef7654836458c8c679d3a361c011812ef32e2305377268b1880cf764ecc0253a97ff216d3f2a5a648a40cf0797bbfb00593400959521657727e7dee65c3a1ec429fc54231eb8f9fed38127a4358acdedd46a235b61f81960b028812f3b879671128375d14ab553630710bf212f1819f05120e88062a85b38a93ec0bb3d96c9fe5b86e5bdb873c1bb00b4c072970bf46b40c1c795bf23699ada0b2e6e0cb089d532cb2e9e4a95c11c4742572b8ec797898d2d0db4f431ed45ce40bbc21397416d57ab7f79fb2b9c7569c240c757d9f3e1d64c9f9b1df7c0c8950a0810d1aa662cc94a9e206e86b2c129678726ffde9c1981ea5ccee6ffc909403b5529cc4448b0589e5f830fe9403a4406366698f714a177539de39bc00b38161a45fbaeb4e80ec85538de621060d047803c29783efccf89fd8a3afc9f181c9c4ce953ef8e17eb1b3be42eb35a2e6c0bfbca8636c757d79793a9df229db6269a739c92571e6a4b82a9bc52f08cf84e502e1dadaa21e69fbe35e40727645f85d8be85b93e8d64ff3e5ce18da9769bedcb769be7c278a69b15a61d59cb8b60fada532a698e64b43887c6f9b39047453c24fbe8807505468234653d409eb88fdd154b48bfa5d58380611ad707efd8ba5ba60f69089b3288b35e58228b574e4e08c5a2c9e641b87dc71b0bf2e96929fcfa03a02fdc8d29d5a7a64b53cfdc034922ed621984b9403993b56073a9726fb309dcc73f34f87732e400c524c76a8929d29b202d4cbe064011998033294d30643b5a15427c6e1dfbc2b1cae860c2a8dc3bff130d79bde5123e50714ac294e6b62fdac042ec5d60b7c05ab14120bac9309e24e65e3c13e29ef779f3eb17705a137b20e70c39c8c819b18bba56c7a4de050a68d0a22d1177b593b9dad265287c7692fea8dade15b76d5d1d2eed2df3de1a32db2a9005ae35dcbeef6cb867697d539477dd914d95414e985c6d6f8882c403b719e9b9e4d8b452a61dea79a319def70d93844e29de68d52e46628c7b53331520d9b4c1bfeda795b15f461cfc66dcadc97455676c23be38f1cc85a5fc9204be07d7150442bf6acca4fb5ec8a7e787372bfc45df64db7ee0f8b188b15425e1b978b11db3a52a18ba26ed15891322130018baac60b4a64a3dbcf7734f7306f34212f021f9a8c623245f1923d48ee2849a4dba9f181c340bb0967cb1ed0f4e940001ccad987839a7d38b05984f7b8d3e7abc9f2dbc1cd3e582e3e8da3240a2220f264220a70c491438fc63396f0c7d3f70448ab616e95f970f92f4e8620ba85213bceb2427af90992cbea857c76bfeec82fb3ec6a3915295cde905a175dd0e2d70e5f667265f562f2bc1c2f6913c81ead7eb59c02cc9cb45039e86c5315a2532b544c924435bfc8ead21c1724d067c9b19103db1ee4d0b5940888af78bfd801fd54974df345885119590ffb9e4ace4b7f39469f769a2fb20f9b6778eb23ee06ca6f298a83f32707daf92e869447f51c53908e9dce873b52aee2be79647da34a3c94e9c6c8819c23f401ac61b90f10cfc93ec8dfd555b19abfe1e7b51baa7f1ea75d21ecb17dc31a5d03a8b62b43b6b1f8782a476eecb8e2519e085d7cd749b9ff0bc38ce1710f21480412a5cdbaffac862365d6524397fff40bcfaa8e82c9e6d974b68b9bca95cedf5c7c2b9d98d92d475ab75ec14297620ec3d2b8d38be7cbc5f439718c6555c053e83fb7ccf1b1f45dcc88788db8403130218cb2400c3c3b68f9c0ce7d67a0b4e7d85fff27ba0751d31b71ad3929dcfeef063e8aef346e42b52d8ac7105c83c5e8b7e1f7dbce8bc3b994379b2d0eb74871a139797985a8a6744e206e6981e2f62eb819acab001e1f5119ab3ab1aaa58f1cca430371ee09214128d9eccba5e850b925d8496608951c3b99ad6cdad8ba2c101d9d56f880ce29eda12dfefe3f1525324882359c592a275cc4992ac6851547f1021c354f96d7323a91b2d812935b5969cff3cdec260a1e9c9082e11bcede6e5ef8a6f14e51d3570dc01d54318c8911721929e26963e270227c6c5bf95115ab2fdcc839cdd6a5f9b6e7e889604c48c8f882a69de03c0110052efe5f99d753687a1f3da5265acb51fc1477ad0c3505790df1fae88e2486378a5e7859e48c6534f2c4e615af66e44ce6b6a997b3475b8be807cd0997b3e8fc7ad3b64f7184719b26ae8240ff111c73fef6998d545cdfdfa581b413097ed7e91105a23ea685503b727812c37717a22b6e930ce1f93181f9efe256316bb2052ac43595a2058469b5936552f05e504c761915388391c5e426bb2e749bf1fb6e292637b82e027327b2582d5757394222021e6929b2c6fc9781a86c0327c5284b2daea0757c83c496f7646838f3fbec96d73e60545c6cfdab67f0488a55e59f4a08a79e78aa3baa8238b2c0a26c9a8eef11abbace2cce55744ebeef18c1b3811ecd599b94a9b1ac4a934e19c231428a11584e49962b410dc03a98f947b146125242defe76d2d73514c572da7b904b8e8c50253aa29a4d032d1879142629414f2de5c2f7a7f7835b7e7f6297a5005240c2257490d1e862c5f82db7d46048facab1ee5752942a6d2b69bf340ec4a2bfbc4f49629e9b1e2e7c0b068882c1df4c31c91789e72b282e8abcf87f78a46c8a6c9ea598af60a9273f9d9ece56983ae95547dcb0e5f0babbcf72ab4a973168b6322da63f2630223bcb7b6a2bd0f033faf036c589ae5e0104c7271d1f0335de5504187599fa2e940e543252f6e5ec7cf2fcea8d1aa6177dcddbae754d4dd00d0bae169e42d007f8ff0340ee55575fb5f3f809deb78c1548db2d1b227b77c085cc60c6587f65cba79109576d53fc90fa6afdffdfcf5e5757ad18d4af90156e1f107949048acce7a4f499cebdefbed0bc99bea10f9802a3abeaaaf3c0d6052625d0ac4354eaf7ec70730dcb80443a89f315bb048b911d5fe12740eb07d61e03f16b7bf0a77e38416d843b1871be4421d61f592056578758dde2f338383047f51bf1698817c3f8c28c538edd8c150f875a55c4389d9994c209e354edd1adcfb165a86a57abcca44844d48465fde2da499484bba31e4801f97ddd299cb47806ec2938f983d54f1d39bffa53077ea4de40625453af794a2cd43c05fa5f733e75dc06e761385fd2c5c5ea912201fac28572ae7bf39de10049c4044dd30244b8eb4c21f999baf7f4c92cfb35fe835764c7b0440f8c5ced1a64dcd1cabe2704ad84aadb08ceb9969066e6692380ad118ea6c27f3fd87a6b5bb3e53d16e55ab78c8e802b02db24fd260ac4dea84cf69690723add25fd4f2275e3dd313601dc86986743169da492df8765793f4783f1686846056cdd4602fe5fa449c7561fbd05bdee79bb2cbe34f86347ec83456d212d0e46b8409c215a81da7a6010ac41a96d4b2e175969b4f277dafd9b86ff20711a2810891b4ef33dce1f68d92c5b98823f7beb10eb3993662d1627cbe4a1641c84b51bfcebaf4cc7516e4518514428e7e9893fed4a4a2b41130acda3a1559e421a253917a995317933061333b8ac56d794569e9e7ac32c41937ad5c186247594999d44887a8a22a9bf9fad9b42d25e70ac71f44ad55f67808979ceb45a0bcb493829c969ae236d5f7b944bcda1b43a5f184a0fd60295edf95c293d988a5325589f6ba50753385576e88d180d46faf014d6c4c319211b96e92aa2c5b6bedefc6a4f7542eeef61b6de82da0ccf714acb8e850f7b0c1b50daf60a56bf04cf279b3adab81ad288fedb6ce23e4e0d9d2e58527e3438556b4b56564096ef62a1bb359de925b5fd413be3d2050c10ba05c138c793b1142b57936cf6903f1f93683a96f8f4bfd4750c50159e03213727da04269b9c3f6ec9532f812dca077554b8ef716232d6308defb6285e7ceac8fe6cf0f32b37c57a1b89fc2c8af6bf8841469197b6a2808df4e59eda1760e35c2ec5d9d7263870fcf2e1aadf41732e9770b3908df97b190db33182cb74e7cf3305967748c83c8d2a54ef4eb8396484defd7dde68e8665a995e42e0897b4fdb9d78272f7c27f005cc652a0cb1083bfe916a9b5190a9886ce42ebe80373a05f920e9d0e76bc0153cb699bf8025e6562cdd47b15a61cdd17f40691bdc6f1ff20960d8bbb8729bb007b80217c110371f1c0863c4e2d20d14b057af276f67622593cc8dcfe8e485dd0500725208f472c9b0df545514727a96f1dd1ab87bc9f0085769d9314d6773c99e3a2326eb72a4b47a312f6294e1147e682cdb4dd5dfba4a7c9722b41536535874f45b2caf5f9fcffa57f733f1418af97dd64f4566d4ab88c54f76fdfa7c760257f733f820e1e6d865f2faf68f32cca499b4a20c7e18d8ae7d5a4d859fefa13cccc69bc6515f93abfb9930e283fc99fdf028292ba1539762800667a8de040f926789ea224459ba8958e4b0a7ded5d7b075953042abf56161e649c9d010573525323986db9d133c6b2c354772d7735bc814db06bf0f9e9ffeffd2f3129c7439b296a8ea94c3dbde602c59ded6243c98472f228ee8a8cb6fdd02c30674ef4a52699599d273aa225b152ebda1ff67a0fd04838a4efa8e842e20d9b8e5dc1249dd59e01e0301d39279d96a8122515615891a09e8758de312c3565ab36779eb6d1d437b8b1c0825f097121269dd6914c7a5acccf84fbc782cd85c8311f4ddab075b1dc406437725d06e5d25b0bac0271fb84a60e51baa5709acae0b5176d5e39bc9a0572812e057a258e75de93b5e7860f78165b8d2bb6c0f98d6c64de77d93d0da0cad23cb9d68926c062eb1c9c0dbb02187c7f730b75c36d7aea48f2b4e20c33aa8c746df3951c38cda7e8e3173f61e542cff824760f44f5f08104671deb74dcc1fb949b3169cf56462b34c90c49e6e1b37d4c412fd3214bc77c8527343788b2457911cf34ea59c10fabf3ff8a0347fbeea6ef0ae664c3b896efb6e1cae06534db2741f799849698832b09e8d26ba9542355d138d2701d8e920308367071d99551862275728c1225cb998de29b34a22b451fad6728e22522202ea4a2caa988b1ead8d9eb88bbbc8a8620982677cb7066dd85b260818d871f888407f087bc04e5f59dc4bf4a65765e26da0c0fb8d0cfae747b6c85080f34ab48f38bfafb724d55d60bbe078ac37f5bf112325c614a44a9725e6fc80464d235f9a0d67ec792c965de9cd77c6922f1821132fb9d29bef6e30535a1f967f89e92ee2155f1e339dab0ae2841ab31d038ea7843640e920045f1cc1d3f99d35a1eb807c6632bbc6dbb49ac205737e790a0d86c8091c1dc3da1770b08eb070b845172d45b010e248e540b5ea0ebca94b879af8dddd1aad732436a469961e3343fb498c3a1b3671ebfd4798c934e6767b1e2ef96180aa5fcb0aadd8b008d2f6317778a9d9e98d385f88b90c885a3cebd766767a03e70b984b49722f72622447187d5cdaa0dadc788ea257a3f811737d1107752d2084e304be7a0a4e6f781455a30f6816a1f52a5c174e94d8c1eea8262634240d1dcbee480c0b34f2563536672b4a8df7a4bb3b3788fc8026f8c23a4a614a4bc65ce820bb567e4eb127cc673d08470fdd12a552fc306290327b84912f8c907c9765eb5aa16171b9e4cb9b6762629bf5207b048760833b42de1aeebeed7b2cb55308457a0667a74b78ec7933023ca95ebd1a5ed04ea311e10d596aeba3870d329094149a7385a203a173a47708c43158368c1b6c0d73e9fa9bb7828b15a25c097c66db6125860a2a1bdd73f56839aec24ccd3d34d1053c9bf3bc3813cd6bf2718820139acfc5f000984b2147a321e0e1268c960b0ec53b551483cd610fe5bd93ff46de917047084fa9e1081f71a8b1f052d1ee43f6889d317d7566b4178788bb436ae1d4337f116877b58f37bf4e09c1d712b89edc68020928a688c91678f9c0efe0688b2e21b6b4638e2e011fd80def52fbcab73c44443c5ceda445203bbc0aa26b19c24e14b70e2b8778b666a28c346303d83e742fa0d361dc48abc7c8da6c6a354dd82c550a1fcb360d0257a14bea0f3860daef37243ea2c62249e4f301116972dd67c47e79fdf9e9f70ce22d11f951ccab614ccb0ee7b71bb2e8941670fcf3d3ef4ad5e741733c9da394a0c5c1b2d4cb79b56f8d4c078244de3d25f041ce69009a203d38b81dc444009c8a37fb50a19b6bc01748e6d322cb7011cf7b6e08c26fe0480d0f4042f1bb4e18cd053ad7cbda632af765391d44ae5e45f566e143454a0f3982b0363c74119c2070c6e6f1728a8a6b0c9ecf627d78cd7d5bdfdd59ac52abf71218ba0435751cb7a6d03ae899e4b76daea1f323fb20cdc26e5bd781982bc055d17e1127a67073e10e859c01f1227322b409e623016d4dfe72603ab58bf135a0f968e0e931c234c4584e9cc0fb4bfb75c3e4a252986cd8d89b15e328ccc0e4b6a437bfad309bc88179e002e4b1d4601da939b47b56da9a9804b7a2a8d7e69437917cc1736f164bd70619b97792cb400aa313930e38c2d1f1120be5a97d0d72b0848f58ce7645776153b9a5d9fa0d4644f958bd4726d84a24fa244a8f41332e10764a20d06fb4449145f9e4a962dd15275265324144b7a8acf994096088b6c03c1e8d7820793a02f924c6dfcb161372324969ed22fc4cecc1976eb959645a991a417321184dec6994d9bca30872717498ba3f316ccaced47c9758782d934bad9fbc6ceb704cb5c58629812a66f6a027a970c8e58f8fe5d221973fdeb26622cd7038a247d0df17f885c7b932143771c612a664de627e799a1b2a9881b53aa40b820be9920a8712d2d81ed88bce276c236bcf7e9a6f38216e09628795437eb1f3d144b46228a1833fd59fea071fe9243393a494331c217085aa7b0f1090e1131c9e5e48f38136105942b135016081f988bd76bdcc7cccee4bf0e55076228faf0e7dee681c03d9667d0a37c59471c4961f775248185e6315ba38bac400bdf3a4cc7a676e8bc21e7a004af46ae592f87d133a34cee636ba23e0cedb5c0a54ccf0a2c83ea083b66ae9e19a82a6a385933a28e7c4b0945f183d4e2e791dfa89576ccc4bef796417cf72f91509375f2c1523c4356c9db7878bd1d6e58e3dc4b1a7c57ab0db624f06f5f861dff2a04b74f8565678583582172afd329f3ae5f4802a25d7265aefd982c2294f0702a3bb0880a91fd5375407df460a29dafec6e7dc6f9904cfde62ff292271a49a3f5478f8d73e51f840af2d9ee1cda00c9c845be53b7070c3f3cd95c9e46966d95065bda1d22ccb8672a2cc6f712768a824dfd049a54cf340756ee51b3be9f1674a6b5d8d33427f4d5e7dd81afda6a7743b159d2569f3dd283a2f98e8e4f2ccd7efe21baad1f6bd1183e4dd9fd26e4b1f8e4ac21a27e00d0bb2e8fa6eae1e7be4b1324a6272503159173c9ff04143a23749b426327c5cd2493e77fb6bccf7aae0c474ecf8fa59b9d481c9836a94765b6a04cfd351281e11944c1eed5e511664e32ec28b1ea5b9628ed487e49ea3e8b8e7446688061a0d08d261a7e5e8a808866898cb4789aa3bed96e2feee92a7f1e434ca2be0bd616f689b03d626ec436330bed7e9cdec02ba93493af8c9afa4c10d8845d6a32ede5e88353fc8c866724e6d2827355ff6faa002d929b323c26d508539bd4b7ba2af283727b05a47be88949d09e858aae22fdb7a3e498ee4d39050de8ca88f0b5d4ab76902338ce51bcb111398cd66af814911832cd1a6492a192c55862190e77747f6aa60abc44e53b669073be00971843a76de4f7b89f60d399bc585e17478ae85e36de4ee8c8e0783232e31c980c8a2564a996354c9912e27f67c0bdc4f718148db1e62acddcdbb1d25efe0385240a151d9dae6ee3c85c3e3f3d3ef1fc94cc5d6ad0ecc87c5bd701bc9a5e494b730f44e597b6288a964a7c22518a3a24a63e3cdb5df255bba1a8df827873ca490332f6fe2e9037b0e6268377d435c292298e0ba62edd033b3a2b5cb345aa72ca04db3abb2d73b16389331331913d339acf17b0a45ebb40a3541967c4108de1ce10ab9f8c3066949b9093b79f12c828c0c365e104810f0bee582182707485337cb0387758effc152d7908d4dbce31c50260b289735c98c3ac52fb11ee9a0aba45612f6d56a4e6160e98d307dffe2aeb709ea8fa833423146090f48614111da86a4df224ec0e865bcce2cf741ef7e7eb0b408e9689a4052fc2146c31f7e76a91f48e7b5d0088db13f6f4a3631bf6605a37059d21adcc0a5659ce911ff52de29da30cfa94eb518659d3812dcd758911b16a5651c5b83feadf246d116794d74aa61944d219fef914703331e6478873c0eec4e93d3a7579beb3c9a2485ac4f8b1d575e1fd7554f4f6f26c3a853c4fa543c7e248e426d2a865b9a5a2343e78181c41e36daefc44f67f26e92aa1e5d9aab6b4cf6ca9c3d662acb88fcba7568ea73610d531e4d11c5d6e956f640f3bad15d5c2ea30ccdf3972fa0a855ba4fd1b8a122bdde67038b12035dbd5c367da2741ef07ba7e9c62416b07df08e1a87f8ff0340fe6ce97bbabe717fdb648ab016303837c8b241beb7a4cbfa0cb2ad8990a824b0ddfd70edf4d4fbf5fbffbf5fbdad15c9c6c545c878609fb8b02561f601f1483489d7dd21e81fe473cebdafa8ebf7aadf01441937446aa46092a2aafaff615433326b84516343d4ea5afdef718aed208932c0648bb381d66670666927abc200dc8f96828deff24c5af1a3b3a674bdf62dfa684a2c95f1c1785c7a1df2a2faad8aad5a95b482162135044b8acd6995d92b3ef4fc795846cbae805b2d5167a307f33903faf7013b8caae516a9d1b2c6cb87ad568d5b8f8369b9d95301f5064abdbeeb6df5ed7705304a8781575fe1fbd0f631f5a88ce728b2247870e86cd0f2cd7d676d38e8c66b4c3d7016350acaa662ecd7eda6a685f57d858dd47329f4dc279bc6542ba301adf221b2b675b61b2e113786f5db332230b6ce7cd3c6306ac721fc1ae73d0a6c333e2eaad5713bd938e376ab16323eff643af5a1f0544c6f5ffc3f214452c9c2b2cdd3b11a2453fd3155ce53bf7e660ea05a8ccc1a8fd37a8dd7b32d393fd38eec9a3876178f5372bd991944369b646058e3715ad2efc3b3122e1c0c87cf2d923cc7154fdf16696186b2640c4e08d3f8d7cd4db420582b8ac92891e9b1cfbb9e7324d56ebc1df6b053273b705d86d04b469d47f23855bf32088305c5e2e05d6ab31d8af22f6a6517b711ac8feabe7a8d6055af5faceb6efde1f7cba2b532cc9df9e4431410c36cab404de524402fc68bbd6dd54f14d2d6cfb5c20a6f361aebfa01bb974b9f20fc2bd39b1a39ba0254eb12790a2432407a861d8c4a51628099d48230a38f346faba0c5a4623c7298ad33b13bf8317f32320cd920044ff13a78b8e99d1d2b10ac62c84e81e123aaea4621741e23861526ef42d177e7b7ce630acd3b17dcf2dff4e5519a89cf50662a9d19f9bc6310d5dd71c547fea88e3a6fecfb4092982180045b7879fa1cb3948946287903929edab96abaa29740f994ccbbe2e62d2f73123579c346a881340a17dc4eb20ce18a3c341bfd513c335c82ae32a0b1428201079067ddd1e911d6a9e68975526f739126f1ec735be38de14d3ab55335a6b8478433ae50aaab8f7d916771f2393cdedd8ea8eb0cb26ab4e1012ccb28b854b86ac66eaa3e9744294a0eaf7de11685eb2a004e3a24e8a1725a7092c8885a55dc24ce8fe26cebbb766c54e06485d55cb68f37b7724d8d07b6c93065efc234d39d0c23fa306034c952e7c7277bb19948b271f8972968092c5fabd2fd4bcdf6728e798d478de9fd315dc2cfccd5ab9fbb6b331afa5e6457e883758f05e65611053e898acd99161fa2f318b1a676eab8a4532d69419a6c7d5cae7632d6af929d2842e7896b2b2c570c0bc3b6dd375200138d0bae3d2c658a813c3c7a211db9b197ab9334669f63570572fb23c911b58f40a4717ea18f5d0f3aa8eb036a2dcd3eade2529e5c0309c1327eecb0362ef803e6b6ccaa200459948947c4c582c5b3abdf28542b1d10da4d7babcf7b6dabae375141a4a2e12253bd60cd6b907150d522e47a82e1391d6259838350994e777f0df1126f2c1a1efe07ce66bf7f43c3de9d96068d2d5f368bdf27a80868ee6a9f908278a9d689fa6878fabc11dc2f369ffef93789191834d69f0e49574b8f1d78cbe620b1975e27cdee6e7287c8e90d424e4b7d578a5222b22b9e8e01b06b0ab21e3d1115bc50a40534b73ab49453b9467dd15d9cd258c8c04bee17cbe2c2d55c2a427419b1c582ee919ffef9af2861f471c03ba2b0a62c0563327333da8ff0838b6a1264a03b3305d88948a8bc45e52f78f2be17be0c3b8fdfd33bfdfa6920b51cc2413a5453ad65d7e1cf59aec1eaf6c2272e1ed9efca5ca2bd82eaba0e806052f6ec89e07c2d2b9b70eaeb68f93a6257021f04cdd38ee881229ab2b540af5fdbb6b8c8e5acccf944a2c32ac83e63384858386b7764a1b4247cd97529a7b454b9d5f12380db17b5b556f7f4a92f0cba2b7b76c458c44079826b3cc854ea1757264f61e3d41d62857608cce847c16334e4e07043b41697485f004322e1e5b147e97071fe93123a9fd90eb2725d40270bd36c27549cacd6ec7f7958aebdbc751c5d603ea9aad2a683300bd408cffac66742bd73b2ed9df5d3c6b6d280ee8c053726de6ad5aa9b4dcc4c8ae21af65fd4d7adfc9e4fb2ad3e7f82ac3fb05ba6d98c3a3d7e4555618d268441d82e921b8fcef2acc0bda2002d24cb40aa8f8aca4b84b7a9b02e80dc10f9724e04ebeb2237bd0b7c874e3d73a9c3263c693f4142b62ac0b2b2ab1b6543f0eaa85accf9ff9b2d6eef36a0d5d6a13b3f2bffe784bac7e6197967a715aae9afc75fd381d17f7a5f89eb1d19d1d53f42e1b42a1ea7cbe3127f8a3ebc2d59f52b32c2acd3ff2aae4a0cfc60042f61d5af60045a7f4f499aa6f3bc04aed4add0783d2c6a7f234f6957e790b4b12ef794c7c206b1c7f174b7e51560a67f0f67b5ec359e9d3d5caa1903e14e8ec7ef2461b5a8f1b40c19a1d29909f12a167f750871aac4b7b1d1fe95256902c2b9853c12b6e76c6a1229693d2709feefb25e3aa8d6eb65100667fceaf668c14f8f66050a6648fbef5da9e0a5de010f4e1cd1c44773893bafd664597d5c155ff582befc0c8c1a31030f0465cea7fd90372b068aa6d26322b87ed1dd620dd6c06b40f0e7cb5d1b195de5f8e931ab30bffb08db794519b9082ea361fb01bd00c36390b8ea8073a93528882d9cc3a2609f5974e38eaecfaf8e40db40db51cabf0415ce3e147dcbf9152fae7e0d9bf85298c730cff062198e79038975fbe2bea947d0dd66cc28f9ed0fbb91b2bf4ed1a1541597a71ac97143e8d44e2100b264f97d8b79e2e0f8e668dd1574661ab94cb07c0a65bb4d58b1486edaa3de20cba334f582d19a27c7179914e817af161fbf921a2675243f9559944d4a137017881f6f80e4da951a48070959a4fe72ec399f726060b5b7b1fb39065d966654fbedfb9e5b8531cd1e39248d01835d5db7eb56107b442498fe25cc6714226b8e8158f3ceda597f6c3b6bc9e7f802a8a6efaa5364d404c5b672c9dc443d66e3fd9f0ff75691429464637ca3abfac2e8e8f9c6cfaa53f05615aa2c17cfc1397d7541933c692d946f8f553fa643b809a88b4d3ee8968c4ba38882085aed0a2b485df0d0a76250330d95968010388ab1e65e94ac73b3930ec2ac0d86fc282cc60b1e6884c96934b9db0c6e85159d9809c59a239a1826a3780723e285963af4a68d43088ddb0209210e6206a2e925755dbf6acff9ffdc3860044e8d05c350adf9c39ae920c9e5223e811b6f15c9cadbfe92ef65d7211184f1320e39f7934be985f17ce224854dd0ec8225aa8fa48287da5af3a636ed6c3c762b9dbabc07e5e7f734eeafdfcbe8f9e30fbb1b8c81841daaccfc2827826aba77f69d04a0756e6aae3819e25a67df89e97a2ca3a08db9d748784259a272b76528f69a28884d7176f0ac5436291c074cfc7c85494f83b6284bf5333ca8eee5045f0812e8c6de2c007c94d92b325a56addb5664eed223e6ab702513e2b5d67cfae77f53226217d01a1c655131de496cb403eaab08b1207b0cb5715f86e99fcd3625f9a43471183b352004e392e1b7df34d915908a1cb7472011cd86d08f11012e513a02454bfdadda6abe26d0483822eb1ea6ed3102b28c280541b7b910da526576f3cbc52eb982df0134e44bbb821f2607b53ffc30813f7f41902a109469d1c9b6983fced1c2a9780469da57d883da9d7341d34dc92602b14d4126031356bd0a47097d645feadb8b9d1d02fb583e438edc86c221ae1ed18043815a17b9089012efd9a0016c5bf845ba6c5cc1efe01f9609556cfffc25aabb7cd5ba90314ffe27fced5e5b621b030959b34729c85370ef6cef10c744cc4ab5f53323c5fbe348d68bb970e39dc5441e77f10fcb10cabdfc90238a4f0a9de50c1ac4a5d2d3330d5520dcfd543cc6f018bef48d18aa77ed4037ed6c244e8375cc34192f30177b598b018137a10008021326df22fd4d625858ef090a032367a150bd149401d65106a32f9747a0d143025dcc421e36b7945dd1e20f6dd17100d2f6a69d3892f7c46a755d5edbddeaba7a250b67216bb651c153d70e66e8eda8b4df75fc0ad0f4f696c9c39c4fbf4eb24f7d71e1e8ad50bcc68caa55ed937c64110b62646783b38604cb3e7f92ccb77622be8e03ab437f16e4e5a0f5706931bac5cbdbe3cd63d15dc9652216b0d101f03c21999726a4e7606c9e3469c377e098c4e2f8918a5c282e5a410bb47f1950a3be34a35a95b88ebee1956d265b7086d56f5e7ab12aee9b0f980cb6c82c5ee34fde484e865e572d037e196d8dd030f3d21f7f291e105ee4e050039b79257004722bb87829d697a067a3224450ca04093b130383307469ed986792971fa63e23f020f28c4ba7401f7d048647e2d2b62fd2e03a9d9e6f8f0576177046d99f06424d78856eb59c59058903611f7ebf789db4d319f436f0c0298d27c48a69cdfab365f885153db3de59b7e617083de6408e429aa8ef2da0afea0ca48d615152cd8dc14b09dfbff6695caf3a37c679309e6fc3277a4e1c817d9e571d1c129e8a928cecb397c6a626afd90c1a2241b35ddb10774bc5e549394ed73fd75ca4d5e6d7c289ea623ea872f944ed22f86746e6effb2bec59b1311e5379fcfa9d48ffee41227902fe08ac1e6621bd9bd6679ddd9972fe080c5bed0fd913fb5394d2749ef5e4eab7c027ead97442cf4c264e97acf7004aafc64842d33f95199c4084139a0ed626e4e0dd69d61ff120a119df05ffaf6ed825434fcf98e6738f8740fe942ce2a6814665dac06410e327c256f919ff5a83a66be09172e67f2d1ac84f33fb8a910db437667aff2bb382e8d999fe8c7b625b4b48471c60ae8b86d9f0a0eaa0fa2b40585612c6f35b02dae31a6533fc046688fab32ff29ce59cbb2b18626efbbb2e18cf6fc9b324f5809097e9fc2822bd8e2a5306225256b600bb8b221f0ad74ad2cbd0b4c04200b8ad197386c46545aec24512eef32c01f5a52bc350d759c77068318c9a6e233d7bddb73a9cb10d707448552b98a9cb8c8b66d79ed92363f8b8e4097d5c30640d44476e58b1a6354090296aa49aa6201e2052065815dd7d3ba8da04ed5ab1927578b1aa335854f5b86bf76a850c82e24edbd776d6155e6a8fb82401ee14e25ec7915c363acb3886f319812a5000545747c03c65fb48aea9654042692783fcc98b7ff52528b387bf3887e0ee40ed9d90ad41e587db0cc1ee76a00c14c37ef00138e5093cd0376d0d14bd531ad20838e569fd7ef2c450763f17a1ba87c121b329016a5719199d267557d49f0f7c3769d2754664dd9102a812a59c81557d6a9c4d46a1fc23f07406a32dff7b4b3bb210e7a66e4a76d99c58a1c4119c1919c9e6dc3037e523dcbed243dbb4643afb65093f451597c7e348a78c9eff48b33c59b075494e86f28c76084b36e74a9f24e63201e79269bda7239f036c951a0cdf3cdfd8212cf68a7557a2180b7773b6d0c316997f9ca494cd69eb682cac679ff206b3e87dc00b0986c401acf9d88e6b764033af6b5b8943d8bb03bb0346e77ee6b154a63e48b7818b518d70a8fcb04787c65ef80d35b695970399e7aec50c475445a6f531d814bcf2ab688782739815361bdae3911b25a7486ba0491bfd368ce2e218a95f85740e16e8f551ff4ae8da07835e531608033f2dd1f3b1c16997b60dead4132ac30d5eeaa8e8ec0aa06fc325a196e9ac7fd88eb624982b63c8675e47fce12395afa377d04c5f822511eb773759d55a3b66b55ee392349ea1d187311bae7ba42eb5c328edaf5be0609a83844f27ab991f529363d5bfcd29e34b30d9657b0bde63e499d641679fd0d1668eaf8af2c0676604b2bcdb6a4876f2acfc4e611b244d59f948505b188a1b88f07c837f3591c86c2a13fd21ad8273407e2f46ad0927a14805897f6b89ee206feecf4821d23b6ec7c3d2c6c75e9a0782eb16aa1ae866fb378aa3feaec58c09ca4a11f59b2f8f1451fd19829750a72b6347b2be2f057263f2674c08ee2ed6010a6130d452db0e414f421794c9a3fcdcf7e72e063f70051556169e9d175eb801940947aebcd92008a5bda9e112465d17181b228bf9e795887e207a94e030483f03e3541629c31c2776613a3c84529b972191948bba7aa2b182135810af852c75251fb71e6a07f443d5730d37ab175c5deb06433b5bd5f0c8c9dc00834d4ef16753caf42e16744e172ae48bf6913cec06adcf60773b7ec708eddd524bc51df953c52270e043a5cf86883b626715ced99c1b8ad6409ceda942ae39498da6244e62b82e069b673bf46d68fb9ea8ead26485e78afa0d769e5c472c780e2d97903643834bb90e9274395dace6e9b24c996d7f65d292dd1f3e1b2c7e4984171bedb31ca5d914c194cd02f07bcfa1049914e47afaaaccee97d390a3610f79a0beb871ab7d39e109a3cb0cda62e50a3e25da757cd7221194725809b7152d2ae2ba36a2c544e08cf6086bc8db4613c411a9d52fb15d3fe47ef7aa21ab4c71b1cf6129ca6345bd3ad3939d6aef3a84eca1c2649d938ae22e42c97b672d71942d78dd237a494c0f36a79016ea6dabc2a1e679205fc14218c590d842046c41877e1f3692f66348d85b45a0ed91b09ee640dbc5395804a6cdb85745e88ababed9c3472319533dcb1946715b0d501e7e3fe21f2ab0536fb691efcb990afb78542eb1216975f82bc39e4494fc8a3b6b9d2845f6265862a3738a1332c2691d40781b08dc00f99c38806db1782ca48e1f1112f3310bb5508181cdea251f3cae6d5425ca4aa0d233f07005ce838fc77d6465025e473b610052ebca09663ee24cd5091311bc656c4c9cc9db8ec6604b3ae15c70776332bd7b3e3e64e254fff9c82601dd3d1d20123aaf946a2c429a95c2f80cfb090ba3c86396e9eea6b5f4314f0a6e4b334848adbccd554f112dbc89ec6d39f68fc7e219af43da64b547843174501690f1469df5419ff9f5211dba7b53a55934fb328d84c657b6ae35e0fd4ae63ec17990993308434615f62f1bbcdc0d04ec72e548f2600e269b29f22b40e8dd26354052da1e83097d21c447371f023270f34cc7a9864cd48829ab7ac19a1e30ab8d5d82f1add7ee16398d00cad20bc222356f292c106abb884c11ca843a2f4452f04c2c05e5910e454617dfda8bd5d564ed5d6213bc91f522d318635e79516503e33dc09bf8f1627a268329a1e7338acbc9647aa680dee4fcf9553871109e2715ecce07f5fd4d7cb365f44496c3f788881909e7fe60ade1244fe3d98b8d798a5d6d2e3101b9e35338c33e6b369fa5acae6ee4908d557840865ebe39a0ca622d77dcb8f758f1b4495ef2f9a0f7a0c2ed5d0222663a7a5fd727af8699f3b420db6f0bcadc116793757f46f979c35938498f2a028c73a46dbb1450920fdbea559c9a24a6aadb54ee384a1990751a08ea614b7bff752722afa976f77b4904f5466f5345199d48ee55812e2a57a25d87a25972cadb37da05ba8f247549c0921a8c04ad37d5ba69a9410a91bdc633db2a2fd2b4f157bc6a7d78bc8a0bc5513a0fbf1b74b1794a5a1896301f4586836dadb6fb33d1ecbe36a7a71d247c4fb6a3bef6b10371ccc84f2cba931a63ebf653dcfa294f188f99c82817e5ff0d7236e7311ee5e499dd09b97d3ca6419f11063468cfcd61d12e1e441d3d878c8f51302fee2acb1f928c7e8eb60e7fdeff8c7e3e992ca3be7480ce3f9373ce072da3923d29879caa9aa77d2c9ab244794246a95014814ed3caa8c0b11b3ae9071dbc01f8ff0340ee4dd77bba4e3abc27d996dc4935ecee0fbb1df8fc9436b607ac604b5e49b41c6ec7f8ffbface617b002978d5b89e220dc4ba23a889e9d39871ee2c6404af7fdffaba6aba63785a0f064092b0529583c4a56576f88c72371d82835d3eaeb2e51b32dc4020b03482f1ae0a06e96ba206a4a72810853094c13e9f5504105d5e172604643b3c3c5c8700361d9c3ca1d940de18ad91b3ac0e310b4bc6c36a56d1f63494dab2b5aacc82154a8cc79055012255c9a00ba841efaf8f8e41a2640248a415dd30557125021823d0e93e967563c58293e7c4e7df5d4a26e881b32ff84c3a3e21eade3799282b1657fcbc725df54ab35cfe59cdfbcbef0da9df9ff9f3783ffb74f904bf818571c47328ab238cf6e616e5281534da24d64a432ea4ad2287486ea0f5b8335e6cada9ab3930bbfad2577f006dd93f8795473932ac5b24a45b9430c2f9378ba0858df1a48ac2e21419d041164d30cc9a5ed8d2a5f1a2dc1642352f226d46a3cf6efbca4d981d1fca672113b983db6e84b292b8768401b3ee7ac7ca7349ca93ee692abbfab1ebb8b3eea4aa80eb921d84df3c291437adb75ac31d249668bca0fba2c8a742af41b95f6d4f74a6b28bb3edd92c7b13bc5a051e714f9941f5d7af6ec0a66f25a103b4f999d82af7c320d9a72c1eb0b6e3b97f570303a567954bd2971ac40bd435971c73ec9ae14b1dd65855238907e9b64f5590f59861dab4a1b107cfd611893b6668f48319a384f16aa7fb260de00c78950df20c50b804f6bc65ea26d383df4d1b20763ea442f653d081754362add2e209eff586899d924b1429ac1e2d263ae38e4d4f398554f9cd36da9bab7a497f58198f906494d0072c561db4788cdf3132e234716499a4bba03ab61937ea85912a16570b9baaf800de140cfa96e6c0a0de0653890833ae61a7fa8d91ffa0fcd616d36d50cd509b333c9a7bce1fa27b4c56e19b04ee17b5f034db023dc8d6f1f2d5642ac7ac87645ec20035b757208a4e2e0f7fe7bdd9866fb56315811f6371cd31bde2a064f9bf72f20df04c7d3aaf2276c7775a556d718d49185370ca6563d4187c9eafa7e9f42be09660f165c09e05e420c218ea60b08a4534531b14bec31102a6bc97d48c40056488b777fde8d33c2d819bd336576956f37bb4f7961e9dc2f3120c5c208d28e91e50e8eaedaeef1283dcd896fad25acd9acba321f267b22d8f7a64e4b8e24773d7265cc539c38d40201067cec8f74176dce9adcbdf88e5e3c9c01adeb7a87e9366816fca46a8b48ee9e6d922859a3396ca12e03d6f3ed193c184ba81954166bd530d82af21a877fd75741fbced59328747a8e3923210511d41e0ebc907cb7f733e97e08f6ca77c73a5026ac7bb34760b2809ffa70464aebf72a119f029646b3b82cd76fcc00bf64e2f10985c97cbe7d9cee0d7bbc225c1ce6fb96e695f7ea7f01cefe130cfcbe317a7f55f0a6509f49c3e37271fbbcbe85eac3eaf6f6e9f679b34131af534788562980eb1641ca8588e67cd1a10febded4e1804a87ab8be369957a268faa95e9867d03c5319a574e79c0c6cb8f094d2778235d461ec5bcc275118b50d061c4c01fcf7fb50614a2cbd540da9b0f3895c615cb52caa87464155a89a13f7e38e5ffde138ce61820d73ce1ff5096a671a0e4a1bd5dbb286af39baea432f41b5e7edd00f2eb7ac9af2f8c2ce2b2880bd95cf877d09910d32d0ce00784236e994ec5d0d16c6412a29a0fc720630a9f193c2dc3f0f4ba847351385303a228527ba0282dc3f5ccb3948522c9035ea18b57a882f048843e6dc522b2cba68c70bbf4e814857013d3683837511a7917dc0c957b72013cbe211d45eae044359bf05cc414091c910b47262455ba5ad592797aba553b8b5a24309258d0f468e7d7924f0f91cb0f78c3442e16b00f481f7a52f80923c71e10ea316a02985fd05edbd9ab4dc9f035890634890a46b9bd44deb0fe7293b2d46fd68fcade64425e8b28174983fc1543eb6754de1de58726291a4981c36b9546c7296a11f6d61bb0f73d9d66c94ad9892472b8b6882b5c99640801c372fdd78051f65c44b14cce220b99e41505c4155c9a014256a489dbceca5caad1a8bc8a05f482f2650ab03403c8e5603552869b20ffb6edcff784834490148c2b9a86a8dd4231b6c1222b28a189dfcb96101b283bbc2438e83d7f957b92f1db309f4fcdbb9ba2af37f02c8a7967360c9efdf53bf16f6c9ef172c94f0b273122464d8d32a120769e47c756e9c05d84a148407c561b16c184653cc48361dadfc08ed077647b722e7c3f933d5c4da37099551c55f8f63350158bbe3d5c8d9912245e674d0a9196850890d5b1fafa52bdd28496a3685cbcfaf4b0cd7940a800e9b2fb32484da784708051fed6bde9ed8fd8c11039a13e77f49c87779635dd33c81eb3a0a138c4bc1b00e43130d569c0c141c3780d949df20f7b1c1d4c7e1051145da680b4d54d3130b0c29c1459c590a7248d46aeb35a2d18fc4c7d8feb33ee193ce250633bf899fff4f4f87140686849c1f935bb5990d962c2f5822f0b2e96a430c324757ae3385a73514350e95d208e34b28975cf371b268857a140f80ce8c7c647139d1ad1c331c128a7465e08eac5f9140e5b48e15d9f5df0d19ae10a68091530992d1020367b91b19cdf44195b289c31eaa50c808052178164e598e4d1a56542e06a9075bb49ad3830bbe30fe85cf7e6213c79888f4d460f54ad6aab18f82e474f5b58d4a16dd7ca9c71606639deb45712113e3ab0a809dce692d7821536cc281e10624a0e2cc92e73ff475264797aee2958e9149ea34e70eaf3fe06ea5117841a457010c3d96930e0f4439b06e6eecc380ab3b4dd323194736e43782d4f5cc374953e9769e862311090467c41bba168490a7d35942cb8ea154b1917655c038984b5f869321d5d13449f726e68e1cbc144a69198166c763659be5551e0473d6587e19d8bba368f622a20cab4cb4c1bb4750649d829f0895537955c052b0c4f924a30ec0f51790b9da5326dfea30e6f1907f186006edf8faa0b5d7ce8831a4f0279f7d8266f40837a2ff3c071b003118b91b519837a1c438076f557120a28c9deb3a2c98511f60f04f069a5fed69ce1d4ab86750ecdf55d2a2adbb9bbcba01cd2afb27be0e70ccff065540ec156530b39cf2800ed146a5bbbb9e4e0712907011e11da7b5307d12da2c72dcdf25b546b899dac1847d019dc0583b06d037adfa82c228dfa3d2a42e61618d17e26b72234fd751855d3b80f4d15b3e9528fbe3cb6c0baf3b87f4d4c4bf979cb28cfa50cae9a278bb858d0b3aa42da39e33205ae92111cc7cfbc33b621c71766e300b43d05f156257c40b87435228a58144508895ca3423a2e15716feac0e746f9224473ed0d01e91934de6f97e5cbd2e1f28a856969b7c71ce44141c7503d14f9759579e76a94da37ed4dd06881da0157d5d618af1a1e58e3dd13229cb7ba490f0343860d71e32d81e41d323408590de3ecbbcdf4de874a177ac247249d5f8593d876412de5916c4a999de9bb6f1ca26e8379030c456a5f6bae0c73a34b80a000d347bc2603420c423870c5a2cce2342d6e392e2ac607c4634f915342bca2e9bc01563f927d56bbab335680cfd2dababc12cb83037e985427de19361789f83d9a4e303b420b718aad66429ba30e6004c6a78eb3372b815f24e5cab220ff1bff90490b25806dee058035d73d1ec853ebd2a2c41d1c63fbeb3f1f2999ff39799ee7241b674a706acbd12519f0679fef7fa3521ef60c71ffa66623c1abae18caf22c9267963ca7284d45a450234b64f748de1165a047c9e4852cfde905de85399d7246e7253c30fa6b96dff79ecfc7728d7af6e719bb8d91bf67feccbfe17bcaabf0e619c9b11871475c261e7149ad444206212c510791cb5f149514e1bf38d4c845903529ea9b5049d163edc26a2905bf5f8aba0c7508174136887843b5946206ffe5e82082ac7d51671165940a971c0baba5144bd392b48eb0c08544cadc1d43acff84c85da0643fbdb5c7313c7fc4862a2afbd1dec1e409871a931cf455f689040aa0186418b5737eb5944292db259775815e4d1a4ed74ee7107fd144a61982a46d99b25a6d832d10ba7e6e4da8d21e6490d6d3beb2f1871fac0727f21054d002a62c6570fe57d94c8d39a54307a522a01ae122ec32894bafb4148eb434351c63c07a32c74576671e4c9c702f5c6a5fe0cb290daa502af605c48930f26ab228f199f10606eb11f51b1377d6c52074137492c17d0ff0e918dc1c7dd331b8ed1fc09d05f566e442506de9ec3f822cdaf97598bdb815271f13121d131c557ce07c3e07a8a4f82e280cdaee54229124d9e4b6f9bfc6d3af2d027f40fd6543398d5a9328f668b8e8cb833fa03182cba4115861910bf8758c4f4aab2f049791896f84ba3e53a10b8b24bbeeb4b4f529907f3291d9722abfe0599a8fecd2143ea39c48792b20e257f2af542fcdd82c9c2eb8d1d3d82eb9f1351109e41472b8a21d99bb6c3136490aa1137a7e8ca87bbac9872a73aa6b45234bac28c2c55e4beedfff20b089fcf0fd6bf2372478da81424abfd7430e59b7514849d5566406d85f87bae3e127ff68aa4a80db354656efdcee046ac1022903a87c13222f10920bf7a1a049c3b722f3afdb24f3af372b88c58c7ecc38a5b9b982c4a8b02dd17eea41c9e75238367099089bbea80ec1a116678bfff84577c656cbb04950f145c83960167d176686ffffe40c3aad5a591199a3146512e7706a8b88492956a1e1280e84a835bc58e61f11649a356aad03611437331a5a36155a20ea218accccb7a09a12180bbd6038ea66a9be5bf087ae7908779f4e333f74dd3e631b4d3c0e19c6c1b1cade755c6b8d1fdf3de7ee991dfb0796b585d5ea37c679b9ed5f1a32cb5c044643a3883b3c59653da4ad031661b3e1072d4577787474fe3419af12220af9c348068c24352bac964168cd1c6011bd69f53913f14a14832900ed0fd08cbd8842a020e3753c1db03b6d17f18069467f676cdc01de656a2adfea0e3d2c9e44d6d561de7ebe79cec40ab8bbf3b844eb6abd9708282a0b18bdcad3ce489a5369a9bd07c1ef64415eab7861ce827abedf49f9782047f569c92f43f0023d33399cc13806938df227dfcc9d29337592845edf91f3636ef23229038b8fdf674611979b5650b1dd417556f4e20cd4e578f8f1b32c1d105568e98055571832765d71d9151db05a2e00e3d8952b3ae0ac3018c3c436a24729842c80452c5ed1011726f56ec51ea3d66c0c09cfac3d5e4a9a15ba3529268c5630142cd32b3a7416b04b2e0065bb6d80d2c34ca6cdf9845d48098a09a3d0eeddd4552e15a19c6c2eec3ae84af3b8415c31593dbebcf4624e464d8144a41d7f8aca60ae6a8631bec2e78a63c22e06b83421ec0760678c1fadc2c36ed0e631847d8785b5f771ff71eae616193c9314a166aaf1a6782e3d7baf21ee4cfa4ff756213ecc017d26419d3134b2688b35668f165d01e78c510ba6174ffef8fcbb77cfffe18fcfa73820a4f0f6944e815ef90661c2c474434e59117c21eaadbec519755aac067ca5d1552c580daa40888680621d429eeaf023436cf9611c460d955fe798e0f483f9fb51190f00b3d5a0730758da95e8cd28518eb94d329f2970943ccd6298ac2a731b33c599bbcb7c5e6a1371dd485a68378b8ae66c498ee893de4831abe45c8161a58264c427db37aac9f0f0e5dfb14a3d52eca02c2e5b2f98d8dc20a332cd6ea69c96d8616aca6724cef9f7c83453cdc6079f4df13bae842d425792830ec526307cb3efa8b5c761201ba23ed6ca4551da3711a5f0d0176e3aba59ead1ead1d7006268f8af1333f84af191ae5044e91cb6c09aae58648994e5f486e0b282ff7c4ac33d2d5ad52643e69c27794cce9a2cbdfbb45b1a7d95ca39d7945ec6ae5ca81f17f7748137d0fb6ed441d34463b1502a5add20dc3f852969adc48754e179888ca367d5765433480580ef79d3c12426c06161079a4a369352db62336926eb1f83d1586ff15ab72bb3ea6316e165c0b68e606c2e2d6e2d651ccce363eeb3ade4f7632f7e2e609ea60aa035c3a27feb68e7ff208da651bfe6f9526ae7ab8788758571cad4d37b5f9983ecb80540af54404c90d86814db19a2aca33d2214b9765ada7c77c3e79c5b2ef229ff6554c68831a55094918b134e1e0ac609de7c77b8eedc196d5356f7a004895d77bd4479de384ea0872fce606d00f8ff0340fe9f6a55b57f37eb9d402014a74ac99ee0f146cf052c7dd9ec20d00072b8dc145714d5f9f1cbfe9bdf5d502864361e4536028b45e2ea5495e8e969312f8837ffff0d3154d5bd1d677ec86f4348ef23dc2647d40885b26bc57493a305a1f0874cf9ff37ff1cb655ab9810e6818b777d1e10c571dd490ecc0b51e44575c3dcebb85c9c16c2356f823e3089f45c69ce389032d63b1301442f23a5249950fc2a6f725623c12f20babd6ef0c1ba3dbdc9d0ab6a407f7a12eeaebc05a37178cdafc17b61cecc14df5c6195ea0c8fa2969d30410170ddfbd7f2a8427da94b1502abe92bd4a97128bff1daa22ed22af14baac35ff22de0d998f30079778d90d3e298211c09ffc3cb6f69abf391b0c7724620b1f6f0d314f1fc0574e424464476d6438fa59ecfa41e10549c2aa0845a3177854b898ca79ce57791d51c0461658d1d062698c5d233bb0c76f608d784d3a89c6d3a181c8186e579c2a097271cf40ae25057fdfdaad0bec8ea8b3c7e75ed8c57c5f9b19fdb0e638f73e7ce5aec36bceb4e5747406286a9708be975b804a268e6a266150a001f52aac2df893dcb6eec9d13a31535f9a8d785cb895f425de5e1a749ce1c40142eb6081cc9e13d8685032b1ee0bbb6c00c86a234a86152838a0649f1e389277cf8247e6c84c5fd90f2517c0ff9fbe890deb0e471f8463464f4ce8a17d8ef2ee0b3dc32167e393226fa0a83bb40ac0b89b1a32b87b52292864f5b12d18477367646dcd2482ef381932bf543d689ff7bcaa2cac594c7c5f21f9dd8e09503baa128aaaf4cbc3512e9a5ba66c0abfb3778b70378b446bae237c676c10fcc0517828177818cbef01647ee9d852e8a173d4ef0f0492294c09bdd95c223222c7c890f40b9d25ed1dac6d3237c06952af98164b0ed930f99bf49a5a0249ffa1e64e9503eb7766b20004a9d12ca6003a7ea8ac1d9a81e21c3f6d01102c03b0e0e7b85a99e318922ea056d60757754ce60bf2d4ea0a3b9ca76305f6f7c431f4fec3c9a42abc5ddc02ee49e3204b1530f071f0b37b1056499b7f24de16cff5249dbae185d272bfab1eb100e56cef38fbe048b8655aae49a1d99b61a924fc065d99a64d5d8e8597d236710b9cad2bb6a2ce89419a58e6d31f7a614e1ff955b512d215847dd91c15c42d638f94a13ad3348b3a8d626fae7e29c8364c653d778c4e79b59e94bb050b03facf19811845f69543e6ad39c7d6d8b6481a35023778f287f37296f99968048529d4320fd5927b43f88e358d0026bae1fd3b5795cfa1ddee14217145347c6eab31db48df1abc764e408190725db8c5a9d6f08a62abf5d61309693a44c73e66edba55b757c3d80a88de597cda54b37b788030986a7590ae5c1bf4dd0aac7e169c6444bbc18c313d9118d5011a3138fae99bd9f55375da0cd508879cdf886dd73838153ee20fa24690a138ae4aededf6aa4ca55b606cf3882a66df42b87946ad2ac43e4864a3ecc897c012bd405ac3c61f5fcb48db3f392f97c50285004c1d4053bae6ac39bc27982e3594dfc8e2a094c1190491278cc5a5f8c8efd478ecea1aabe756b7322dd827bd6f515f071f178149b3512f5d7cf03a2089e0c812b653c5cd538ec85f210a4a05320e4ff2d3fd6e5fca1b61f42043fc10985d56dae0c6bf4a9e3b1741b55e99bfe7ff25ab5e74633bac50e7c1a7d1460d14b61c0cefba01564e31b2158680a877b47c0dd5a8e7e162e8752529168ae7e2815f7f6235b9001dee609678cbdd755148e802c81b1a42ddcdb111b01473482f5e6c6b78cf25c616fa17cc6d0cf419f0ca9433b37d2c4d234216d55db1ef60e5a5f6d1e3adb9d72c66b06477fe21959e2917a2621d39a43f357b8abd576b32f577c149f43101377e99c894fbbe6f1f74a094b15823421d33d45b0fdbc33f72a89c849731f10f822f9eba39335dbe64c988867025553e7ddd1dd46705a2679731c6385220147364aee456af7a36ff22a91f0e91eb534f8df7c71c4c918eecb13fae78d517f1b03fc8463d0efa5e3e47774d65ba26eed7c2adf30e3e43b8ef3f9412bde86edafdf891987efe8ecebf0b42eb3f2468bd362f9109019e683b35fb3ed8d9a7717c4db61aef036ec8a2decc82fce0c33aa946c860768d52b23dd3e3972ceaa3228cc16d10b94ce72681bb0263a8fc0d80e2300ddd955ab5f6e3f98c35eba675936b61e8003cc067a9e7b3689e1a4707b1a1cd17be81d3cf6f3a6ddc0a10c5dacff2f03ba3840ed7a81a4a7817d8c3f852a5b8fcebd30b6e5d5187d81aae927781020b93286c1a6e1bad3ca4989f7db58f660b5d09ccd5e2269b7d45b8ea7b34453a5adf3392f9a7636c9c11d9d13dad688df6fe56b49c9dfcb3908ab1c66c951bcf8bbe05a869cfe81d45560cd07071707e629ab2a4e0b0f3f6c1fec27982112f89eec379afc9f0a94158a36275126725d5399fc2271a50ea8a836efea6378d83ec4c5d22e1506a9736d2c0af9cff9a90952cd65cb5d2b210dc808bb5a6abe8a1bb6c60707532ee279b0a8331140201ceb0785dc307f70e541e33f6a800a463a44af182c4d65ec3a8c8a85de691d9d15f0a7e3e048a2cf58fa18e015b3f4711983857a24c97d3dfa0c0fa60d2245199125fd8f79fdb27d31ebbc7ec69ee02d7eaea540ad109f5f3c529d26d6aec7fd574cc372f5e8c781d3c3a42c523b9ad3321d92396116e131d4c6a8ab852bdc56912492e95655958ccc0c72554c3a7db2f657f4d69abce9f7082faa4b55375c25fb165f3ea5aa53692cbf6626ea33bed7172d8117c509939f3334e32e8095bcae5890ee799f8d09ea49bc40afbb9178a7bd3eb44d547f354590404ed60a05db3c4f46f93762aa19f157cae2467dccbaa07f79810ac3cc7bdc4b87c1b617c8a34199e9e9c30674703eb9bb8f1b49ed9cbad0c12033cda25536e819d202ac1b3d3ac215dd22227f7b134df705a564a202869b64f4998d22b45f6e53269d8ff3d1a6de4163dc150d3647dab198ce1038e109c461d55ce0663299f274111e0a67e321cb4d0b10fd56be9e87421f6f12d6d5a7619c97d9be2c1422f5cb4704a9fe329fbc53037f41124254692936929249324f2dca240cfdfbc99bf0749615db6cf935f7d9d18181c4bb61821aa0a86ec31666817206b72646152a6acbf484e3b6051e48649465bfeaf6d254b38f50130faf1b388540a683d29fac0648601330162d6db5e90b9547448c92a4286ccf50104d7b05852f083fca5808203b55242c2600716d191b37ea3bc5f0636c5788127aec5048f873c9131a9f81e56432dd838e3f70824079f7e9755fb53cf6d3eb38317f0b4124321a9013848f80a106334336afa0e3d6e61d20625ec65dcb42ce19a2bf4fc330763269e6a48c5aa7e1efcedc1af5ff8850b66cc5e500939e43397c2450f150f0f98aaaac3c20852fc508d0ac43f21dbab09d2175860e9f55798301a02d2ad24eb0a242c1f02528461d8d5459014fdd760c13dc47ba14b07f8bf6b751f161b145d66d4624cddbca104c0dbaa4172bd82904d0d08933b183527deba069806e9598abe2ff15ce714a81e96d3e86de53784832340fa3893c06b7b444cd7983239060a4a2220c8333892150384a9eab6c4e0b3a136316922190f2ff96be38eb127f462facd44ae8894a1efb712c5edeb34c748a4eedb0d73e289669a7fa28ad50c145482dfd493a8df2d713a5d15986239fd9168011210fb996e9527b860f0eb13421d143fe7454354f00b2327125cc25c2fd24e7b554662cd92730b9a3c131cf5224bd178cd7bbb4c6c1612b567d291b2035822021417ef3a380c61738986ee0e72579123d1d1a58d3de5c35f5e611a05aa314cacf1606fbbf4039ab4489b1e23110b8dad52960d506194d1b51840a7ed09dc9d490fbbb09cb85c6d438b09ffd04bb51ab80245b8f266c6600ced24fb0c79ca8e088eb7558ce8a1445ccf4cb0566b22119229038a8da533d526a1a26d2197f77f8389f9fcf23a2542ee03ac56e511d41079283c6013f2fa779946a8060690c8b6f581c67b97837c9978f2245fbd4dfc82400fded0aa935c096209e65a44974b6a53e12b414437a6039e12173326334b34d247e37b0f4b236c8bace8828459324e0bda4096b6707d5244d65a4ef40042d896fab983a77fffce8d59d83a24a976547c6842cc8e7c9d2947bde8c5f4a63cf8e9130cf34c5a8366cdc7092d9761c924f8dc037ad735ff54ba5435305c17355dc913a8c8eaa148806f59a0ee69b2582c857c458e4688de95976522250c47d30dbf13b5dd612de8445dc725ee1e85698943ee51cdb1e9c27101ce9ccfb385d6fe846ad49751f24a1a932de6371782586c4152338dbfc28b466e5e474b9a4123d14e917e950466037f1254be487039e138b680f318a8f000a3d4119402f696a8f1012e736127fad135abcbad587301f35942b86c023b866644494e24413c7a2d6f0d0e894913b29b93d48d3f31a835b7f877d922407881b02bea227f28d5993c6d776ac5f53f7a6b505de2aca58563163738788804cc26d12c1c37afc052e8b9bd44118ede08866ce4d46b408224739e05d58e5527cc579b62dd29f0982eeafbe7a0ad4455da6c613e0168fba230fc75f51f21c902a096eba5807738bcd93306bcc9f482cd4307aac9ef1cc154222ed85c31816d84fa4da9a993e4ee1f8d08cc35b795ecf6b3396d5a979647e016c9e5829092b868e5531edfb91de503f0ccad8edfd7abb4168d078eb02c8c5189940dc51ac294bab54ec8a53c1c5c510092e546306f0439a3cb1040d402aadaef47687a1256efec0d4075750a5da51c7021e4d1204c5b65196b48016e234aa07e7a332691ada7078aba0bdba676cf04347ddb6a4361f587596c2db245184019ae01065bd8ec6f3a0f0767bbd1b09066c944454c84834c5fa243d2a5501740326803a3ea9fb838576cd27f2fc3e5a3da3566b6bdb8c588aaeea845ccb6bb2a5b8d0c1828d2e365e496171b6c477a3b20666cd6f602cf17c60d31a320edacad6d38b53b2644046f454fb7edeea67921465de5442a42de35d5e8bb6a959c5cb2e4f2ed46dfaf9e5245fcbeda7d5d1c575612fb5a4835945ad0ee3f84edcb30e03a0a36ea3fbd46e73ddf5d0f1c03d00f8a79c55d3653f85853523c3d5daac83fbc94cf6bd3edebf0ce963e96f3e6d4e3657199bfd143c9b3e65cd45c7fd7b236481d990e57a5c664951d9728c5ea5cc8adcf8c947d3f8b471455325f1381a9342eef17b2df1f2738b16ae7d4e652886d494d1b672d5085acf45cc77567209051347f4b63c80c7ededf2c3c1455d31918ac79206c7b7944b698beb4cd3ac52373d6f28d6dbc02842e14aeedab61ae14c6234a82df8ff2e70e4958cdd9142ee6e168c5ff165528cc1484b9f5ad90749b660b0659ffb641061d5f2c6161cae94e4049fd72020bc796f84e1ce57a655d2906626d1c3bed2ffffef7fc38c0729b382b9ec95d2ed03a752cbbdd4b2af11bdb4610ab23861d86bb49cf378d4c30628dc8911cfab231f8aba9f71ac1e81f5c578d86d78ef030b636e7cd1dc894a95a061bcbbeb2849d646678297afbe63fd9ac7926759201baa800da266a3717e34352975c60217c17f15bd37b3c4cab9329cb8c19886bfaf4f5aab87b5328232e238a1766ba755a4cf78b32f3bfde28c93a92d809ed9ecf330e3b01684607a705167459ad50618f99d4af89d7578874c05b048b87607e10526845b5950f28a732750c8e3167523a74474163a0439ac6ef304210d44154a2fb728dbcb522898baccbec8aa4a80bcadf3ee8c5a35edcef8e4449b0f4672d1bf43671d0868be9aaeb7938228fb9e5931f63c5be0769730e16de37450455c107b1f04a0f92c5d2555ef8f7f581eb47967598f9333100d4b714a256bce879cac65b8b89fc74c3a01949e04afbbf7b525ea2dbe57d3675e66f81a0d59ab66b333f7c9a5fdd312c14851967299955d29dab2eb8aa2cc445dd6524470822d4757c281f8f15bee6171e6540397d285ceba7647b671bdd5c72c8a61718c062e5408e85eb3b754ab3e27a1bf7cf7b41d3d5e8f6e6f36e39c81e627074b6e83e5decb3d4fa97333094f9fd62cdbfc56be16161fcd43a76dcefb346858d77bf86cc09faae93b65fca3fe9f5630341c1c7dfaf9e1812307974ffddba7f97f9086dfa74729684a68fd34d0f2184506d46d3dafdaba31900cf38bd7d879017421e9ff258dc8781ef0ad19173c7865a5cab54e1109ff1d551d97669da02badd40ca842771dbfbba03cebdf04612ab22e2bafa4b3dfccafb8c81e4b50beec1e57659df90d953f10e61e7099c407aba017036cab2dd84e7c5d514d0bf6b019b38761801ac834b4e40243ec8fc5e09cb069f175fe46788a18199dbe6499505f94b106ed9164728946f2fb84ab770afbbb258676342cf77ca2357c28fd8c7a5e6358fd7f5de202442dcab408ab9fd5e2dce7092b27c4eaffab8a6fa2e037c16322c2d93f62ae071e0b4f7a9c37ae18842f2219c70a436139411958e74d3cec1c1c2724e4dd32af26f3ff0161ed1041b63fc6120a990fe5e2035000be90e3e1e962e1936e94400f06dca2b0619827384f18cbaf18e1dc27eb71b419d4d1bbd0e46e1c9d5089ae829dd49027a9c1e2af9719b276ec02d9515afa8d246c186f0680968105e4c26df4cc0e9db34a8cd5dcab212fde4dc048150b56042865b87b4810f53b68983748da3ce8877580ced9aee30185eaedcce653a3f1c0305ee20b61d4f7767d8c4c3bf5e606b7868c5fd5c57eb18ed68ee59759df00d767bf715d9366552eaaa800204f3eedc3914a580e9ec02dc9f18c7bcc8fc5d0e621f46f6a0e1088e584f01eb7ff9795ccd8de262977b2eabf9f42249e00e6b64fc589b2b23554ccfd0c84a17bd6e03ecb5eb1207e399bbc81e3fef39624b1c3c060f2e82c1ef67f9baa1665291f967c17cc024c2f06a5e3e60050dacfb20616d3053535f5d83f7f4c263934a33046b4e61144e97c42015c536d7bbb4ec827e163d035138faba45c15f22e0478228ecab8e4e2f2d22ee4744ca93c0b8423c87b0ed2fb437e0039a359429ff73c9a5f929cb1fff333554db989d67b0133a0660e70003c6539a9173fca7d9f546eaf0382f36a98f10ff390be5f2fc9b0e6d76fe2fb601aaa56b3e8cbc403c7599c2a0e1c0cf8af368f31134f4cf19a59f436ce38767845e7f17ab2cc7787b7c4eb4700f0274f6664347aadc9f0fed8b4e22c00872c49aec7021bd9faa26845bcc5cf458c79e1c69b917d96567d4b0af521c17e9eb2a8330845bcbefbe194b2d8fb7e7e50a30f45f74545d6437f5e2a6fa934590f1ca98beaa71fc60349d24769dc59243419f7820f9388c746bbfb1cfa6a18627317d371873075427fc351cbf1cc3d2e3b1d193cbc48dc136430d4f8303faf1800c88b71e11bcb364357b460311eb98d226dd1ff00c6abc2b9fcbc644a408ba4fa04c946f7ca8e914c31f54bde83cf131f6d319d7b1cc1d354c51ef6b045f3c1c399852544ee3bfbecb158daaf0f11f89e82420f1068be1a28cf16c3a366732e0c44bd357a41b795a51abc904cc1c211e06e18312ed4ae2673daf77d1cab0763f045b6f9683b55157d85c31ee147c809b27146ffa5d02d004e36692ba98f6537bb4f8d4f4bd9cf722a7dcede9c459125455efe9acb5ce3c49a7c3700f8ff0340fed4965fb52fce053396906e9b2a64267bda7bb0e43a841158f38dc45a3247aa6d8a5ffedf37ebf72d4b1f7702c4d00c292101e2c04c0dd8afaa06139476cf29a4eaee9ab4ab9422766286387343b833a734f78cce484236f44374f57b5a4f6b438110b6258294e56349e6d800c5bfff8b982a0ae938da932612b54f122db28de4b0f210c5914c8d5b2a4f2ee3e46f3b423119c8bf64e682afcc324e61b6914f59ee43955ad63356b6202a92b4be11c9c8ca157d295b132596ede3d9664ea1a3c1b4ec94c70d244fdf5cfe0b34ddb07a0ed5cf1ce17a9b88d11402087943274a964d765ff4d69ae27d3bc26811ca63c0c9e1b83f8435c88df99e990b1e2ab37c1b33f0e08c95ab7b002abab3354cae3726444b4b24f00b68486771ac6ea059cc83d2e8d3f1cf9c0dd1ce2e276743b4b7141de1df37a7e8fe708f1efe392d8aa4a6dc8ab38de3312883eb18cc0b470912ad3b1672903cc2f189f402b07a0af57188e60cdb504dda6309d3f15a99c159679431e94f05d03400cda1724fe9101cc84cd51702b1420d0cec46bef1d0d2e8e5da53b3db905cdf01672abdc36c3838ca8225ca2e543895980557bf9e345499b2f95d89f7feedccc64ab794f828bf33cec1f994e8506c75245acfbbd7eb76c38e8733b6460cfa474dc5f72dfa1de2c5968806fc1ef33ba098c0a8783f8622e3149399b76c2056228acd49533ca826103296897f529ed9a8f91b3869a3650297cdeff243fc249a0da27eacaf3fc95b908eb2e3bc99f39b1453e868c1100df79be8c2f4ab2b9095efd0d6edd493aa264fac3f17a251540aa4b10b8024c2679d0c11ddea895e772386411f958c0a611092f169b28b07bc02b14b3ae753a29c82ae102d2e8cc25b066e3f4882b35914bb0bc6ad1a973e49bd235b731c89812f49bd8d7f0a47967ec60a829a37a2091da617039e07d1c91c27b557b424c4782cc340cef74e1dbb3cd8744eb862963f94d16a5a0f86c60ba462b11ed1a47ed8e66b60fa41b29ad28eb102dcc3ab94d6a36e520efd8ea757d520393969e897e1dd102d36097c79704a4a4d5f1fcd612e93a3674ef319d047693d1634eac8bb3f899526aa4173ed509ba2ad2d263f6116c567710e1ff671f66fdf8840413547bad17b9396d8c44ac82fb6c3bc1374bc5676f0739e2356be0e8d7e01e8f9d2183e63b8c099cea7c3b32e42f40d69d2db8ef529d9df59e9a7b7c1c7bfad782bc9ba70073f157358ba7e157358bba9f3acf3b17ae70317a561dd5367273d4deab001bb17bde94e2f84cbfb9034ed7ff7620eaec92d87b4142513882f9b53181a58c285d65d15efa543ce3f61e26806c888a25b8811c17929892d6a5f023071863fcc037b22c7f4b46d30f79b2782aea2690fdb19b6353de13563108d262d13f2e619163dd20b0dee12e745c7a24ffb10a42c6a0eec36d178aa641064eb0ccb583b0479dd1aa7d71f69fa438a6fed5ba045b9d124e79759d31191d9d20c4c377010ab1957cb23decd08d4886be7e3ace5b1328e816a738441f0e08c15e5e6b539bee3e3bf961363c8463331139d230b5a08929a5424f96e7b1e73174fc69f2344bb39b6b3eca96dae6dfdd8217e05f9d51070e1e1e93a7c980b09a9b8534dcc32085e1bdb6896c93b9c2ac599465f291e261844e14feda117abd0069ccd850b107bfa15cc93de7677053515e835ad37e1ccda5557229e7634af2ecec6d93dca5dad691dc64565f7fd10b808836f8b9828b2dc3a0d07959c5551d18de897be2899cae5d51e057735c11a5843548451135ade143c059e089754b3954d34e5d8de5547b42ec7a5c89a1f0a9ad150890be732a7d5fe384a2d96d839fd2cbcae4a921afe520e3f32cf1196fdc86a79fbf11f59170e3f32f76990671bbf507510f0e49956dc1a1aca92dbf0d55dbcd2049b4c5b360f69c7c1fa52161e7557703610bb61037f1e522c234bffc83952c6c49b0958e8bb105aad74bd8be558985ed80514cbafd9fd3514fa0a6747f8b28113de3658414a46a8d7f844ae26286e5360491623bc4f322c7375811b62d3abe2739c5eac4d6aef5f658bb3185f46af11c15f5a20bceabc4d2750aa32a097f0f25ff4fdfc6e0122009194af51e6a80bbe8b1126109188f2710d669e32a92d9cd4a26d22be30349564806bc22f5e746b5bbeb51756e0f27fff184df0f19d1253900ceb955d5be1b85ca5dbcab715b1b3271f3096b604aaf5d9f52a42b0b74a7260a2d64055cc2453cced984a5feec955c5e4de5fc42449b3b3ba9abbeabb1e022170cf27b54c631d9d492988e2fae0f62be6140507a80bcf43546fd4b20433cf7e4dcd2e59082b3cf7d7bf99a7251096f0bdecc905ed5890249258640e88566b226944c8d86248b700af883d3242415f274401ea8f37abab8f9256605b38a9491d07b214996b4cb7f80b8b355d29c6a4f15e12a8af322942e636ba1af88de58d416f750fb0fe0accb07f3278b08f837bae374c25089f8634b493756f65b22fd8fa45235aebceac1b6f6bd8383dc238f266053ed1e8c8c7e80c984d083257a883f5c230c2397126ff591363412039d1f0f11a03c0d83ae7edf8c05ee999442ac2de31a097ed90492d602be226a2e6e7260b56c6b1c608652eabeb858e67906a21f647278d2a21e81a83ac51ca3340dedb237c775b9a497cbfcb3464454684c237959bf8ec79414b51f01af26b123a72652bcd91f9eaddc6e08cbedf949c51f63ec14e8e384cbd8dfe3e8d82c57bcade8e57d061aee062d09131a2d569f9aeb8a8b20a3bc27edf9a6145bdef572930d7c9729b52257dd33305750dc22cc5c9fa7e66d269f62c36dc44f455518c5461d405d604ca8ef5865e18b44d3c54bbd6019c95206edb0d5f3d6cd1804776c292e77ed1e83d0a80b816219a71433d0561abbdc1c31c4436a38c7e1bd01801aca929682e265ed09297e2adea139a95ea5e2f2415a5dcdfe912fc5d1f554e02dd511a50c9594a14af4ac82cdb34ef75972495ede6e941eda4f23d058335a2b2a61ea229050295016daf8d03b382c7da5677ce0254025edeb7dd8ce00ae6ba4afb052aea4dd8c774ef1646b8050a7c5a037708e3a7ebe2544350684322fbddfee181c98f37cc42a997aae1666198c6fe2873d5459c7c289c759012601b7cc083387dbe2c73b38a8b725d18eec160f046e8d982e1b236585a7fd379f8f8606e579e6a3b967e8d9d1d9896442c6bdc9b94ec855cc01777b52791aae5fdd789d896bafb37d7efb0154a1db8c1e1ba0f4279115f071df06bb858fca5d572e862e92c2f9a31cc57b0ca0274f2ebe5e0f9e95ff0e1009161a9d00cc618cfeaf2a65813e2c7bd2c02f5d5fe81099ef8828473e40988b89133b2e857a8297b342581b03bd8d57527241398805eaed8941ab734657a6d774532c39641da2bfacaca766ad7eb9be53008ff3c9d7b1cdc6ad2adb7482f4f9b4975f9b39c42755643ec7867cf7541545168eefe78755623fd3eef9bac2f2d6d4cc87964842c444c0c305c3e3c64bc0affd9c3b77366ded9e609ecb7e37ce69e5d059b61b88b3d05c9e8c72ea84123547318fd7b19556775f90460d3cb157e991b86cb0d47bcd0a7bb70487ab38a0b14a7d311eb6ed3a5d10a2396ea6e3bff37a17c2bb0b37c23f221124cf58475201101ee6ea35db8249c1f4a2dbcdfa3678a5a7be97038e063e2696cdc67aa50866244e1faf36dbc40d1a9ba19822beaba5059d39ab4ed907fa3eeee7e90189ca743526471e0b3c0f85cf918e900308deee1dbb8a1d2322dcfc49ab5ae9c27f7d04ccd1d93012e82b4327216abe506a50ff0c98f52ce482b6b1b3c002cddb690c3983bddc34bb19e9edfdc4c6f7bfa5d3ccb011fbd6b1de258ae3e3265bf917c6758b6b469ee7fa5c0e2753c76398628f770aab314899b8069c179dd5c64922d8b447dd55206f2fcc8a0aa811dd8f93594058714ddf7e90f349937f6f7047b15259cdd1c1729fcb417ebd7918c16d6abd37d5be42b2e0ba9c6f834d731b4529c766fb26602c4aead445902bab5c1dafc1a2c513579ae7f2be3b82a81a1d69ba27a704d307948a5c0f2f6a4389fb3228d6eed41a9565965cd6964b6c8b3156814a9beb3c0be76652acde39a543c672540b15aeea93fa555fa2c9603aa2d80a7d70200d20eb177ad1ffe42f4a9ed5d4b7250dfa942578240968f7d1ae4ef092bd5d0d32e3460d04255f51afe72e5fa2d90d680a33ec2bca0a494a90bcea831f507cba763b3efde29af16e812f667031d3fc18d943b563ccb04654f9cfdfe3576d6fa051d05bc987ed5aa606e169dbd9c4052b1c6bc49e4b6925d78d1ec1c8aa04f1e0de5efe2f7787b884c12ca48166d7597e53cba9aea2f58ed0dd43ae3f7747f2dac7b6bbbd30b083573a88c1c169f38ef6586eaf59366e2b018f09930229892eca8a15101d2b762f870151f30307e625293de9a0ef92b0029e422d30221c6325097b8f04d71e444a13d510c4731e4a836ebc3e3e4cfeb8148499a7fa076ded5e638bb9c7c7f0b2dec316dc1423a7cb1e1142e4f57e701a8b3562595421513496d432c344754c97ff28b8320dfa8e4e71519b8a32b99fa545d728c2bd106e609cebd351184547953802acb7d0989cb4b108082b3507eac1f347da7234b54c9d27785951a252ae8c17af98899cfc88d7afec06bc73fbd0939b2ffe528cd372bb76993d4523e4bdcd195727808ebb435c77fd696ff46a23049880f483bcabf117f4d6a2d3c3ffdf7c92d505ddda1aaaa9f9ffeafc03830c14051b1b9435d9ae93ed1dd443a82c56336a7e9824b1e8190655511f85f3e1c0ef8302457e695df6c993f36b97d0ad68918fcfd2f1342684a6aabd1b366333ed4131a27b6ec43130896cbb4e3ab07a5b2582388fb9ab0b23635141e48c2a6d43ccab8960cd44ff860691f4529657da3b2ef2a299efd399adc840f8ddf466886567b4e50c62c435c29f2a298c08d7ab9acb217e57547c11cf2832a1eee183ee79f15299a50faea3fe4ba0e391f3c953c25c63e04a3b3cb7a6c4d30b75d092ff1b58c487d7cc7020b2f59d37b0100d291b39aebc30156e32fa85121b0abc74d86dcff8a2946455d302a53a8b05c9c33d38bf295ecb6c99feaab48b36e79a20465202323a2147028b8ec388b2472398768349b2e870d0ee987154559686e4358178f264637ff4736ee45d850816079d91765e03fda781d3c6a050d88564b0cfc8f3ce91385b6ecbc4ba805d5726c187ae37ef294bbd20a559f34bded076f113fdae72c014d0f649e572c479f99735337fb46db9480810a9f93378e286b0ed8bca2c55bba579f4493a427f55d94908b223205db6cef6e82b7f0cc42f3dc7bd1716bc8f43c760345fb92a64e8b5cb472cf40ecabb23f4ee2ea1159d7a1762182d274e81be760afd5b400600c538a92aa6d3f3c6a2fb170f4782f5a908d739cbb7a00dcbb5badcf03294a22c7653ca4ec13b45e82aab0629979d32de15153be0ebae14b57d072c4a9366b5938984d6b92b698faa7dc95082f48c70fb91c9f725872feb8648272a614e619f489cb2e71f42d8c09c533294bf55b80366cf4b712449e146850fe4dd4c04e7a2073e6d4e3f92157bcd0c5237504fb525f7ba2ad5309259122fa8310525fc9f3dbbddd5b636559a9e2fb4018d2cef6f8ff0340fef7e7fabe7eef619b66484803d43f2caf5afa16dd12358e3903099b20eaf3ff7f6ac99385a1209d81007aef7b1f589681a79c13cfa6cd2e4a6fef3f7dc9b2ad990e665b49afb4554a026020b03c699ea40394131440e28f61faff3e9b2ecb1a8288080fa4f5182a60f9d2276a45216ba626f3fe13cf800f1ae2446faa114b71a04cafdd9a601a38cc427d727b01c090a7bca44d6eda1cfa87041c95560d2b2344bf08c67956461d3344f895cc12faac607b08dfac183732bcd7431e81cb7f5a9b2277d342e97ef044e284d36b28128a7010f4899e1ed8d9f4bd0aae4dfbfe844aaaaa411522fe7569ec0c9a16f47752508ed18fa41444607df72f556aace0f5848d728c7e4fa6fa51ddc9367b5b4e28266f591f583f96ef452a5d2f205391f641c2d48df2a9ede46513e2c935cb5016240cf716278699a658b017b58f56d8910f87af012df139cebb23f3902cc050fcd667b92b1c9e3a3edcb7e584ffdd4968582f395ed18703aeab72048db535898ebbb4e6dc3ecdbc531b4f73738606359ae273d2b4c51814d67107e064d22f2065daef23525fdb8675272edb65aed2cb853359561566d58c37ebbc313826d342e976733c18e6640ae1800a32bdd442657f196a9444dd969350f16ce74fef61415da6974b4a655280a49e2698dc41ef946e454f9068f138a365e9e36af0b0cfaf473c3231cfdd6c27335ce70750771ff3c2c1574ac1e3282c6f63643b2393c976c4cb0dc6c20ac9c39ccd1d2d2817ac47fa2793c78ccd0484ecfa2d9d0db1a17aedc631a81caa9823686a67761e0258494926399281f85d57fa5e9fdbc23983f64f952ef14dba55f86e4d83294b74ab1ce5d89d102cc1174ef605decdbf6e3165c9645fe02c98eb66db0c71eea4f64cf605573051b003d759e4b9a0c2dd363b75ec87dc7dd3e5c4c3cebf7cc708bd64daced20b7ea6a2440acddb2e24da1c537aa5697e47a7db927755c551724c0d46f11947798cb81c10f98be71b482e86449b8d2caedd6ba44b1d1a6e69052871ac5aef2a8b01ff02dde9d60e4cb0103b245e9c1c1c53077e69b81200d711742a7ccc31ba96aa75fe18351169a128444f4c9b6d6176bfa587f22808537292a1cb4ec9763b1888f47ae3a66c4f034964fe3a3493e4a4f13ced1ab632ec2fae58674ce8af3389f75de418289e93609c95e5b891349b5c282f70c8387417b68c89c805b8c805d6f205dbea08b52c4ca32ec9465928be6aed083f947befb26cb945b3a21f985673e5b3bd1cb1345180e9de8fc027ef2a6f1062bfbbfa8d4b3679848531133c0b3f113c8b3f100d29932001626aa199ac942f28553007df8fcd5f9b880def088dd6de6689131e4458111820b0f0dfcd4eb08d74058e6ab43e44312a2c018b44451d9d3214cc4ffffc911fb5fee1693f3dc118c191275126c91178bc5c9871c68109ac6090f0f4b2371be093819ca2a6936a7200186c4d912079e68f164e1488b84c500225aa920425d09e493c1674bb51e4d45fe4bd6a5ccf131815cbd68353620e01869d440a139505bb050605404e0c4afdb47740d50d6816592b626a8488a860a2ec004025b6961a561de1a66682b4a32567dedc44428e293aebdaa0b833ac73916178e6095677f7f2f909333ed76599b3d2c5e4eef6753e84cee66edff3eb962cedb1542baff80d9b17b0aa41b360124b98b2cd61c3ecad0b27c4c0415b7961297530c79b314a622bebe273ad351f0241815aac8e31e48131aab6d3f82889748402f09bddbaaaa4a9a7b2b80bd31287b9b91d183679c8967e0ba94e7df6d295e61c29000635302652f69ab048686e55ef1ad7371707d2dfdf46a90d14ef5edb00b19bdf19940a78197824c8475b377b121f3595d30d2d459d22dad19e84359788b3a55dc6412b24149edcb34af984e32ea7f13af424f5b39ee9c85cc3d3e2f8ac4f32f991a0b8f23f00a22ff294bb235a5dd3629db51fa27eaba21de4a93dbe8256f2d5b01ae5047689d3a559595f8269084369a27c256ad7a5de3285c05c20a51983037db0bd6980aa6c276a2c55283661c164f7423a6a3df0f0023fcbc0753755edcfdbd1067d63f798a9475c31361973b4786fdc4c36fb52707fbcf91e5bbe980fafbf258ed14a6b76c04634ec55e606cb568d524aeb52a085ec0c2ad28041dac696a5fe77772ae7eab1c838a245ad00b5927c231d0b0770290c324963002452e0477501603908903edf17632f065aa295ed4d45a0921dcc476e19a9204fac328a2f38e6dedb84c8c83fe2165f2afe15c96c319e4cff1d15eb31a962cef34ebfb671531b3115bbce6544a2d2e393223b3ad3041dda6d5cacfa1bcf1869d4e9920420e5ba2fa69618fec0800afe3b32c1e8476d55a6e5b5e32f23527154ea6c137a93592702ae60cc843b3c4235715224658194168db520f797e205fd07aa4afd7036b1ae5538ceec35b53559d229b6bacf00e6ae55f16117789bfb02428a5c2c4b026eeebc631739ca5c79b5740f5ce535b5ca636cc8ae38484bee01dabc56b30e39dcb77119689695b238c526b4302dc41296ce7a0d5a32326e9badf89c3c11b38b76703bc25b872b37b49b2fa1c82b5867b5b88a494cdf98cdfc4ced7fdbf852307b32e4150e248ddcaaf0062ac52cf6feae4db2a7b0090f047705416952d27b711498f0b1aea6c56d1009a31800084ddda2a02566037d6b1c1ad81a516005d0cd54dc6905c9517b9b972fffdcb52a9285a54d22de948ff7ca49ba4ea685abd32399224a3f7eca38f51aaed24d59b1989215af091a5105001a56e5578c374df93a816793203af1c2e6d49b8168d5b22bd0e30fb6b1e1d5ce827b001f403f069493cc834a5a74568ba2a0cd6043bd213e0c1f82700597c9f81f884ad17c1639b8be331a53133e6346baee0c751fdf24bc3faec99a5e1ca98d53ebbfb59802850470a398fb3779554afc3931fba17e4dc6097a09b2930cf6dd1998516c98b075d2f021d18086e2c741205fcdaad37f7deb0f8d961f2665ba72d1f55636160b38e272603a1f605900be04e866130dc550e303c099681ff5b8e0331dbd5e758a6a4efc9ce8146392e2eeb1d1385f87fe8cf8636a03835b3e745bc64ef9e1b1039783c777b6fad9ec5745976d38de453bcf5fefd3d917dbc91fc6badcc3e7b838453f9d63ffd9474ddc2a006c42f647d923028cc6f6b742d6d155ec0c9cb7e492bee898024063c217d84a7b2c42f3c57682d020a938c2b364c661ab207a608cb67d139c45d957f01a49d98b2be54aec2f36ce6888d3b02cec0d013522d10b08f18ca1c1ca6f6e75609ff8e708de120903bb5f37cf5399ca807cbcbb87d66ed7f6bd48975b408149089219d9b3b9f8ea9e577ceec54884aa2788c82319484d4fa139917bbb506efe09c31226c7e801f0f9822f6d7822ec5733eb73536b112cac759e08a10798a07f1623809067fe28ec9b1c90fe5fc29b62cbdc8bd020f6e541a55e45e813a430187538fa58063acf6a6eba82d5efd32f5f139649deaa54bca75904fa6072b49c00426b7dbe7bf4c90c02692f3075ae0efb800a8f19857539bbb754fe90ebbaf5e9c58334016d2197da88e108b5bab990cac9c3e7f28e7959160a49ff5504018436414b0d26cd2daf0cddd1facae15174f64c8a4c205257e49d4868b698358045526cea6a3e571151c52e0f927923a601ba8472569eda97d44ca6328386657641b780a84afd1ee4682501848cdb62e51b84095d6bd30780b3fbc28dd64dd0b5c2002e9cee7880af7ae72a934e914d93456317d1c1d9ca6b558d0ee23d9197c6d111c8ac56907ec6de2c3726681bb589d19cc1ae53242b64b363722676160c79547d89cbcb545c597e687b499937e187fb90bdd748ec76f94bf0fcd5ec9e9cab59a9e35bd81b53d6ba8bb9c848343cc0eef6acb2c22c4d6f81c6273a3464fa52342d0618487b743a039666b6929a7eaf9b7396a5e9a5675c719d11bfaa3064904a5afa74d242faa1a085093b68424ea8f9cd9bea7c66d62f120ace65c4a6c9213b302a5db34af5390aba23d3fb14ddb1b9f0def16dc0498a73fe4ddd9c3e51ce873fc49fdced21f77ce77573ce68468baddfdb9072c60a07e4aee5b777b5d55ecfd691b4c5f30fcd884658f93971bf67191aeb80500d672f9f12639807a9a3963f8029cd1e079d8dfdabdc0518cfd392a9406dad87a758878b9cdc6d08b30b7123be90bee07b5d4a2f68f488c76ff52ab28b7a8cd12914073429944a216cb299254ef473ce1a7ec5242d142a21a6cc35c0df446b422a8310f30112dbd65243a129b0a3ae181b9023ce7e1d9c7abce6e612da078e578fe5adc7c81d232efb51c7c855d61c23d87888bc8d41795613f51707cc347ac268b366425d128bbd5b1a8dc9e6c857c01868e9cdc0b7ca034f96e3d6e5164b08d8a0f7b749a2e891c3581134db4f9c23b8df3cb2899985d14c03652fa8b642455efd0826dc3b397991471ef206103aa2792c9dde8f1badb3198455b914a7f60d4d2fe9a789666d2f9ea2ad82714195ea46cc475cb33c5563f8bdbe045469359c50e8e0ec7ab0bae529d1872ad4779aba36a6d974b8d117b6d84e6f64d0114b67cc8c738af23766fae129612e997a87faa6c8e8be64a3885cbf00ebe7bdac0897bc6df1518461669ce2b8c32da32907d4343f93129177fb496a9790dee07bf7f90e4bf3dcc9f89eb2fa26f699ee76e1b293879e17949dc626a6950e95d7e1efbafb7b9192d07c9fef87b9a47977629524c43046cad2238c233d511f1462749bd859802143b4dbb443a3871f1512acd61978139f26d73da24f731df174fb8da1cfe5a98690a2d2c403d6d42b7b4df3ebdeeb09b2c007e1b1f7b2094e71c2f4c6e8b5d648b68be17c5c848e0f9993e0440c2e0e4748694b1904d5b0d23a1815320461207478f42084a94c1789633d56b95aa99c4704914e5c1619404b85c620fe648e6d2f71a2461e36c5692bb2ae64b890b2067df7e26141ea778528c7eb8265b450b5aaff19ec9bd4f470dafc30dcd0a36fbd8685bbc4c5eaf225317c53ad8a6114c3e8eded5f3a9b1735fb3c17860aadd63181122aaca144e25753e917a9a748f540412b18072ed64af94102092c60252a78bdc3007894025a38295f1c07ceb65094dfc27839e2489dadd2f99a09b1ce5cc82db014d166450c1759a53ae9391b980084d2c19e3d9c7c1a0cc80888bad0ac6163d5b9b3503478bdbb7bc5ea016dc0735272847f1b7466eba9f5eba08483365bedb27c64f23bb98aeeac379ab8327ca77b2e21179ea26eaba9cb168c5a7a9751de707d47f3b12f3820340a1d4d55d099a9d59597c3d806aaf39f2294f25edc86fb44e263ac3e093cd9d555e08cd88f2b28fd963743b07e9e65dcfe93ddf208980036cf623db8a0cbc3f3b9fb40a7378ed7b2d1ab2f96811eb5b6f76803bc21a6246949aa39958c0c9b9b96eb328fe0f49f1cd325f3309daed55a3fc3fce82800a6d5a4b278b6f493721ed7487dedeb16abb33966813571abbd2d25799437b735be37df3ed795834d0a4aa6fe0d082633298dbfdb8d38960d6059a2bf6593abedbee2fa875a014558dfb047b850698d9580a8359d8c89616a2eb2732a685cd2a434267ee038d6b3378d3b9b911fa7927afd582a08eba79c77ef44e20a11cbd49d034442b7aab36974ed053daa52eff65ee1b61aed4df3c52954b9b4ed0ee7fc047666ffa45c3a9a05dd4c691749159195d2cb68790128edaf3a9b4c6c694b9cd46d59d5d9470dd9db9a806da1c8ed2e05e8fba9bd9484b9c15e8089ac272bee7464f5e4bce9730cd47854b6d680071bad2a4e7e39c0b262eb42b1f377c24e3e6bf5d0b69b334eee77ffe49733919395e058de888979f43e0e3392fad2e10465eb4a28fb0855b7859958f50684e2ff08edca3e42dcb3fc176bc56c80100948fb04055448038fb9da69b5a8b1c5d551a12a61f74168e42e3a01c1ab01215207fe28672076dbd0f6dcf88bfafde2e95e3578f52afe65941a6ac0b04db4d6a4bea44600270a7af471a3b6e7d0eec73b53b12839b95e1bfaa2b8c51c9b8d0d6e9ce2f1d5ff2d60d16cc441a969f20d0f78ce6eb0518945d929926d0ca1afde279ef31229e1ec77fc4b8a192c5fb92dfc7f45b4752ac9ea0ffde8a930a4a92ed21f0029852dee9f9f155f95b66e8c375455b679eb4ef5f89de495b9963a09ce9fb5d7beac70ef59c0cc8ff3c5d76ba993cded2f9bcecec6a72bcb0b91b9c925e0375e2fc0684a5d1db45b72844a70e0e8b8eb25b445d904d51ce403c7da0f9aba07f6ca7e00cb92f8102a014bc26dfd1267e1f0bbf9a1fc653ac37cefea7f4f3bde4f1e37385256983f67f4436419c39ab30ba411dc590260f4207daab785db9ab6f52663a1947f97a3c5b037add1e68a19c6556dbda95f0c44f33508b54a021c2d23e4768834ca2a786075b8c37c325423e061cf72fb853ed742e59e8cea9e415cbef0b28b37c336f18b48d1bc31b7e113dbc62fa935e7bb05cabf91b4b43027d2de70e34c4f689649c84a92a035340a998db4d6f5b5290e89abc1270af2727cd993e981f436a46c4ca740fa6718fb9e285dff1a235eeda70a0da2fdd2309bd8826fd3dd86d2d556570e4bc36d520b3abb2d0ecdcf19e256ce74d6dbdd5da1dcfb028c0a5993adbddd9d2e425b6673b68da601bbf76021f84180c6294bdfdfda9d8d55b6d1c5ee7eabbab4da9ec7aa6d4d540e04ccfde6508a0d7f1b825f37ab90273793f6895a72e9948397c6c25f4f293500f8ff0340ee4d977fba4ed2794f12928c69392d9be66caf833d80828a23c9f0f9edfed72afdbd681156644464225cd17b6918313b87b31b8623aaaaff7f4fcf6c1f300b7bda04b4e8ee0352a7e312e162435cf1ff54bf6cbbda082130d83d60f49d7ac0ae46e6214fbcad5ef2159a75b7c1e1fc94d4da236fd87ed03a6cd011875cd92cb4e5e14624d7bae79fb7731672382234fbe71578bdb47718c91484a70e8a2fe66a6b632d248520124dad8a0fe38261d851be5b5e253a1b2ef9b258357ebd20deaf87772dabac21c70187641a9235b0d8127aa7b2eb35315b2979e02e9daacdc736a6660cbb34a547aa45beaa7236781ee4a7fb8585dd2185e1cf36daa90ba48c01ed5a66f58b7bfe4d81a29ed164285301af10cb80f4dd23e3a834e221424e4e8dd106562b4f2070357290e750ea95957bc42c65b763c00086f18d06190da7307599981eed7ae52ac6116ef3052f65db82afe4d15d4a1f9222ef12b1c971296d6e6bc542ee9a7d97b8c5ade3b50001083fd7b0734dc0cb02e6369e570c7758ad02a6187cd707064af27866c792c3f3dca6b61dadece30a1ad58eed31abf6b7498f58db3448a551a2c8b8b12bf6d87a2ca3460d9392de5960f4f220c9b7eb50bce8a8d4bf4ed534410334a34f9644ea91c8e0e93f1093c46cb9216617ac4b9931c26a1a536c1d71bd108a1519a532aa76c92b2e7efca6ee6f94a26a76c71aabae3403635b8f4100568492110f7cab3b48f28525523e4a644e30d9496a4c4cb954587e0e991900c46820778dd1a35778c97fbae6d132af36794f7e95dd41766c9180bd451e3e2dbae73dc6d3e7fe7cea757998a3e1e15a53d87b63cb890d291f316ff57a64f017c37f1d6627483988e6d7d9d1b98e019b8188a57bd093b22c35bb5671b963b78e8caec47a65e0c489c73b602a7c591b43226fe490be62bad2e8143398a3ffb0b214859c15624777690be4d449183c5d9c93b64bc7afc543dba5dea048ef5d73381c78631d2f91ee3ed266e438291dea30dc1987023cf77b2298e24c917939b4fe268139fedf5f493df826c41c60188ccc1758965f92256ad5e5fdcd14b79d330e4d839468b563da50bc38b23cbcfd59365268e4dbea964ba7f0e31208c023896a2ffc43b1f108401b016f851dd748ce5cd2e480170113dc3743787cfbf3eac7374de3aba06bc94e7f245186ad49f4f81674179bc7b29912706143db264ba6f216d9d65b8a046d17977e1b4b8c6e62f190045f3feed535b56b1efbb84aeacbe1e9fc201c65a419c1b3cb8a84be45ede5cf877eb33041bd713df767c79cded081254e175c5a6c0739314a51ba9f51a3460ec73aa6edf944bb13fa4bfa0ff2bc42cf4fda2e4d9f10ec00df840e073ac95113839a95a53393138ff98e28ad2ec5a4f8b597213480195d7ab63f211b4fe1a26cb4ceebffc1e4434ed8981449441e88b994713a42cd69e1baaa281f58f03ba8467ecedfb0d5528d8b4580cd06b19958c08c208fc653d84bc8b13c65fa0252d0e906b48df01b93e88bd77fe713749729d477111c821ad7117892a8861be4c42a642173c81c59a4041a205969f994a80f76d633d5ebab36525e2000ef5d80b71f88ac0497306401323a4280ebe1eae73a9ab9299416b04d3e8f2097f1fe6b50fefba3df9da7dbe0b6eba00d9e1e738e82b39f041f56d1b281324dd0acbd6270f96aa8391dd7747ff86c04891e6060ebc24fbc636bb3da14d780a7b237b085bbb4b62d21349a41425c466aeff4143d50d0f61f7c7c87dab7942d6cbc27d76e3fdea399ee07529cc2e693ce20a884b4049272ee5a7a528cf512831c52955896d8b843382d353487c6f9c912a8598c088de32e47340e6c768621886883d434d703c454e890dbaad55fa479b1b77242a3a72a242ab5880cd615113155719525d3bf1e2081fa18b966b9dfde1463b4931d35d14e2844f00e9ba27ceb9daea5274a17c3257314d2c31729bcefe4e02c4848a71febbf1feb30f9aa12e647191589e686f96e3ea3673d0723cd0559181fb1c14ba5486d10c4752af16da3cb95d2cae7a80c4c8af4a9aacc8495cd594bff15c303d6a8e76a39b808ca0a7eb18aa21f1a8838f931a9ec018da48e8256709e3e88b5133b3f4a570b36116ffff8ed1d93a3f68ee161fcbb32daaf4483dd76d824b610a92d83857cf4265c7396c8424eaa0eedde2cd15d4c33d7d4c65df87b02baa8d9c9a00335dc8089c14c61dee8a8250328702cf1f39c2b39f396e4d48e977e8d96e282eaac79b2c0e139a8fce82b398b1f2ed862740ec0539cb4a95466a237a3ba02fc8b304f3de0b517dee2ad72034115a2a71a8f644eedc848d8d447a627b2188e6b60899fff57b3337928926e1895088e82fdc16191512093c4499f854f569836d092dc9400eb13714acec05242da8d41c522428dbf6a6672247bf3f805c186ce4829706c1fa023b365bf8c87efc4c85ac09b8ceca0a2217cf60589003ed85b8f6963571f1ea4fcacfa4ca268da16e69622f2ac4fae8fa08f9ac986deb57f5d9c1c0b3802a03a6fac010f0fe9400dacabc42461a3dc00d6d0b2d016b55112601e0c5c4235930803749f5ac1fd45025d0533f9d3ab377518bf684a681535ecb319b0b7b4c6bcde1f651a190a32063ae3624790efa08302179b2cd313696736eab3cdf6e36426e05d8c1c73c63f5456a85f4492b91260ce79ad52bfce6795b35caed084bf78ec422c509fe7d25562a9f4a4741a7fa8db895e2b011b22dda29f567b6b69ed36d9bc67865c9fda55b5a4977aac47f7c444972876c58eccb012708c12b3573ff557dc51250a2e525dd9a2496e9a22ca507ee2bd624de2ef1973f5c2dd9993da78af1606f6dce1fce77756c666d749bfb77f23d9e79d6b3c29e5020de675e17e7d4a675ccca46daab5165737f59c35268407c2cb84279d6519013649e88092b925cdc50a0d67b5fe489ac182a0ce79c8b35b0d79236ce8c8cea632c9b11e5f18c420e642dbcc5582526fb722db2e3aebf111f2263ec64586fb43b498094efb21286c510ea21cede296b576d34ba7b6b1153078826da9c0e459197f67e768dabbf01014d83b53ba1b94aa047d214cf4eeb5917f84fb3c7046a0254b066858f1ad31b38506f79e2872f89cf1b9457358f0fdff7a63bce0ad59ce1e54c7ec3685ff7e5528e0d4349fc71acac174c13b1071e0d6d3f96ef329c1c3a9b9bed6698d40d31b241a4b9abf9863bdd6e3dd8c85159395fb0c36b14d41c426a530e70633e01277abac5b93cfe02969ec558ba78a2c024c7656dbce683859a0dcaa2f5a8bbb61e727a7064aa79341de81032abf8c31c45a161ee510071348a1b6310393abe759e7568f2d2201b9f0db14ab2644ccecf6a7e60d5cc61d238030c0b0131688500467aed7a905817713b638b26b205727e395a3aab5d7b66814174f21efe07693d4eb5a5f0593bac29d33557badeb0712489b544d8aa6514b694437a918ba0c162526a3c60d2648edbb85389a927080aabc5f1f3610ca374ad2a5e806a014867dc4489fd57eff9e3b35f1672c1e5a43f8e5f89e3ef7df8fa5a627d3890fc3462fc2d26d75bc20cd56c1e0d386cf5088fa1e625ee331186b9397b1bdf0b00c54caa4fb8bd91f309aa86248383204f22d391e370a0d1b7b3508a26bcfc141104e304f533b5886cddcb7c8eb891d2da05b637e55783448ccaa659c6c84dc0d5c1d0c702149621c06df86b6a08b02f90188cb2fb39baeead6980772e60d088dd35318046943b8258d3e7a85293d8472d75aa04592d98020b5dac3be35f5dab537885a496c697071a8ab190643c019c724fcf1fad3dc86a5b0178f3c1c3b5225dce0fb8ce7dc24eec396e3c73cccda9dbe5b8895e37c9748dbaccc8108288803c819b37761d9a7842364784986d31cb180ab9c0818a1796457888636fdafa14911531e9c2ba4b548ade5bb454024953750ad07d8ec6c7e70057ea41436f08d4b598e0bae203c67dcca498ba47eb46b0a15b1ab9e9653023326ec87626050a239e86043fe010e645b8ac6baa36701ee7620677014beb5a1c71a9603929158f97e7d03298a995b4a9158d44898fb9fa51f87a9672b082a3dc97d863572dd5593ac7e9818685f8843d89a21a07da3109ce5202585a705018d0fbf1fd921428d87d4bc03e7f910854c881233890b730c293daba1dfc1da64a5730ec9370b72953065ff9410520667f943d798c016383226a284168b689af695597a3420ab7a9f00bca019dbde3bef1e8c07212f014c868ea306765abe7719a564ef28ffad7e7ec01933be51368c88b8ed8070efa836ca1e16f06c02abda306a2c90ee0145703a8ac17f072a144a638a2944556c9c5536d59f51fc15ec5e55c7d948c4e0a20683787d946de452bfda86d85448b1a37e039e15cab6042f9a828e0f894f2fe8fd505f2e0beafc8b6110ae160eda4a8b19a65c3f67bdbfbef5e6f403f0553f09491c07f2f49f609eb755bf09fd463ae6622ebf873ce02b876bdaa8739b600de6557fe9641d3af68ef7eda36d8d6fd0527f79cc565c6939ef31900e44d94603394b0941958480a587e0958a4f246ad976fe8aa4ff20668e6c27ba67041ff79fc242be55263f3d77af52632d86879ea3e6967164f1ec0e1802016890f65696a6cb72790cd46c71680894a42f3e9f9ff6063abdbf521e1366ce7fe6b278db94d85142b85d51549ed05aee41d03c8a710a2d77edf0e844fcef07a4216871e7d423ad7728dab3e0eea7821be9a98bca65ca59011254d3388bed90127d648c9a4a34e46a403f9a50144ead5ff9e8d1a2cf749753ac92b174e5b66e678893ba49aebd9969b641629a2af3c0263081b00a59d8d984b803f663cf2528f96bd18ba07c1c2c67c5164f991abcef0958bd67df046c1cd50a3d7f6a6c085d947fa5bd174909dacdaa0c11cfade44ae315041164abb67cd9a5fc333c5123cf12cead4881c023e79ab0acd7e6853d8bdc8c0eadbe269671bc1e070f6141c167fd54d410be5f4fef0cc11cdc560e81974c4bf23d70b4f4266afae67a1b39dda21b83cf8a9af396b6e48f8436e1bdcbe0d54c6e437c2e48277cf61aab6c8054501971103bbdb4fd20090c5e2196d09f35576a6ecad824a6143f2a35e3d76749ec9416949a214bd985d514439f0dfa2a1f203610111ba9841050764d13a27b94268476c56b78d67584445d368994a65a659441c4c2c88174ae93637131807caf705eafc6601e4d23ae68fdbc2258621bb42ae9224de3aace2717906f12eb97080e09a52cf194338c2ce3bb26e65b3495952c7bb03d7bae5194e180b7faf8bc0a4cba1b20bb9c5ee50455fdae5cbf43af9bb34312de0b7952bacf57f18829bcc4085e3c87ce41e9f0d10cc10e7a087c9f323e1a8140511f097b1570082527beac6b4105e0ff6d1e3c83bd8a3731d20faeb478ec421961d0332b3aaeffa32fd7d5457572dc7fb5564016b43dbd69bf498aac8d3e933b3be11d1e15f7411de97e1e67d0ef885ca7388359f0ff8ea18ac1edfe738e2683f164aad2fdf14dd8bc4acfc3644a96b2fa72a826b2db7aaf26f2fd984865f58f3e8d8c180798ae68e7c56022697c75d7b7d96d8ae89c4ba8420915b80bea75647602c36869856b59fa132afefb933adf62446bc9867ecdedd21ab35fa6b00afa6aa6d7d11883f61efc3c692f1bb42986664bb8715cab75818e048bad842b83dfd0e9f4abbbcca4a48f77e695fc6aa13a2b145c606df6c41727a06dcfc8bb8a980c19d73be827b4b7bd4af6b2f596ad7c3167996b28a9a1cb2d4b200843575a13690461a6bfb63f7e18d839c56ddb8cf6acf1c7c200f8ff03402e9bdaebbb77dd135696a0e7e738a59513b0018d85c4216107bf9af609d0dd51ec0cb8117d1d80c23ac5d0d333a3d5ea528cd475c56d63644824fde0949009b78dfe37feafaff2cb3eea16b13080ff43e05c64513c4fe2dd3ab2a64baba5a4fadf6171d78a25e2b6840ec8e9a36a1da668133e10bc3005eb7c5d6a11d48adb8dff0212d024a01a51c730d35120a02e442714208830ea509456ed9c0ac664d54734bf7319a80ca935216783d68823838cdd4397af3301345b568d2ae5eff516951dccfd72ba211561a7c8efd3b17e4030e29c49b676a9fa7f72c5c28885a873a4163532045a5fa3aebfe6e2485c45b9b8e52804df2a61243b1f1e5221e8cc4081884050dfe12ee72d1c91c95056dbec707b91b198d7cc71e9f872a45256cf888833a798ab34d40ff4b40b58b8d8f68f2bfd3f514bd6af10ee7970ee4fb202d5e064214eb74f990f985ea353730b672c5b79ea1f5771a8a638dd2ec63cd0e95f08d38ce367e85052821b59044753f1b07ab6fe4e726cb795d2a565f763bde9c216f7e8bb782a96aa7ef317a0e8cdfaa4c77808bbcec944af91a99bb655760b99904d2e91e73b9cf619e1cf1537161ca4847fc49866e01970b0f2f92fbefaafeda65162e98a7b4de28a4bb1de60a572cdc436fb46e73e261c0c6e803cd5c5a0152d945dfd12645a2546d78c83cc13dd656038aae25026de32c264b293445ea0f8ddc06d22be36b2357ab8097f4d677bbc2876041f140627a2ef610e868c89248eb7c329642cd49921c3ece15d5497ad2008ebcd403d168b0f3dd20f747282928afffc737a82417ac2cf48121e9f43d31f3a0c0c4e9a1a1ac91a3ded77ed582cd0408999e4ecadc71d85d3a24d077b13196718e9b6689302bd91a64168ac3d814b690b90fe1bbd18ace53428f1b493ecfba9d156646177c331e97d2b6d7426b34068cc31379ebc98fdfbc525a32445b9e6d2cb2e9fdfc13046b34064b0e8168d9746bee72727122645980a5d51a8dec07e5ea0fcc69a0158eaf980b712b13cb3411274d313e0e43b9082a62bdd7a25f5d29c2caaf77bfb99996c8354577eb143ebf3fa36ce4b3fbab9c8a3284bf2304f449abb74eb8dd24f03bf464315ab1cd49ef8c88ac1ff4c1c27eb9134d2fd4552a795d214097f117295af74d034412e311f6b17569703429ee36fe1a0c0fff741f87b757735e289b8c86c84773ec8fedd42d2bfcba0e83279c1dec5d0355c2860d8c815dea5d87142715d9f165489663206cc28f502f7158c489ee89d4d8c77911a3a69e214d0d9263ace0e55ca3c78ac51b8c5e4293323af020ec3d8c14c58c4a5899b0f538a68e0775d745ebe5eb5d234a46df32adfbc21ef70a0cf815c4be27d19041a8250664e8e094ce27e0003f12a2d6314d1234060c4e0c67e69f61232312f894cb5b53b4cbd1e5d93b79ab67e0dee3ca7e15bc1ad5f51ecf6dcfcc141d1fbf594853e5a71503843726968eb30f915271414f4dcaca3ed8630b0d9e7caeefd49e4515e5a11f55a4d24ba142d8030b9f2c5ce346fd76c0d7b1bb89cc592b94f9be5b066aeb5d6c62534216080228948e1d1d49c3fdd49bc3fb58764bb71cecdabe13b2be1665c4d4d73c7149e9dbd7b2ad88409db1a9c35e49d325b8f79ad4a16aa8b5e9e432324607268f1f7c81e77b82c2bbc58de7f7d54a4ea777981b224f348ff38619168b43fa174f2d14b55c2f92b32769167d56ac10ba787ef61e7be484daf3e70224dc3275e454c1348c9502c36bf41a13174538399e29d256e55dccef016a282cbaf64d5ddcb35ab537356707556b7dc6555c850efd267f4147f1b173f7855ad91c1b9f9f8baeb70017f4380faab60eb7e3228c27ca5084aaf7c8b4b7b3de5796857f0c13e8f114bc00326150a4923f5f58ac2f101b98d56c7e614462bc00f1a4ccb26515fc562f5cad787b856184d333c15a3ab15c3471a9a54e414853afa05f516d05e8a1415a14407680569486fdc4d16605fb15b06e31d5c4ae5845aa51bb2981592524b0c4e47f8958383209cf1b1a62308cb103aa2d23a21a625288ea7d0763c50931782a8c393609b2a5a5b300ebda216276b3fdf46a996e19505ccc356b22e8488763d11be5d68d8a3be37976e0a60be6bfede2761508dcde21bbbee293640633facd2d63466ad10a359becb6b1c70be965f116504894378dd40ce26ce2df35553656b1f31bc9c45b6436c2092f73b94d26b425fb56490663dac55decfe0a1250365e5d87655d76939673101011b6080574a2b90e00f47cded80aa4e29b7299ee0e1917019291abb5a27c17b82dfd5afbbd66eaa79133f6a623e4c5dfe2574eee543018df6304ae4bc5a6940447b0c8c436794b453fd4ef6add23795b4d9ec5090e9cde98ba86be9c51ad180ec679595a37cc164c36717a6898a9b222a647f7dbc2b9a394f0c95aec3a450c17503a164e4830a5d72850175adfd865111b1f6ead5db2a8bd8ba550a94d0ea1040ca0a6d88033da37ab9a156c8dd131e80378d1d1d1e850743e4c0bb0a6625d6bd9d6d1ba8ee338af9e18ea5bda85a753661c6bac217a6c51ef112faa956e2b54144a95674fdc2a7c5464e2194484f5839841b5c0f559d59a465b14a87c1410cd50fe55157608d3f363ed29710376250f2a7f62a47d2db439bdd2a0246916101f29ba74ecf1cf9ffa9d151c8d53453a800f09fefdf2debdf40364b53f4c6f9662ac2d2a6669cd8fa56610d2e5c62a66c650025e7180957f19beac11076d2725ec66065648d8132998a98ae18e11b3741dcb8ed170638914f77cc30b66f5f4d0476ef62f6e5eeac13a8975c921a3b14d9a7abe0620c7a05e969476d7ad2843a8a081f534247b5cafcb243d89272231a7423bb21e6c9af3b18f3e4d71d8c79f2eb7ab394977b751f9ea4f118d34e01470c4f5d50fda73c075de100ec12ad8604740b38e8eedff3196d7df83a3a0cbe7e978e1275312f990cb264aaa2d9c8d2a5dfa278c7bfb896c36456648b4ae7e78d0dc995d1446cbe116dd0fd49028ec093837f051bdb252539f634126b65739c1e89fa9ef8ea1a7548e627962c7ca36aa56f127f8fa4fff2186c45c090d337609b18db99c87a93c2282abaaefcf204c4f66389447781e73ac45a0b08a0f8f20f78eb5197b4882c1fd7065e7c863305bb663c544ef7001716513a3390582672de430ebe98000c697cabc5e5d12d121c2729489c3c6067ca059e941e0abac36e68fed2f5e9e1fe12fa8aa932ac35ae3ba393e1a75a98348b9af81492af3b1e1a911766c362a2227f545ba6f237a59b455947669f56948865f801697433f0cd53134f5904131f1237c006db5b4adb34848b931862534d7b7981719e39dbd29bd171860471dc7ccde960073b2ead9199ccc5f8650dc9e842e9d1c84139956d4a657b13f31d3cbd7c5094221e81ed45c877e0a27eb3b43e9e2d8ef015544d1b8f1ed7d6340ab1c60ad66af5d1c44688ef5fb1f32004999e6c54851fc103f673cd62d267a2a6b3e7291b2020843b93481e32919de862e29751886ae9c648b55c4a346c07ca091f616ecd457131c1273c3bcb184bc9e703189e86fd3f65063d21341d89a5f59b6e7c4bc6c23a622106f26aa39a7d02f1a5df9a01bf8c5866b27d674500955f6b03426c2b1a7af11c7d025f13186b06f0d67e01048b47819865ed173813f37a72c56d04fe59057a5381b4d3404c132207e646983bc05bd05ddd1db5b1900131bff94629d3cc3b83cecbe11b8a6ac58cd951c1b5185c87d6cd35db6bfe80d6e2d26b6cc3e562e2d5296d98733d4922b7180f004dbc9eb6c304d00e5ab256f02b4b1ec32c570981d5777a31d6b7ca344ab981a49e77062b7fa009ee86159f5fc642dbc04d46f1f2ce7c63ad3c19b8ee2bf4c7ec8689b0c4b560a2f1caba3283f580d438b0803fec717bb477d3207a483f7c35e229e322e379dc4031ef0fbf7954c08a65f355820d3b2a6c094bcee8026f947ceb00327f72fcb9952c25b6d54f4be3a1f181fd9a325cd42f420019e6bd4eb6d7546638ba885aad73bc19deb9907d4934077e72227fa9b76828ad73170d5ebc30debcf1f5cea1f9489526d83ecd09e6cf44185a41472ea5719c034157746a788e3c9d24d27c4a7468de6d8e3cdda1b0e0dbf49a2bc6667c0c5e918e56025e624208de212548cc063a7c4c873ee7c6bed713c87d3b3e0aae6aa993f323d25423cbbd6d96ce1b9e6bab56276524a17acca359821502b5ffa4ec157c3172a8e633b02932a350961283a813f340d8093239f700908c1f1f14b9735b488bd8beefcbeb77d5a1d4cea2c4c6da1a9bde87a8c57e8dd7ef6f8bfbfd26b72f20aefaf55325bb7daa47a78be48794159ca7719146459a46190f7931fbd1c759f8ef1102dc8cd24783f1a1b2377ee4ff79e8b21fbc174c01116cc60682243cc271bb94f52689b1f5b30b43e49da170ce049ff5f17ce08f83733abb7ba10e942c6ab5b5777351c25aabdba61b52f897aea9ae958787e137a52f19d109e01196dcecb1204c4e987bb10fd1171a8abc48a0e84c595b5175da7ebcf39bfd072052a02cb982ea5de0106ab011805c571d213c820f0f94125fb234c249cd8d1c567afc66ff611c8a7b55afe3e53f20adfc005c544ac66499927e27f0f7e26239a36195c1be93d81bc41c5b09112c446af901a3dcf48388269894f793fa2ec1819c2ce1e5d09059b6fec42d3df397cc3729c16dc3e21a54669a6b4b1f0b0acaaf66bce2cf6fecb08a4c405a443886c41afd1bdbd9b03cce3d74f0314fca8dcf5744b307385bde32b9fa02fc2da5a3d95686296d2d6f0cc14a2429be5162e8e0c3f0f73eaa998e113125214c62eec2853fe281df800d825896cb6209884a3a1e6110264b2608e173cff146b0382f2ead834165b04986c02118c5ab1e66b5a3a4506dca3da08189a3216d00f8ff0340fe74fa4eeb4bba8b2a208453b1936e52fb33c8b6ee836423d98465cbb06558b20ff1fbd6faf7f880d56854848e70b7aa5aecce7e82470184eaeaeec17def03a10a90fd429b99def78922e322641671abbb7781a10b691b51d4a40047156fcbd75aa1b71a34ec84562d064a3d5ed184fd2f93844d28e8e0185c8ad7a65a5343edadf3b56361cf0b0c629e7426adc8f83818d537f0b47a0a9798673177af8420459990318c6f332993a7ce4a9b823b54577850cb4ebc41bf9977ad7566b2d34b349d8253947fb9c59c2f9f10324c60b5c734f790c2dc46c356599e8262b4f7911c7f4ce6a92e11a6ac8d48254bd196d674a88c5fcd1a2d75a03f4dadfc1e8ca3aba30da45f767cadff1b453753c2f5fc56bdc7c874aa49de7506eed9a389efcee17420d193662d183ebc6dc0da0a2899a8782b50b872bbd993f79ce3b5e1233589736751c2f1ad5729a7be3e2e78f0f596051822abbae9e3abf43d9eb2627b2b88d7bb4efd4e5fcd33e96b1cb2608a85987d77f9f0a062d95a18a31e8e9c5c99889c14cbd4f68436440b5c44187ff0df11aa90b3265bf452501e4fa7ca8e5db7ab73ced6903b3f1dac3cee447ccc08f9fe325a63366c7ce3b6b84c519120d6226791404c9e9a5d8e1613a76740aa043babb5f06668d45c8fe4ba25731d08e76a3c39171f226b84ae55f1ad1282155e84a247104b5bbbac4805395e5320a19744b532b1afe4609cefad008908c30286901dc0d03b807919aacd7c393aceaee3534ec1d46e04afaae2643c008c53e5746790fc943d029411bcf4fde21188077ffec4b2621fb0ed597020386a9e7adac8ff1406942726e519c2e8779a55cae299c62c5307a8c53b3ba650ebafef455bdbc3f2d54929e74b1412676692648a6cf112ac9a30fb32d601464f8827036b135759e61be68c6d2f2612493d22832eedb3cddca114d0d985c329c82d962cd01ee2b790669ff9f5ba029c3d85f0203ec8d75711bd22b4b57b724d1c0aeea6ec63403e960f97dfe2c903e7a64f58429c351e0042b0be3d82f2159923def20862927091642c3f3f5d47d36157264e4a8b250e5723c7db5dbe4cc109ab8df432d85554dbf681fd1e939493ecd412729aec4a1c9f8df1a47dd6e9f8a5e2ffb235e891d65d92dd3d5e17b63ebcd471ee347b249e64e1da1bbe37b1037c7f60ddd63a1b8d233c6bea0e149cffe648c50c63ce583f98fc3cd7f8e19b236d02d53c062ec628334079a6b299d45f70393c57cf16bf3992fbbc224c73c2b30e24f1c3924868cba5047e3d4d7dd5202869a1482e069ac513e7ee8d6d6f4cc8166abebd4b89e9cb6bfeff10b8cb6bb2bd223d71b93de4fb5928dd5e51d088ff065f3263cde774c779aa4298ac64aa52a2dcd9e64092c7581ab338120596ba444d8e30795687a8eb7a8219905a871eb39313c77a68ad7dfe79910e64e00e639b5099480e2da977ea47fd3a59b1ad1813df55a77f1b404e674d52a42200e73281a0857cef992208c95b639b002821ddf6e1c589c19908100721e46e989be58914a6203ec84cc0082fd456365d959b484c021dbd841aab34e06243a18b2e24875f186d8873c7877259d67f8482afbc2846f15191ae923c89d0286320b4d2416c7294b8ccf0808c86508a94cbe046af75a6d86cc849c3d224506a1a2ce90d628ecd8be644e83309a9f8122a6de109a8db14c11fe4cc60948d7ae6a362dee95d5e7682e87d8efae6a40a67ca63e0467edf84060cba049d506558ef4b3050949fa34dec4504113703817ab20820691164b40d3b746965ad94644d80a81c92251614bfd6c49d5c66e3b8bc479bd08c537c21a1d9478bfbfcd4b72fdf95ef9b7366f28e60e69de150629d72d0e39675ea433f870eb4bcd6e79d0534782700234d0da0aa5860e59d17f70ebae5a8e8863c39fc0b01bed63c81ee35105d4955d7c2de38a46d17c62060847428191c88e518964235d957dfdddb4966f6c8da46a729fb7689c4c9ce4d6cc23ac0ce312e9306dd24ef4ce615a86b2f59e09b500587cd49e9816118e149b51993d1dc904a529e153d246ea28b58d2739d33cc2fb3dca7f3fa9a67e81e3130502c1caa908674f91487e728a30ecd2be2464ddd77c8da38bea2e45a6acc5f1d6901366363af40083e14b44a09a85363fa38c1c2dc23fe448d8101f99a5873a9ce0d507a1c2288c13147a7870fc75f317fef78c75959961585f343ff0d45626d43bb38fba4e16dd1ff28d845e489deef3aaaa9b58c87f7d77956887a170a776bb90a8ad4a5d76ed972891415e4d6ed62be192b526e2a686463d5988a249f5dadddf70edfa1769b63fdad1d9ac8508a357bc7e5b5e21db7abb9e361a800c9b6bcd07852481fd2ed908e1e484133e52e240baf8cf3743274605fec31471e2be0e1e411b9ca8cb7e9fd2b156212d224b2e8cc609a9e30b318c6a5b1890f802433978f938323dbaecc5bd26952bc6b2031769ea9646f5af8cc4a152d4ac0c788447ea6288a99ec6a4591251205ea28de897f291c5b67169d63a90382be04f2214592aeaf9a5b5a66b3b820ad3cba372e462f98a30f7243ecf27edf64714e3a3cac87b19c88148b91621a7aec6b98948ba9e7b0003faeb65d734bf6c9fd7557e0b794c70812c76bb8ba3d71e593bd0a8433d943c7e1e34839f31bdb5ae98995b0e18f059397928350ae7a0077fd1464e399b158a6badb5fc1ba9c8d05cb7c55002e30cd65510287a0dff7b82068d96883989eb67cfd7d2e68f974019f6d4cc44dc46d8f32d7b5b5f46e6f8ec5b8d8ce030ec4e636236c9b17987d8711844901d9df3ad9ba6661593b3d2e721e58dbea1cdb402ad966d206224087d1d0f4e86c422958b841148a93374f6697f1b9daeb41287bbcba76d9c93a2f02426476429fb780ae09f8fa8ad24559dc54a6b13c5fe282a225b7dffba46d3b321366cec787e69706c788b0929fb3ccf54f47b94fcb39130e103fb1d41b3e033dbda49ad24030e334d8e275478016375006804101fe3b6a3e1aeed5e6a5ad35697ac0e00bb0c64d988f19056bd7850cb2065b5d84285a38a3c5a7bc7d5b918e33a0b3caa7d408454f6d1827ac6abed80f9034600e5809e69fc36ad79cc073c3cda605f4a4133b5f29359ddc76ce1ba8ae45105448e1b552cec90177069cd703eb8a27d6e2338029a2ba56a0013b540ce69dab826bb619c321efdfe1b8cc21add9a51007ae05903d07a83bfa716d2a71ebe206f1ec3f6e0e6aba08b7534157546912e144bbac699a9f037c62ea1ad56658ed14e6e5148a1c492cd97031064674397da7353c6308cfc2f6f0d53852bf87012081928d287dcdba1de90882e08a27dd8311928e7668c8c383fc169e84018ec8650a97335a46f42f840f85c031e5fb00e5b6686018190536900c5e70ae248fe31d910f9e9322920727f625175aa7427120d75c2422c9fa018ad8dd00a3c5d6288be411c184db10e63dabce8a97d10d7160a16a0ecdcf0ed8b421f6f5ae3c17f6be966b56e9a2c1266d98c1bcd422d447c58d75d76412c06ffb6e1d8e6fa7db29894e3281485102f89a321eefad57adb4b3f605954e51459bd36a82ecd12c6635a48bb2a652f663424f1e0e6ef002fd07a6182de64bd824adf01ad3e46dda734d23be4c3de3cfb8b0be3d8738a3d04a11139ac7ec97f55e6437dd78692b072d14a8fc62d6e9ffff193a8cbe1b8185ef5be2f58d83be563678e8f848658b0d55af6d718c3806b9e83b86892ce8131eb1a88516e33fd3a65a3e28c49d6b7d94c5c3d515f17e18ecc11c78fcb45c858abef02da1e28baa745dae873b4158aba950b2cbde895469d6e39feac59ae08e9de945699947554861b8c6155d2918169da1e49151464fd8c6e5252da8dc122fe648011be280e8bd3a4439eeeffd967209fffffef94792b3e75c8782486fc977f48211a0aee85b9467128bd125086dd46912cb491de37c8a4c5ac5f4a178bcfd8bed2c30a32443ba19c73323a8343c72bb0e182fd462e0dddeadba1008c3b8df57567b8cd5da36005a47372f8d518ef37203a8abbe9efbdd9370cbaa0b78fa7c33a18487566175a022abc77d7bccececb7a17e22bf13c6a87f97b131905249f7ffebb040a11ae13189bcd1c47c3821a0bfa3f7796a425f25506e9a88949cea6c5345ef06e8075a4887ce2813e36fb09bdc401561759daabf63b708c5c5a32f132e27944f7ef8d5f4b62e5f4788df52179dbd4d542bff8987172cf0246fe0cac642603e23215e2f4131629c4bd30331d4eb3651c3f6ccc0f928bade3050572618c7bac7360272e21881db6714792e62ddebc89009d7d3b176f0456bd56317e8eacefeb0cd6cf3cc623734bc123c0f557593a3e328e89abb5bfca17e52272db005327bb8a60e997bfdfc03b0a20db9279a2a9400686d3c5c4153ecac73b1d8f06dd743c375a882358ca1e3ba35d92774c6cedd42d4c0ddd23f11a04a5527300b3b633670d898e094c09d3aa52df085704431aca8b9510f7bc3df3c8b76e7c89967fc3a8001aba33e5d295d05f22a0a0be850aa56833cb3b5f9543c483aaf722558c9e05f394c09fbcec199cc3dd1df3212c24b4b7e21802b9ba8e2c11532da764d88103453e9303a0aa8532fed5f112d065eea7dac0ba2ada14bbb5a7796132b1494edbd6441bb98dc3b79580bb03adc3768e899b95c69d70269f2fc4b01ce09bc5ba9f0ffd3941b865e227eebe5bb870b7ffa103e2a53ba1855db9bb3fc4a308c2c103345e2657c774e92a94aa2b3c320e85f8067e9c75b4808d5cecd574d561e9cf3d3c493b49fc5088208dacc369269f912c9c3510ee085ec2ebc589257bd2fc66585fe79561a3a030c473201839b081cc98e07551430f33721ce53207946685a334b73ec3cf86d752d2b8bd9d8732a363cdcf95ec1e0ec70adc14dccc5bd1153243f37d02c2aabe89e1eddbc74a0610755a5a3df9a3c0210f595e841c48d6b5435ac3b5938e0b86b3e6ac03af27c4615247dcc88fcfd1254dbc7d9d49f8b2864ce46d68cc50e02cb406a4a936b5ac56b98ab2e03df3ba748d8ce7b0cef9c203bdc784c3cf82ba26248afd291b9f2389b2d55c83f3b9d220af61e5fcd8e6e00f8ff0340eed7f43b5d674bcb1302a891dd5bb2f597522638fef082e202a6f97d6bfd2bcc3fb11a35cb2859b7aac5ccecfbb42f04545d5d3d0b0f507c20d42192912ec2469a99d90d91f0f888b9ffbf67dacb92380e61db5948c16026da6a3ca2228d2238f7718615401bead8ab9bbb434fe7f6e4f00c3e3d42f7e3e7fd24e68f07b447e65ba6f1c61e7810dbf9ec58343cb9f3e5d9d46798aaecfd7650399968ee2ebd069a860af5fb6f58bcccc4df78fa4e0ea7f7d180c33179cc98ca1b209eee1be6fa10a4c6b6e7110b6c9d03242f6626a3a0308933774c16b4427010a96607308c428ebdfd4e197b0c4a0b542d705ed1406a295304c3a8965e59bf0c6f41946f98a58436cf1ce46117c8031065ccf94d7522f2dbd66440b3d6356c2be31a02cfb3d89d50029e012554de0ab1eba9d19eae1a2469368828b25427b624fbd48bfd2424ea93231f0dd4d536bff1c2212a31b2dabf3b16f67da7ebcaf8be6629d88db00e0263bc220328ed5816ac6c317e0d01d054ee1918161aa31a4587af146e511f549fc7f896229cda5c7aa8686d9b4acc0cf4d2a0304c99d95c16cf6690c2d630723c20561407b4ebf4037ca87894a6a5a5b58fb4d33cc0f7d3c85ff166ebacd7b54e4c5947cfe7137c342dd776e2597df099ce1ddc7396303e2c6387cb999ab162679e9983531649a4a9ff64b2d4b242eef6b95c10b8050dfd842472ac6f96534d33780ddb5462c649a527c821b15d0a1f2ef07752b6559e848b142cd3772861ba6025ee563a4f31cd36e7a79933cff101fda808d72d910ad3aac8ac02d47c3c940cd1702202a8b8fe7df6dfe821b0e98b69c6aba14b16cd786cbea43bb719e095c7b82d14a28a42a50f3731ef5f213b27df46c6c4181f408547de4558b78aabeaa1e07ef2714514b14ba8b193ad4bf25e4bd6380ce7e23feee48599adbdbd8bc13dd668ac9ec16e65486db7643730917be6c3260dc16ea9a285d8055f53510c858b459516832c226d7badd8d094619d7d3913b8ed6c52146ce9f6ea67b24e966143a87c3d024a3854400d699713cea050f775da4a2bb2b8976a4751baa6746ba2c348d049e2514515f7af156055b1c25a73020d894bbe0a4ba891fad772c78faa5b830cfdfdd4680226b300df81ea35d000bbef31bead059e77816f40579f4093df4e64fe29d82662015bca79a81eefdadf317d5865e4dc82c13e87d255700fccf6e7db6e7bc41300657d232ee68f27e70ebb25cadb69c3cf5c8d0d3eb8d31aa3d15af3f667a4c5b994e0b5b8f43d93147560988c4941dc722a189235401a7b51a29099c69043ae1a9377648e7a4c327664a8f56e09a34091a1119f44614cc29929a52825df9a0025469188481199064d8f3f0eb9e10c388124b9f1bbb3a63e32e76a042916470ea13f4314225c7911f5c90243e73e9a4ce52f9bff0a7c9bfa04ed371b173131486ebf98f19f6622c259ee85638514d077de4e281810cae4300a4db04817f8981653a2c0e1a4f3478c951ed0b6adb2fb386858081a5195bcde5e6a261cce9668ff34c435b18c779da1bfa70af1a644e298c3cdb165986bc92d43204e4ab5fc2add71d45bad33ed9ce76827e91cf511a2265410a94abec5b16b439ce651af50b93a233899ab8581d0af867dad8d9f0ab76df1df0371673c27bd42c77b1ce5ac65f254706487ca9ddd93ba2a82d0eba2d48b2b8e73ec8a6704cf9c0a982ff25530284267acb026ef59c1d0ab633e15ac5996c9df60d52d535b4357271d35358bacdcb19cd6e58a3356f3d5fb5138df7582d059f85a71dfbf19fb064de73379377ffdba5da6fca7fd629c302d04b62d5f29de6a3b785e8a5ddf611f55efef820e2331e9227340a4365cc094d844541501136e44fcb146e3f713bea955cca84867cde4e6d93db43fc4346aa5390f5d9fbac30f2f9ed3c4757d9bd53277b16a1e45e69718eca0f6801bd39b04c2e47eb0e43113f4d2d73ca4ea3f7818ca655f9d39fde2d8a310e027f9f689c20fa6a0a8916329847f94e3bbb9828d588e4b3cf8cd1cf61aa8895a03057ab1e883a977290870ebb3a65f76e507038f8ddc05e0ba7c79e5100902aea59cd04b5481d4a265af63f4ebd6f8daa8f8e8539f7b08eb992ba22cfc2d303ddde3167a76c61458ee977a0cef6b284b80ff2d48b03f4b96142d34e801b1887ad06892cc873a28ac1791568b7f77ecbbcb3dec0c8174edf2882539e93f6162d21455b8cb8776ec5deb83172f0b1cc2c9e1917741f7504f0e8ffce12f6b972a4c10e69292b0f787a6ce51f01a1fb26948216ad86eba07a2ee98390ad357e3ff7b494b645bcdfffd4884d1281fbc1a1d5b391ac5d2d2a88028ed5ad6475c86212d9ac19d4d41e24b2efdc6fe250c79cb68d4525fc505a7439cf4f022d33d870cd62a2605309342f12ef38746d56e38c783353f4ac608ca6dbd8706778c981e282bd0208fbb99e27585f42483879f3ec01a0ff61ebef211d074a6b6a428e2e53f8692e1dfae4d4b33891e00c5b75205d6254c7e78c6d7b4e453ecf70adde2d119c22fe6d7cd5b0fb19208a300d6b5ef29f654fed276de3d5faab8103ad70ba8a2db49c97b211dc37c3bc9a70fda2be86ae5e90951d0fa78a62b631af3bd6c4a2d567eee9bcf3338f46f363c3cb9c6ae9ee7e9f3255631e7da6d7077351b6b64907ffb363176dd3a57efbc3ade9d6d8f76cbf21bf505d521da1e7cd87548c065db51bfd886a3a67cef7e1a4a3c0236dcc4c599e1e6845dbf9e1396fd1cb18ba7dfad53cf48d13b2d2a6d13496dacb1bfc2ea77bbab8ab2d8321a040f0728a7b51382f9c35bcfc3aad6b96f62d0285484985f16ffb538081f9b0d16bf7f00167ac09f0d22a34c78b24cfb8f696c3b1fdc8a52880ee8bcd5cf913f0b186d4ba4fe9b82c54a4e694548df4ef349792cd42a3f830b2ad89708090a0ec3abd30600a7921ac537b7c5a67d1694cfc849cb6de0cd1f424a6a418bed2c4a6862244265cabd48df5b2702102ac57699032ab210e093cff65f1109a6ae4783e0035ca76333c8a44fed9738cca465e8c935a9025ff6acddb2e52e9760bb79f8e7270d630c12f69a0ec4b0e7dc0fce0f3b9779ee5af6828fac1d6d8ef9e0460d7bb7d3d4d129ce24eb223824c269905b076826ff6949999b32f261f138e9a1521de80553a90491b03088e414488269c7e27fb4662a1680b07020a63d6757d809b94fd101e0d06bd4f3b4fcabef5d3b68ec5df2a546f44d284699b42565a7464d002fd09f129073ee279984431b4d4139f86058f0ca478664c26b9649ed3b365e7873520988372cbe649111dac8059f0bf2766c09e7fa2a15efb6e766a376608ac195ff39bf3785b73a97b8ec2b0b394b66411cd756aa2b0d6d2bc72a8dafcf3c23f5cc952985cc1ec31c13dd6f10f689bb1f3b166f3ede0b5816ce93f075522c2717ef69dcd047837c1cb7b2d6b2b7679c656445025b9b503d9abea1e3d2c57e32a7aca132dd04649196ba3247f556259b0177faa1f51e6d703ffcb607e0b184cc39de8f55eb10c3e8922fd112dfc0b88d803da35580b046207f6de0dbce671789a9e935b0e9b9185422f8271215c7ad1311ec817c228071f6c626359816d18cdbea7fd7009d88e13b65ae3a7cce44f4cc2591fa7cbe467c376ca2c455015f4a3098689d59f3a3729433f0b044f3a805f2551cdab2288d4b8fa073703a0848480edbdff7d40d7165d83c6a0d6718017ff3eb85da83440ae5aed7c163913bb152064f761f08cbcfd1f0b5122d890d7f06046bd9d83505919254703f958db13e1efea6c7ae0b0f8cc2e07fad914518fcb416616d97d444b136b07c64bd3cef2b73aed3f84e818ee080f7f263a488614429bb263c8102855e2b5ac654c082215bc43550fdc8225738c1001c54099553869a78a0d78ffbe9082f8f1c52bf24811f978c83c8a4c197ba74f19057f5bc6fc88b29fb92a7c8dbf152d262ec1ea972ed7bc1a83fde01156b369e6d83d1b412d9e9209ce59107f104f156e7a0424a7c22891c6298891e0457418c4c08578252871f0517fc048f1d834b630ac33fdf43b555763fe6d4e606f46312fbf64beb228324f4e90a40a74c3728cb57ff3c0cf9cec185fad3bdb212fa713f8a3139f9081ec4538d9aa86a041019d351f5f5e694ad09748ab719602d09c93f999f34567dde25dcfa3f0931292b6efab0313d73715da6e4b331924b10e4092bce5279e801e20d15c587ca84526b4a2d5a592d03c6f6f377b4ce408a12820d51d7c115f14438272988124fce0c3021cc055472568f0e2d85893388a70a27989b5974f9f37b946af80b9b0a087e57c5c70b7c3cfc481b3c2fe6742076fbad894a6d451dbab34df2706671a17e4540346da4ebff55f86ecad8ce553a167fd00fe38f12d5f66993e2da15df43336c88780cc3dd10f26a92c1aa38b9c788ca331bf7d3909c3c79c856634c6a9729b0a6412f6b0a6fc0f0ea283151b098765d63718b5209a997feae3d04fb5ad1f70b1fb76d8f694116c7adadbbee1bdac5e0a3851de692571c6d383fd18cad1457b587759f0cce7d2431eda53474d50470d534dc063cb50f125e6f638beafe291045bde01d6b8c600236b227852bb9b28a28a2ccb3afc2e1e51a6f4d4c28ce13b2402f3459e7f5d221f880706c0b1284d591c437ae2d012a0098fedc171e787f8d054285ac1389f323202317833b1372dec4c0ea70f03849c758d5049e4cda0ebcb26fac86e8a1c100f32ddc1715b73c718b2169a9405acd06c84103f246b79ee202c764f5472f073734cb04a51f55e6f519ff1645e7e6c30064f997b1cbc10d6c1d7830dc25b91078127a8d38dc5878d2c3b16dda39a149846277f074be476cf0731f7cfcfcd717fa15c055370d27191993f92ec5009071088a6b0c59ebe0d162d4dca9c8ab4cc1f002ab8335844e195e68d8e9c183204328f21847ba9a59974210bb9954669c8f5cf02cb0138e0f40d9cef9ae6bc4c8fcf1bbfc109cd715dad6e86a0eaa43919ab6b1730fe89bcb6ee74e60e4ee2eef461a340cab995a54df6ebf959d8fb6d6c84b1580c60e6ec1006b7f2751e86cbbbc638f07d5c8fb35c40a8fc00be4e3464909825344409c54fe212185db208a0f655119bc9660a6f71bf4eb45e1a111745191a61e43ae798939f029caf784115d83cc56b7e4e339c366c1bb84d2dbdf42648c152db331b5f17c9c97448d53140a8cf6dd4ab7e5b1ebb6e8adb60bda0f0f40c556e62d70966f6e9410208a83fb42206178a3140a86e5fcd1fcdc24e7dd52616adf8d4d9d2b6555cca36beb9cd680ec14f309abe6d92ecfbefee3e1e2fcc7e597317f2c389fc897bbc287d434469bd46066f20340c8b25a12e9ff1ed04e3ec5d9a70df8ff0340eefd69f6f53b49aee07d48684433d749a966e394eaf5bb15201b9d41c30961c2e15aa12f590f2c2ca091031159107b25e0c9248b8f705f209684b2ce9685ae9397bdff322b12cad4c648ebbe97aca65cec0823c26ec2bc44c672b9a2af8878b2a6929bd01e96801a7aaba2480b9d1ca831115d2968c89ff9eac086d06bfc1d50848e06abf6a485dcc5d55f4ee650290f000503a51df855ea0f9a3537604f1c8460befb686b68746d18e9c34c8dea75eb219511e404b82c81b1a668af7abca467e107f66bb18ea847ebca02b694a5f8de2af4ef241ae7e923603fadf33be047d59ae9edc1510f4cdbbc05b8f18437175df495879ad464ff59e9a82b5dd9166f879cf1b2705a1dd2aeb9e825360bcaf7c960a37df2798aff4cc89f5340c17b3fc2705bbc2f9546a2536f837ddeb7a399cc6fa94adbfd5090ab4383e423b0552b1af6100882eb6df4bd8b13945bfd070b3d68a9a6deaf2e83e14f4db0fb0e26ace907c38188a238f8f495e6c6d3bebf8db004d4bf48ef37dce0787a21d7a36770e5df8244877d1164dfb1ee147713ca9b8b41a648a45b55105184677d1e19ddd5eac7db99e2efb759acdb24324cd804901d7e5144585f6b2f9a1be346beb647198621c0c921384daf0df62a4c77132c381ff6ff47476a89e9de0e395b10cf006417005379ed8d6acae7dedca8817b7b906a764379bc871a7bb7faa17a12057b8198ea25631563818a12b3593be60dc9442605d02c4af3bd8680fa563bb88a4c1a60509a562271e4ffb54e4688611a4402a01994c444463d81de991d5a6a3a16d8ba960010df042e36cec05d114f5fea90d53984d0fb67f9418f2085c4246b135bbfedbbef61ab188c8622fa0b2d1d6dec8b141baef549e63b65f31fdeb25881617ade2dcd13c362daa8e2b8cb63f674ff9655b6ab02cbb644f29dc0c14e421f9ac6990a555d4ca57a3cacf75458c1201b5092ebe07444d06840c46557f96f6e748f4745cf60b7abf87275e760c235934ccdc9c1a5c099cd07daeef6d92174c2bf748ce95749a1013242f34dbe84264ee0c23e45f1f58a8a123265d8a9a07c52b1ef4f422f8a2f5cbe825ed9766ad4a843742886a9195264cfab010a19ec819764392e72b5109e613d823ab1024d480519784a24acae4b004eec3381413cde52925b21330e532b19a23ee1418e09a66f5fae55c05030d30f219c7ac3c2c9163f136ca36b437ae2b4202c1116d784a6467fa9ab4cb828302b244a0486b57b447df2605aa6e24296ea9305e159e35c433687e89e9d65f540c75e001386ba5cfc5fe1c77cc7d0fcdf1ca02266db7b70720ae9f719d78474a86ef302365b723e1d00309006b140a48cdb7b2bebf0939fce59a87d7bf6477dc9402d0b97e104aee32158ba12a742b71605e45bcbd49b35fa9bae16c9cef50b8de73e653b60bfc864ce8369ec87d34cd5a7f215229a81169cf3204cc46abe655590b2dffcfdb0ae4630d2d0090e0a366de26f36345f06bf4eb034b103a04ba911c39c2c00a61f7a5a5baf76fe02e0c5100b559aaeda1c0c1f50e93586c9e6582f816093b1ce69dca7897451329f64f153cf16bfdd31ded8f563f23140d99a8177371810339038f7f881afa636387b4070e218b0386b189110ed90bb6a07876a2ffc859966b95bf19f6c5a12ce358a0d2490099e320c521d4622071084ee72410ee9e0ee61b93df7ee212e4b98312e41b007b46ef04675e448e20bdc3808f121a3b1c9eda22117e02bfc4a604a32c2809b8599878ae306b6c6ec26f11021e22f8966abcbb8060ee8040ab6fe498e0db9150d5833f3fff5963c346e6e8ceab399cfbd50a0c5b8fa4d4b120112fb444adaa1528444ecbcec9e1021d3e14ef365689c4bb0ba3d05acdf749d947b40a78c90fad78de565fcf2a6c175022b1d38ca5888101ce8f33568d4cc77da2bf81350dbd7fe29b7041fac8960bb2fc66a867b2d0f6118d533f2b031f1a543bef4631786e82f2c2373d0a416b70fd5c9454a889999542e6e6bb890a8095a7f702c95a571364ade32f987f30e956f0f8f5ba1b23ed6295f8523234c244d0a2f5dc3e155a525dd6e22d55b4d109cb46ed989dca25cbbb1c7b848e2590ace9f7f8f0ae80449de5f83041df4783109ad588f8ed025c80f1f875ecba50f26dd478ffa209a1f318c95d69c1f38e3adfe2156d1f320e5a39f64c9915cf4294ae4b4cddbe3a1307565a25364f38264be375c8842eb7b69a961132396fc2aaf2eca99a7fa9a8c4ff9645b234c0fd5610aac31e516765cfcfd3e8467eff78d12cc36540597268f14695a62b140c8f73a9ec2770677027dc3ba0822a1a00c97903916c10cd0e775d463bd3e4bbacac95bc3458694e0184743ae0690841801544cb01b8779f4daa055a6672b6e8f364bd92a3dcbd2cda5253c6a95e276d892ce9cf743c13563616b3412d6ea9c67b12bdc082a48f2b8ccf3536bedd5d2aff2df784179dbdc554cc01ca14a334eaf9de68793b0820ace483ec63337ba2c25d174f8a952d32f53d4168f0454cf54e391f9ba421c0ba59fa771fcb4121c53d516aca2c9af0e0a7f7f43d8e194b248ff14f302f633dc7a5614294bddb49c4e74ca6e5741f2a32145da1dd8357ea19e3b441270482eb609b9317982e7a1282f3896c313cbe12ebe2e66049eb56bfb4cb95a8d6dcb87de1220ada7ffa7e9943b53dbeebd2475b630bb7b791ceb8614941d909ae572cbb34cdc4d1672978d8087ee0da16dada3181cbc4776f6c54c2495f8ba2ed59c51c6d161830dbf7d0ff1c9b6a6480369370d0fc15b755aa7fb71f5afa0e52db815302bb73975b9f8ba59fe1e324c92972a78ec6f2c4bb37152c238437d19239cd0f1f10b69bafde9495176b6d8f9a9f8d8459ec6716506c98f4fd822fa78e4ea8ae5603fe1ffbbc13af943b38ec764317d0b39036e4849f9640994945d085b513bcaea454c64e3dff33d00a518f4311904189f0ee9f1587e674f7e001039adf17030fd3692d83513be3fd71335aeb5728ae58f75c90aa8d7c845b92efe5c35fa6512e1373b8ae5b5c1a408a27299c23bbb23612cf36df57a206b7aa3a272eaba9d19d4fbbd7fdd15f9e3e5b97e3c737f51564728057baad689d1897da09606b6a3f765806d6a7ab5f0051719cd382a06e6f323e73c7a96d7ecff9e9ceaae450e6ef15411e83ecdc8c3fedaa011934d0af035c27cdaee67a4664f3db27696c553cc1e9ebe9b412c988805fb1cf78de685f20244d5bc5e120ad6300a0520462025c37806dc2428b9c9c94d85a1e951fee19cdd30b2905042aa3c0c05f7f059018064fc4085a3f8483691809a04c06b4d16cb9db2ed2ecf9e633c1c4abb743ae51844ca449d069b50d3dfc453dfdee73ab3eee18c88c134973ce85c70b8e73c062d7fb043fb0af5c580afff25531408f108f2b49c4f5d110f50dbd096bed3c32d79786ad44076974a81ebfec9967c7be1d15c197e1ad7e86861963cfb9ab0e15b7577a59e8f660787969ae9180485fcf56accd1ec8d3dcfa749de8f49e4d33dcbddeb4c2113690c79f3420702207a1fb64c6447ff6c0125268705de78cad0b15b7cd7da3128b349bf6aed18f7fa3b1bd59d8b1bae5fec0616db8245d1dfa46028efb5ff10351279e3f49fdae5f853ef1716cbf32fb362ecc9dd4501ccda83d69bcd9cdcfd1100ebae006b340283169af82ea895bb5ee1bd0803f9e566877903982f98599641a89f8241500f8994e0ab116eaa7a8b12792430adc41bfc64ab07bac1dded2a3626e75ce102eea1c4c3440862349d1096375aa0c0e85a89e6f482beb9077a1e01a69b71009d688f85f730bc93aa501990eb39fc1844970504f38558d4e6618f7f547d2ba654e55748c92eff6e26de7feacca89ab048ffac0f14214a4d8333dc078f218a8c8d044f3e9f1751d5d5f79a15859452a5fb026fc293b21c9de9e1f30855dd376b7940ec1f8822bbc131bb24c7a34b0b019518e5db5899bb04f37bf8478c59bcfd408fd7589c3c5fe23fb2edec6a63a34ed78db15eaecf2a30c8cd98e492d6302b8fd1176275b059b14d63b614ea5c2eb30ae874491f08da3697d840c37ee68a88a701325ec89fecb09bd99a9c491eee6c58cf7aa93b4c26902fcef854c79db55fb32b69983c50fbfe3fb4891066bb8bd2af3ed2977213907607dc8d70c778af59916ed3dd0899b89c5d84ddf20e72224ef105c626b76355c78e98645ce964245f5afb63955ba9e5bab08df3fb7e085c623c3e76a39fd87a95765fe9c4d66bb6fc8aafa376eb07081996d89489c8c88a87dc235e891aa7bb90f244525fb94515dc184a90a36930bbb67a5ad329bf49876e1aacbafada0bacca2a60f63329a4daef297e65234b2d124d5d55544b5f735ccf83ea0d5965a261f5ddbf17c3d0cb2cb27d88bd2e5f5143d21c76ec6f50c5b92e603cce3c34ce50adb63729a1ee4fbd00306e6c223d89615c1747cf53adbea34afde0309fc557efebd9392ca1d4157f0088721ecb172dda3256e5e41501dd282b945ddeab2960328802dcf0baa015d98893ac9ff070217aaab7e1a474e1168a522d9c683578f4f0a307bbda1fbe747956bf8214aa7a07d2c5d4cfdc363e2e4caf4757fe019aa76fb545525ff6a6ec92f82b99e4c59f20efc084c532210e1208553601c2422e4e61b1bcc8fa63b78d6d02bc47384eee7e01a807c7253c2eea1962c7236cdd3cf6bae5a4a96d5432654136b4934e122778da76062cab5cb7a4d23425047686c07b1fcce80532bba41a36ca37a65c3c7afff439040a344429a1438be2aad9c0f0f903a0ea4ca0028b6d8dbe3b3580ef6424bab727db761d77b38007988918937c40b0b2feaf6d8cc926572823c20278243f9e2e5ab4ba6a2c22b60a71d46ff0e5c31c08ce63602a9dc17051c4a4d0d62a78c30eee92de8013d30b9b105ac2cc131d779003b085c83352dc49647ff53c569a0d5da059341f52b8008fe7d4d94fd3491c2acd8acd12182c98044b48eff3e84c09d000ef26f096c0b18983a9b11d4c6d5d27e51978060140ee9df395bde77725b043b458081528c9e083dfff7eadd7556a993b33b836f1f4fb9e798288249616a9911cbe25c442a3045673a6ee79baef00e39001ebbb3afdfd713cca01c2695f70a6c12184bde3b268679bb0da07b1e5c7ae433943f8f3f09492840aa183daa3e3920e734677a9b26a0614b5f19bdb61b8ff22c975930f24ef66dd1178b05dc1e8cc2ced28e635455e07dbe96d51822abf392e0bd761583ff672fab79d354a5326137b7903a96b9415193aac9cf57d264d7ae64c763bbc6c67e7236e1d5cbce715ceb533d9ede0eac0a2fe4814ab921ef8b1cb288984f05095c333258526b5852ce92771ebe0d87653be92e654e2950d50191cd058f17f350272cacd5341e5c1259e96b2101d09367c3b4e76c09edcd6c9a79df42e6eeefc6add0d30f93987da487fe420334f67604f53007dbcc0010d2f59466524a9e43d5df6312baed680741736f03a781982d221c6763760dc21d387d8668465a6dc3c2e64055cdff8eaf47706f8ecb66aedae204c18a2fffd9428c0e68b2b2b054bb9b2d7f63e3791d346664d1b401acff54e1ed6696e0f0a1d3bfcd332de6a3382d012376384cd51b703aae4f91d5149f99a560219aea47073421a3856582e6c49b46a7120cc39dfbe0727a26952fb9d3c1f2a2af546259009852d8f93089d7724480fd628df1f98827f8b290d018217b0fb620f9a644b6429d34c25585cd3601986716e0478e5711c19fa9bac58e27436ca33c0d25b5db999be39e38ed501308e012a182ce6d3b1cdb85f065fda59fbdf3a1cafbd3c8870afefb8bafde36e2199db201e22a10810ae3d2233abb755cf1d6f83de91d566bcb888ad3517948a4c57ca796c2cb9a587412a5372e3613d016620d7066b2d0a25a12e0afdec5c092f537d0aa173185716d456efdafda5c88eedd1f4362cd1c76cda67c61a9237aebd67b7d5fb75b17b7551cd19bbf78f58aeedb9d0d4fba7d606aa2959fccdee8693d2c57a34b2638713189b11dff34e050786a20370de70f95f62ad1b02b6d7c8b69af06feb7a4e4561dea266a777cb7d29cf7949f99779a512f1891558941543abc07ac36d3ffe685447e8237f37940ebf47dd52ddf65fa5ac096a1d7ccec82a796a32b8f4278a2d127bce0141a00f603051271c3567a8f880878d0452cdeabc5f4fff3648e2f439208d0e17f3424a2d2a21680fbe8792f8c3810414c3b77f0303" ], "rawHeaders": { "access-control-allow-origin": "*", "cdn-cache-control": "max-age=300", - "cf-ray": "9358ea26bac719db-LAX", + "cf-ray": "93ed4992bd5b0906-LAX", "connection": "close", "content-encoding": "br", "content-type": "application/json", - "date": "Thu, 24 Apr 2025 22:07:38 GMT", + "date": "Mon, 12 May 2025 22:17:39 GMT", "server": "cloudflare", "transfer-encoding": "chunked", "vary": "Accept-Encoding" diff --git a/src/api/providers/fetchers/__tests__/openrouter.test.ts b/src/api/providers/fetchers/__tests__/openrouter.test.ts index 4874575b3f2..4be52c69cd0 100644 --- a/src/api/providers/fetchers/__tests__/openrouter.test.ts +++ b/src/api/providers/fetchers/__tests__/openrouter.test.ts @@ -6,14 +6,15 @@ import { back as nockBack } from "nock" import { PROMPT_CACHING_MODELS } from "../../../../shared/api" -import { getOpenRouterModels } from "../openrouter" +import { getOpenRouterModelEndpoints, getOpenRouterModels } from "../openrouter" nockBack.fixtures = path.join(__dirname, "fixtures") nockBack.setMode("lockdown") -describe("OpenRouter API", () => { +describe.skip("OpenRouter API", () => { describe("getOpenRouterModels", () => { - it.skip("fetches models and validates schema", async () => { + // This flakes in CI (probably related to Nock). Need to figure out why. + it("fetches models and validates schema", async () => { const { nockDone } = await nockBack("openrouter-models.json") const models = await getOpenRouterModels() @@ -66,12 +67,12 @@ describe("OpenRouter API", () => { supportsComputerUse: true, }) - expect( - Object.entries(models) - .filter(([id, _]) => id.startsWith("anthropic/claude-3")) - .map(([id, model]) => ({ id, maxTokens: model.maxTokens })) - .sort(({ id: a }, { id: b }) => a.localeCompare(b)), - ).toEqual([ + const anthropicModels = Object.entries(models) + .filter(([id, _]) => id.startsWith("anthropic/claude-3")) + .map(([id, model]) => ({ id, maxTokens: model.maxTokens })) + .sort(({ id: a }, { id: b }) => a.localeCompare(b)) + + expect(anthropicModels).toEqual([ { id: "anthropic/claude-3-haiku", maxTokens: 4096 }, { id: "anthropic/claude-3-haiku:beta", maxTokens: 4096 }, { id: "anthropic/claude-3-opus", maxTokens: 4096 }, @@ -94,4 +95,40 @@ describe("OpenRouter API", () => { nockDone() }) }) + + describe("getOpenRouterModelEndpoints", () => { + it("fetches model endpoints and validates schema", async () => { + const { nockDone } = await nockBack("openrouter-model-endpoints.json") + const endpoints = await getOpenRouterModelEndpoints("google/gemini-2.5-pro-preview") + + expect(endpoints).toEqual({ + Google: { + maxTokens: 0, + contextWindow: 1048576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 1.25, + outputPrice: 10, + cacheWritesPrice: 1.625, + cacheReadsPrice: 0.31, + description: undefined, + thinking: false, + }, + "Google AI Studio": { + maxTokens: 0, + contextWindow: 1048576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 1.25, + outputPrice: 10, + cacheWritesPrice: 1.625, + cacheReadsPrice: 0.31, + description: undefined, + thinking: false, + }, + }) + + nockDone() + }) + }) }) diff --git a/src/api/providers/fetchers/litellm.ts b/src/api/providers/fetchers/litellm.ts new file mode 100644 index 00000000000..ac143b8acb3 --- /dev/null +++ b/src/api/providers/fetchers/litellm.ts @@ -0,0 +1,58 @@ +import axios from "axios" +import { COMPUTER_USE_MODELS, ModelRecord } from "../../../shared/api" + +/** + * Fetches available models from a LiteLLM server + * + * @param apiKey The API key for the LiteLLM server + * @param baseUrl The base URL of the LiteLLM server + * @returns A promise that resolves to a record of model IDs to model info + */ +export async function getLiteLLMModels(apiKey: string, baseUrl: string): Promise { + try { + const headers: Record = { + "Content-Type": "application/json", + } + + if (apiKey) { + headers["Authorization"] = `Bearer ${apiKey}` + } + + const response = await axios.get(`${baseUrl}/v1/model/info`, { headers }) + const models: ModelRecord = {} + + const computerModels = Array.from(COMPUTER_USE_MODELS) + + // Process the model info from the response + if (response.data && response.data.data && Array.isArray(response.data.data)) { + for (const model of response.data.data) { + const modelName = model.model_name + const modelInfo = model.model_info + const litellmModelName = model?.litellm_params?.model as string | undefined + + if (!modelName || !modelInfo || !litellmModelName) continue + + models[modelName] = { + maxTokens: modelInfo.max_tokens || 8192, + contextWindow: modelInfo.max_input_tokens || 200000, + supportsImages: Boolean(modelInfo.supports_vision), + // litellm_params.model may have a prefix like openrouter/ + supportsComputerUse: computerModels.some((computer_model) => + litellmModelName.endsWith(computer_model), + ), + supportsPromptCache: Boolean(modelInfo.supports_prompt_caching), + inputPrice: modelInfo.input_cost_per_token ? modelInfo.input_cost_per_token * 1000000 : undefined, + outputPrice: modelInfo.output_cost_per_token + ? modelInfo.output_cost_per_token * 1000000 + : undefined, + description: `${modelName} via LiteLLM proxy`, + } + } + } + + return models + } catch (error) { + console.error("Error fetching LiteLLM models:", error) + return {} + } +} diff --git a/src/api/providers/fetchers/cache.ts b/src/api/providers/fetchers/modelCache.ts similarity index 71% rename from src/api/providers/fetchers/cache.ts rename to src/api/providers/fetchers/modelCache.ts index ab6dcce0210..9ab4b851fc5 100644 --- a/src/api/providers/fetchers/cache.ts +++ b/src/api/providers/fetchers/modelCache.ts @@ -12,6 +12,7 @@ import { getOpenRouterModels } from "./openrouter" import { getRequestyModels } from "./requesty" import { getGlamaModels } from "./glama" import { getUnboundModels } from "./unbound" +import { getLiteLLMModels } from "./litellm" const memoryCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 }) @@ -36,9 +37,15 @@ async function readModels(router: RouterName): Promise * 2. File cache - This is a file-based cache that is used to store models for a longer period of time. * * @param router - The router to fetch models from. + * @param apiKey - Optional API key for the provider. + * @param baseUrl - Optional base URL for the provider (currently used only for LiteLLM). * @returns The models from the cache or the fetched models. */ -export const getModels = async (router: RouterName): Promise => { +export const getModels = async ( + router: RouterName, + apiKey: string | undefined = undefined, + baseUrl: string | undefined = undefined, +): Promise => { let models = memoryCache.get(router) if (models) { @@ -51,7 +58,8 @@ export const getModels = async (router: RouterName): Promise => { models = await getOpenRouterModels() break case "requesty": - models = await getRequestyModels() + // Requesty models endpoint requires an API key for per-user custom policies + models = await getRequestyModels(apiKey) break case "glama": models = await getGlamaModels() @@ -59,6 +67,13 @@ export const getModels = async (router: RouterName): Promise => { case "unbound": models = await getUnboundModels() break + case "litellm": + if (apiKey && baseUrl) { + models = await getLiteLLMModels(apiKey, baseUrl) + } else { + models = {} + } + break } if (Object.keys(models).length > 0) { @@ -68,7 +83,9 @@ export const getModels = async (router: RouterName): Promise => { try { await writeModels(router, models) // console.log(`[getModels] wrote ${router} models to file cache`) - } catch (error) {} + } catch (error) { + console.error(`[getModels] error writing ${router} models to file cache`, error) + } return models } @@ -76,7 +93,17 @@ export const getModels = async (router: RouterName): Promise => { try { models = await readModels(router) // console.log(`[getModels] read ${router} models from file cache`) - } catch (error) {} + } catch (error) { + console.error(`[getModels] error reading ${router} models from file cache`, error) + } return models ?? {} } + +/** + * Flush models memory cache for a specific router + * @param router - The router to flush models for. + */ +export const flushModels = async (router: RouterName) => { + memoryCache.del(router) +} diff --git a/src/api/providers/fetchers/modelEndpointCache.ts b/src/api/providers/fetchers/modelEndpointCache.ts new file mode 100644 index 00000000000..23c486e4204 --- /dev/null +++ b/src/api/providers/fetchers/modelEndpointCache.ts @@ -0,0 +1,82 @@ +import * as path from "path" +import fs from "fs/promises" + +import NodeCache from "node-cache" +import sanitize from "sanitize-filename" + +import { ContextProxy } from "../../../core/config/ContextProxy" +import { getCacheDirectoryPath } from "../../../shared/storagePathManager" +import { RouterName, ModelRecord } from "../../../shared/api" +import { fileExistsAtPath } from "../../../utils/fs" + +import { getOpenRouterModelEndpoints } from "./openrouter" + +const memoryCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 }) + +const getCacheKey = (router: RouterName, modelId: string) => sanitize(`${router}_${modelId}`) + +async function writeModelEndpoints(key: string, data: ModelRecord) { + const filename = `${key}_endpoints.json` + const cacheDir = await getCacheDirectoryPath(ContextProxy.instance.globalStorageUri.fsPath) + await fs.writeFile(path.join(cacheDir, filename), JSON.stringify(data, null, 2)) +} + +async function readModelEndpoints(key: string): Promise { + const filename = `${key}_endpoints.json` + const cacheDir = await getCacheDirectoryPath(ContextProxy.instance.globalStorageUri.fsPath) + const filePath = path.join(cacheDir, filename) + const exists = await fileExistsAtPath(filePath) + return exists ? JSON.parse(await fs.readFile(filePath, "utf8")) : undefined +} + +export const getModelEndpoints = async ({ + router, + modelId, + endpoint, +}: { + router: RouterName + modelId?: string + endpoint?: string +}): Promise => { + // OpenRouter is the only provider that supports model endpoints, but you + // can see how we'd extend this to other providers in the future. + if (router !== "openrouter" || !modelId || !endpoint) { + return {} + } + + const key = getCacheKey(router, modelId) + let modelProviders = memoryCache.get(key) + + if (modelProviders) { + // console.log(`[getModelProviders] NodeCache hit for ${key} -> ${Object.keys(modelProviders).length}`) + return modelProviders + } + + modelProviders = await getOpenRouterModelEndpoints(modelId) + + if (Object.keys(modelProviders).length > 0) { + // console.log(`[getModelProviders] API fetch for ${key} -> ${Object.keys(modelProviders).length}`) + memoryCache.set(key, modelProviders) + + try { + await writeModelEndpoints(key, modelProviders) + // console.log(`[getModelProviders] wrote ${key} endpoints to file cache`) + } catch (error) { + console.error(`[getModelProviders] error writing ${key} endpoints to file cache`, error) + } + + return modelProviders + } + + try { + modelProviders = await readModelEndpoints(router) + // console.log(`[getModelProviders] read ${key} endpoints from file cache`) + } catch (error) { + console.error(`[getModelProviders] error reading ${key} endpoints from file cache`, error) + } + + return modelProviders ?? {} +} + +export const flushModelProviders = async (router: RouterName, modelId: string) => + memoryCache.del(getCacheKey(router, modelId)) diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index db0ac5a0cab..f8e605aa08f 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -1,51 +1,87 @@ import axios from "axios" import { z } from "zod" -import { - ApiHandlerOptions, - ModelInfo, - anthropicModels, - COMPUTER_USE_MODELS, - OPTIONAL_PROMPT_CACHING_MODELS, -} from "../../../shared/api" +import { ApiHandlerOptions, ModelInfo, anthropicModels, COMPUTER_USE_MODELS } from "../../../shared/api" import { parseApiPrice } from "../../../utils/cost" -// https://openrouter.ai/api/v1/models -export const openRouterModelSchema = z.object({ - id: z.string(), +/** + * OpenRouterBaseModel + */ + +const openRouterArchitectureSchema = z.object({ + modality: z.string().nullish(), + tokenizer: z.string().nullish(), +}) + +const openRouterPricingSchema = z.object({ + prompt: z.string().nullish(), + completion: z.string().nullish(), + input_cache_write: z.string().nullish(), + input_cache_read: z.string().nullish(), +}) + +const modelRouterBaseModelSchema = z.object({ name: z.string(), description: z.string().optional(), context_length: z.number(), max_completion_tokens: z.number().nullish(), - architecture: z - .object({ - modality: z.string().nullish(), - tokenizer: z.string().nullish(), - }) - .optional(), - pricing: z - .object({ - prompt: z.string().nullish(), - completion: z.string().nullish(), - input_cache_write: z.string().nullish(), - input_cache_read: z.string().nullish(), - }) - .optional(), - top_provider: z - .object({ - max_completion_tokens: z.number().nullish(), - }) - .optional(), + pricing: openRouterPricingSchema.optional(), +}) + +export type OpenRouterBaseModel = z.infer + +/** + * OpenRouterModel + */ + +export const openRouterModelSchema = modelRouterBaseModelSchema.extend({ + id: z.string(), + architecture: openRouterArchitectureSchema.optional(), + top_provider: z.object({ max_completion_tokens: z.number().nullish() }).optional(), }) export type OpenRouterModel = z.infer +/** + * OpenRouterModelEndpoint + */ + +export const openRouterModelEndpointSchema = modelRouterBaseModelSchema.extend({ + provider_name: z.string(), +}) + +export type OpenRouterModelEndpoint = z.infer + +/** + * OpenRouterModelsResponse + */ + const openRouterModelsResponseSchema = z.object({ data: z.array(openRouterModelSchema), }) type OpenRouterModelsResponse = z.infer +/** + * OpenRouterModelEndpointsResponse + */ + +const openRouterModelEndpointsResponseSchema = z.object({ + data: z.object({ + id: z.string(), + name: z.string(), + description: z.string().optional(), + architecture: openRouterArchitectureSchema.optional(), + endpoints: z.array(openRouterModelEndpointSchema), + }), +}) + +type OpenRouterModelEndpointsResponse = z.infer + +/** + * getOpenRouterModels + */ + export async function getOpenRouterModels(options?: ApiHandlerOptions): Promise> { const models: Record = {} const baseURL = options?.openRouterBaseUrl || "https://openrouter.ai/api/v1" @@ -53,59 +89,21 @@ export async function getOpenRouterModels(options?: ApiHandlerOptions): Promise< try { const response = await axios.get(`${baseURL}/models`) const result = openRouterModelsResponseSchema.safeParse(response.data) - const rawModels = result.success ? result.data.data : response.data.data + const data = result.success ? result.data.data : response.data.data if (!result.success) { console.error("OpenRouter models response is invalid", result.error.format()) } - for (const rawModel of rawModels) { - const cacheWritesPrice = rawModel.pricing?.input_cache_write - ? parseApiPrice(rawModel.pricing?.input_cache_write) - : undefined - - const cacheReadsPrice = rawModel.pricing?.input_cache_read - ? parseApiPrice(rawModel.pricing?.input_cache_read) - : undefined - - const supportsPromptCache = - typeof cacheWritesPrice !== "undefined" && typeof cacheReadsPrice !== "undefined" - - const modelInfo: ModelInfo = { - maxTokens: rawModel.top_provider?.max_completion_tokens, - contextWindow: rawModel.context_length, - supportsImages: rawModel.architecture?.modality?.includes("image"), - supportsPromptCache, - inputPrice: parseApiPrice(rawModel.pricing?.prompt), - outputPrice: parseApiPrice(rawModel.pricing?.completion), - cacheWritesPrice, - cacheReadsPrice, - description: rawModel.description, - thinking: rawModel.id === "anthropic/claude-3.7-sonnet:thinking", - } - - // The OpenRouter model definition doesn't give us any hints about - // computer use, so we need to set that manually. - if (COMPUTER_USE_MODELS.has(rawModel.id)) { - modelInfo.supportsComputerUse = true - } - - // We want to treat prompt caching as "experimental" for these models. - if (OPTIONAL_PROMPT_CACHING_MODELS.has(rawModel.id)) { - modelInfo.isPromptCacheOptional = true - } - - // Claude 3.7 Sonnet is a "hybrid" thinking model, and the `maxTokens` - // values can be configured. For the non-thinking variant we want to - // use 8k. The `thinking` variant can be run in 64k and 128k modes, - // and we want to use 128k. - if (rawModel.id.startsWith("anthropic/claude-3.7-sonnet")) { - modelInfo.maxTokens = rawModel.id.includes("thinking") - ? anthropicModels["claude-3-7-sonnet-20250219:thinking"].maxTokens - : anthropicModels["claude-3-7-sonnet-20250219"].maxTokens - } - - models[rawModel.id] = modelInfo + for (const model of data) { + const { id, architecture, top_provider } = model + + models[id] = parseOpenRouterModel({ + id, + model, + modality: architecture?.modality, + maxTokens: id.startsWith("anthropic/") ? top_provider?.max_completion_tokens : 0, + }) } } catch (error) { console.error( @@ -115,3 +113,97 @@ export async function getOpenRouterModels(options?: ApiHandlerOptions): Promise< return models } + +/** + * getOpenRouterModelEndpoints + */ + +export async function getOpenRouterModelEndpoints( + modelId: string, + options?: ApiHandlerOptions, +): Promise> { + const models: Record = {} + const baseURL = options?.openRouterBaseUrl || "https://openrouter.ai/api/v1" + + try { + const response = await axios.get(`${baseURL}/models/${modelId}/endpoints`) + const result = openRouterModelEndpointsResponseSchema.safeParse(response.data) + const data = result.success ? result.data.data : response.data.data + + if (!result.success) { + console.error("OpenRouter model endpoints response is invalid", result.error.format()) + } + + const { id, architecture, endpoints } = data + + for (const endpoint of endpoints) { + models[endpoint.provider_name] = parseOpenRouterModel({ + id, + model: endpoint, + modality: architecture?.modality, + maxTokens: id.startsWith("anthropic/") ? endpoint.max_completion_tokens : 0, + }) + } + } catch (error) { + console.error( + `Error fetching OpenRouter model endpoints: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, + ) + } + + return models +} + +/** + * parseOpenRouterModel + */ + +export const parseOpenRouterModel = ({ + id, + model, + modality, + maxTokens, +}: { + id: string + model: OpenRouterBaseModel + modality: string | null | undefined + maxTokens: number | null | undefined +}): ModelInfo => { + const cacheWritesPrice = model.pricing?.input_cache_write + ? parseApiPrice(model.pricing?.input_cache_write) + : undefined + + const cacheReadsPrice = model.pricing?.input_cache_read ? parseApiPrice(model.pricing?.input_cache_read) : undefined + + const supportsPromptCache = typeof cacheWritesPrice !== "undefined" && typeof cacheReadsPrice !== "undefined" + + const modelInfo: ModelInfo = { + maxTokens: maxTokens || 0, + contextWindow: model.context_length, + supportsImages: modality?.includes("image") ?? false, + supportsPromptCache, + inputPrice: parseApiPrice(model.pricing?.prompt), + outputPrice: parseApiPrice(model.pricing?.completion), + cacheWritesPrice, + cacheReadsPrice, + description: model.description, + thinking: id === "anthropic/claude-3.7-sonnet:thinking", + } + + // The OpenRouter model definition doesn't give us any hints about + // computer use, so we need to set that manually. + if (COMPUTER_USE_MODELS.has(id)) { + modelInfo.supportsComputerUse = true + } + + // Claude 3.7 Sonnet is a "hybrid" thinking model, and the `maxTokens` + // values can be configured. For the non-thinking variant we want to + // use 8k. The `thinking` variant can be run in 64k and 128k modes, + // and we want to use 128k. + if (id.startsWith("anthropic/claude-3.7-sonnet")) { + modelInfo.maxTokens = id.includes("thinking") + ? anthropicModels["claude-3-7-sonnet-20250219:thinking"].maxTokens + : anthropicModels["claude-3-7-sonnet-20250219"].maxTokens + } + + return modelInfo +} diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 5e9db97afea..d519d5e6295 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -3,32 +3,18 @@ import { GoogleGenAI, type GenerateContentResponseUsageMetadata, type GenerateContentParameters, - type Content, + type GenerateContentConfig, } from "@google/genai" import type { JWTInput } from "google-auth-library" -import NodeCache from "node-cache" import { ApiHandlerOptions, ModelInfo, GeminiModelId, geminiDefaultModelId, geminiModels } from "../../shared/api" import { safeJsonParse } from "../../shared/safeJsonParse" import { SingleCompletionHandler } from "../index" -import { - convertAnthropicContentToGemini, - convertAnthropicMessageToGemini, - getMessagesLength, -} from "../transform/gemini-format" +import { convertAnthropicContentToGemini, convertAnthropicMessageToGemini } from "../transform/gemini-format" import type { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" -const CACHE_TTL = 5 - -const CONTEXT_CACHE_TOKEN_MINIMUM = 4096 - -type CacheEntry = { - key: string - count: number -} - type GeminiHandlerOptions = ApiHandlerOptions & { isVertex?: boolean } @@ -37,8 +23,6 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl protected options: ApiHandlerOptions private client: GoogleGenAI - private contentCaches: NodeCache - private isCacheBusy = false constructor({ isVertex, ...options }: GeminiHandlerOptions) { super() @@ -68,98 +52,22 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl : isVertex ? new GoogleGenAI({ vertexai: true, project, location }) : new GoogleGenAI({ apiKey }) - - this.contentCaches = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 }) } - async *createMessage( - systemInstruction: string, - messages: Anthropic.Messages.MessageParam[], - cacheKey?: string, - ): ApiStream { + async *createMessage(systemInstruction: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { const { id: model, thinkingConfig, maxOutputTokens, info } = this.getModel() const contents = messages.map(convertAnthropicMessageToGemini) - const contentsLength = systemInstruction.length + getMessagesLength(contents) - - let uncachedContent: Content[] | undefined = undefined - let cachedContent: string | undefined = undefined - - // The minimum input token count for context caching is 4,096. - // For a basic approximation we assume 4 characters per token. - // We can use tiktoken eventually to get a more accurat token count. - // https://ai.google.dev/gemini-api/docs/caching?lang=node - // https://ai.google.dev/gemini-api/docs/tokens?lang=node - const isCacheAvailable = - info.supportsPromptCache && - this.options.promptCachingEnabled && - cacheKey && - contentsLength > 4 * CONTEXT_CACHE_TOKEN_MINIMUM - - let cacheWrite = false - - if (isCacheAvailable) { - const cacheEntry = this.contentCaches.get(cacheKey) - - if (cacheEntry) { - uncachedContent = contents.slice(cacheEntry.count, contents.length) - cachedContent = cacheEntry.key - console.log( - `[GeminiHandler] using ${cacheEntry.count} cached messages (${cacheEntry.key}) and ${uncachedContent.length} uncached messages`, - ) - } - if (!this.isCacheBusy) { - this.isCacheBusy = true - const timestamp = Date.now() - - this.client.caches - .create({ - model, - config: { - contents, - systemInstruction, - ttl: `${CACHE_TTL * 60}s`, - httpOptions: { timeout: 120_000 }, - }, - }) - .then((result) => { - const { name, usageMetadata } = result - - if (name) { - this.contentCaches.set(cacheKey, { key: name, count: contents.length }) - console.log( - `[GeminiHandler] cached ${contents.length} messages (${usageMetadata?.totalTokenCount ?? "-"} tokens) in ${Date.now() - timestamp}ms`, - ) - } - }) - .catch((error) => { - console.error(`[GeminiHandler] caches.create error`, error) - }) - .finally(() => { - this.isCacheBusy = false - }) - - cacheWrite = true - } + const config: GenerateContentConfig = { + systemInstruction, + httpOptions: this.options.googleGeminiBaseUrl ? { baseUrl: this.options.googleGeminiBaseUrl } : undefined, + thinkingConfig, + maxOutputTokens, + temperature: this.options.modelTemperature ?? 0, } - const isCacheUsed = !!cachedContent - - const params: GenerateContentParameters = { - model, - contents: uncachedContent ?? contents, - config: { - cachedContent, - systemInstruction: isCacheUsed ? undefined : systemInstruction, - httpOptions: this.options.googleGeminiBaseUrl - ? { baseUrl: this.options.googleGeminiBaseUrl } - : undefined, - thinkingConfig, - maxOutputTokens, - temperature: this.options.modelTemperature ?? 0, - }, - } + const params: GenerateContentParameters = { model, contents, config } const result = await this.client.models.generateContentStream(params) @@ -178,7 +86,6 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl if (lastUsageMetadata) { const inputTokens = lastUsageMetadata.promptTokenCount ?? 0 const outputTokens = lastUsageMetadata.candidatesTokenCount ?? 0 - const cacheWriteTokens = cacheWrite ? inputTokens : undefined const cacheReadTokens = lastUsageMetadata.cachedContentTokenCount const reasoningTokens = lastUsageMetadata.thoughtsTokenCount @@ -186,16 +93,9 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl type: "usage", inputTokens, outputTokens, - cacheWriteTokens, cacheReadTokens, reasoningTokens, - totalCost: this.calculateCost({ - info, - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - }), + totalCost: this.calculateCost({ info, inputTokens, outputTokens, cacheReadTokens }), } } } @@ -279,22 +179,19 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl info, inputTokens, outputTokens, - cacheWriteTokens = 0, cacheReadTokens = 0, }: { info: ModelInfo inputTokens: number outputTokens: number - cacheWriteTokens?: number cacheReadTokens?: number }) { - if (!info.inputPrice || !info.outputPrice || !info.cacheWritesPrice || !info.cacheReadsPrice) { + if (!info.inputPrice || !info.outputPrice || !info.cacheReadsPrice) { return undefined } let inputPrice = info.inputPrice let outputPrice = info.outputPrice - let cacheWritesPrice = info.cacheWritesPrice let cacheReadsPrice = info.cacheReadsPrice // If there's tiered pricing then adjust the input and output token prices @@ -305,7 +202,6 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl if (tier) { inputPrice = tier.inputPrice ?? inputPrice outputPrice = tier.outputPrice ?? outputPrice - cacheWritesPrice = tier.cacheWritesPrice ?? cacheWritesPrice cacheReadsPrice = tier.cacheReadsPrice ?? cacheReadsPrice } } @@ -313,23 +209,17 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl // Subtract the cached input tokens from the total input tokens. const uncachedInputTokens = inputTokens - cacheReadTokens - let cacheWriteCost = - cacheWriteTokens > 0 ? cacheWritesPrice * (cacheWriteTokens / 1_000_000) * (CACHE_TTL / 60) : 0 let cacheReadCost = cacheReadTokens > 0 ? cacheReadsPrice * (cacheReadTokens / 1_000_000) : 0 const inputTokensCost = inputPrice * (uncachedInputTokens / 1_000_000) const outputTokensCost = outputPrice * (outputTokens / 1_000_000) - const totalCost = inputTokensCost + outputTokensCost + cacheWriteCost + cacheReadCost + const totalCost = inputTokensCost + outputTokensCost + cacheReadCost const trace: Record = { input: { price: inputPrice, tokens: uncachedInputTokens, cost: inputTokensCost }, output: { price: outputPrice, tokens: outputTokens, cost: outputTokensCost }, } - if (cacheWriteTokens > 0) { - trace.cacheWrite = { price: cacheWritesPrice, tokens: cacheWriteTokens, cost: cacheWriteCost } - } - if (cacheReadTokens > 0) { trace.cacheRead = { price: cacheReadsPrice, tokens: cacheReadTokens, cost: cacheReadCost } } diff --git a/src/api/providers/groq.ts b/src/api/providers/groq.ts new file mode 100644 index 00000000000..2f4e763b8e5 --- /dev/null +++ b/src/api/providers/groq.ts @@ -0,0 +1,17 @@ +import { ApiHandlerOptions, GroqModelId, groqDefaultModelId, groqModels } from "../../shared/api" // Updated imports for Groq + +import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider" + +export class GroqHandler extends BaseOpenAiCompatibleProvider { + constructor(options: ApiHandlerOptions) { + super({ + ...options, + providerName: "Groq", + baseURL: "https://api.groq.com/openai/v1", + apiKey: options.groqApiKey, + defaultProviderModelId: groqDefaultModelId, + providerModels: groqModels, + defaultTemperature: 0.5, + }) + } +} diff --git a/src/api/providers/litellm.ts b/src/api/providers/litellm.ts new file mode 100644 index 00000000000..be88ede5f60 --- /dev/null +++ b/src/api/providers/litellm.ts @@ -0,0 +1,113 @@ +import OpenAI from "openai" +import { Anthropic } from "@anthropic-ai/sdk" // Keep for type usage only + +import { ApiHandlerOptions, litellmDefaultModelId, litellmDefaultModelInfo } from "../../shared/api" +import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" +import { convertToOpenAiMessages } from "../transform/openai-format" +import { SingleCompletionHandler } from "../index" +import { RouterProvider } from "./router-provider" + +/** + * LiteLLM provider handler + * + * This handler uses the LiteLLM API to proxy requests to various LLM providers. + * It follows the OpenAI API format for compatibility. + */ +export class LiteLLMHandler extends RouterProvider implements SingleCompletionHandler { + constructor(options: ApiHandlerOptions) { + super({ + options, + name: "litellm", + baseURL: `${options.litellmBaseUrl || "http://localhost:4000"}`, + apiKey: options.litellmApiKey || "dummy-key", + modelId: options.litellmModelId, + defaultModelId: litellmDefaultModelId, + defaultModelInfo: litellmDefaultModelInfo, + }) + } + + override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + const { id: modelId, info } = await this.fetchModel() + + const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ + { role: "system", content: systemPrompt }, + ...convertToOpenAiMessages(messages), + ] + + // Required by some providers; others default to max tokens allowed + let maxTokens: number | undefined = info.maxTokens ?? undefined + + const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { + model: modelId, + max_tokens: maxTokens, + messages: openAiMessages, + stream: true, + stream_options: { + include_usage: true, + }, + } + + if (this.supportsTemperature(modelId)) { + requestOptions.temperature = this.options.modelTemperature ?? 0 + } + + try { + const { data: completion } = await this.client.chat.completions.create(requestOptions).withResponse() + + let lastUsage + + for await (const chunk of completion) { + const delta = chunk.choices[0]?.delta + const usage = chunk.usage as OpenAI.CompletionUsage + + if (delta?.content) { + yield { type: "text", text: delta.content } + } + + if (usage) { + lastUsage = usage + } + } + + if (lastUsage) { + const usageData: ApiStreamUsageChunk = { + type: "usage", + inputTokens: lastUsage.prompt_tokens || 0, + outputTokens: lastUsage.completion_tokens || 0, + } + + yield usageData + } + } catch (error) { + if (error instanceof Error) { + throw new Error(`LiteLLM streaming error: ${error.message}`) + } + throw error + } + } + + async completePrompt(prompt: string): Promise { + const { id: modelId, info } = await this.fetchModel() + + try { + const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = { + model: modelId, + messages: [{ role: "user", content: prompt }], + } + + if (this.supportsTemperature(modelId)) { + requestOptions.temperature = this.options.modelTemperature ?? 0 + } + + requestOptions.max_tokens = info.maxTokens + + const response = await this.client.chat.completions.create(requestOptions) + return response.choices[0]?.message.content || "" + } catch (error) { + if (error instanceof Error) { + throw new Error(`LiteLLM completion error: ${error.message}`) + } + throw error + } + } +} diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index e1104f4f9a5..2d9c7f8b8a4 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -8,7 +8,6 @@ import { openRouterDefaultModelId, openRouterDefaultModelInfo, PROMPT_CACHING_MODELS, - OPTIONAL_PROMPT_CACHING_MODELS, REASONING_MODELS, } from "../../shared/api" @@ -21,7 +20,8 @@ import { addCacheBreakpoints as addGeminiCacheBreakpoints } from "../transform/c import { getModelParams, SingleCompletionHandler } from "../index" import { DEFAULT_HEADERS, DEEP_SEEK_DEFAULT_TEMPERATURE } from "./constants" import { BaseProvider } from "./base-provider" -import { getModels } from "./fetchers/cache" +import { getModels } from "./fetchers/modelCache" +import { getModelEndpoints } from "./fetchers/modelEndpointCache" const OPENROUTER_DEFAULT_PROVIDER_NAME = "[default]" @@ -58,6 +58,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH protected options: ApiHandlerOptions private client: OpenAI protected models: ModelRecord = {} + protected endpoints: ModelRecord = {} constructor(options: ApiHandlerOptions) { super() @@ -94,7 +95,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) } - const isCacheAvailable = promptCache.supported && (!promptCache.optional || this.options.promptCachingEnabled) + const isCacheAvailable = promptCache.supported // https://openrouter.ai/docs/features/prompt-caching if (isCacheAvailable) { @@ -106,7 +107,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH // https://openrouter.ai/docs/transforms const completionParams: OpenRouterChatCompletionParams = { model: modelId, - max_tokens: maxTokens, + ...(maxTokens && maxTokens > 0 && { max_tokens: maxTokens }), temperature, thinking, // OpenRouter is temporarily supporting this. top_p: topP, @@ -116,7 +117,11 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH // Only include provider if openRouterSpecificProvider is not "[default]". ...(this.options.openRouterSpecificProvider && this.options.openRouterSpecificProvider !== OPENROUTER_DEFAULT_PROVIDER_NAME && { - provider: { order: [this.options.openRouterSpecificProvider] }, + provider: { + order: [this.options.openRouterSpecificProvider], + only: [this.options.openRouterSpecificProvider], + allow_fallbacks: false, + }, }), // This way, the transforms field will only be included in the parameters when openRouterUseMiddleOutTransform is true. ...((this.options.openRouterUseMiddleOutTransform ?? true) && { transforms: ["middle-out"] }), @@ -165,13 +170,29 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH } public async fetchModel() { - this.models = await getModels("openrouter") + const [models, endpoints] = await Promise.all([ + getModels("openrouter"), + getModelEndpoints({ + router: "openrouter", + modelId: this.options.openRouterModelId, + endpoint: this.options.openRouterSpecificProvider, + }), + ]) + + this.models = models + this.endpoints = endpoints + return this.getModel() } override getModel() { const id = this.options.openRouterModelId ?? openRouterDefaultModelId - const info = this.models[id] ?? openRouterDefaultModelInfo + let info = this.models[id] ?? openRouterDefaultModelInfo + + // If a specific provider is requested, use the endpoint for that provider. + if (this.options.openRouterSpecificProvider && this.endpoints[this.options.openRouterSpecificProvider]) { + info = this.endpoints[this.options.openRouterSpecificProvider] + } const isDeepSeekR1 = id.startsWith("deepseek/deepseek-r1") || id === "perplexity/sonar-reasoning" @@ -187,7 +208,6 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH topP: isDeepSeekR1 ? 0.95 : undefined, promptCache: { supported: PROMPT_CACHING_MODELS.has(id), - optional: OPTIONAL_PROMPT_CACHING_MODELS.has(id), }, } } @@ -202,6 +222,15 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH temperature, messages: [{ role: "user", content: prompt }], stream: false, + // Only include provider if openRouterSpecificProvider is not "[default]". + ...(this.options.openRouterSpecificProvider && + this.options.openRouterSpecificProvider !== OPENROUTER_DEFAULT_PROVIDER_NAME && { + provider: { + order: [this.options.openRouterSpecificProvider], + only: [this.options.openRouterSpecificProvider], + allow_fallbacks: false, + }, + }), } const response = await this.client.chat.completions.create(completionParams) diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index 9fe976bb51c..fe8bba7e6ec 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -1,11 +1,19 @@ import { Anthropic } from "@anthropic-ai/sdk" -import OpenAI from "openai" - -import { ModelInfo, ModelRecord, requestyDefaultModelId, requestyDefaultModelInfo } from "../../shared/api" +import { + ApiHandlerOptions, + ModelInfo, + ModelRecord, + requestyDefaultModelId, + requestyDefaultModelInfo, +} from "../../shared/api" +import { convertToOpenAiMessages } from "../transform/openai-format" import { calculateApiCostOpenAI } from "../../utils/cost" import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" -import { OpenAiHandler, OpenAiHandlerOptions } from "./openai" -import { getModels } from "./fetchers/cache" +import { SingleCompletionHandler } from "../" +import { BaseProvider } from "./base-provider" +import { DEFAULT_HEADERS } from "./constants" +import { getModels } from "./fetchers/modelCache" +import OpenAI from "openai" // Requesty usage includes an extra field for Anthropic use cases. // Safely cast the prompt token details section to the appropriate structure. @@ -17,25 +25,28 @@ interface RequestyUsage extends OpenAI.CompletionUsage { total_cost?: number } -export class RequestyHandler extends OpenAiHandler { +type RequestyChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & {} + +export class RequestyHandler extends BaseProvider implements SingleCompletionHandler { + protected options: ApiHandlerOptions protected models: ModelRecord = {} + private client: OpenAI - constructor(options: OpenAiHandlerOptions) { - if (!options.requestyApiKey) { - throw new Error("Requesty API key is required. Please provide it in the settings.") - } + constructor(options: ApiHandlerOptions) { + super() + this.options = options - super({ - ...options, - openAiApiKey: options.requestyApiKey, - openAiModelId: options.requestyModelId ?? requestyDefaultModelId, - openAiBaseUrl: "https://router.requesty.ai/v1", - }) + const apiKey = this.options.requestyApiKey ?? "not-provided" + const baseURL = "https://router.requesty.ai/v1" + + const defaultHeaders = DEFAULT_HEADERS + + this.client = new OpenAI({ baseURL, apiKey, defaultHeaders }) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + public async fetchModel() { this.models = await getModels("requesty") - yield* super.createMessage(systemPrompt, messages) + return this.getModel() } override getModel(): { id: string; info: ModelInfo } { @@ -44,7 +55,7 @@ export class RequestyHandler extends OpenAiHandler { return { id, info } } - protected override processUsageMetrics(usage: any, modelInfo?: ModelInfo): ApiStreamUsageChunk { + protected processUsageMetrics(usage: any, modelInfo?: ModelInfo): ApiStreamUsageChunk { const requestyUsage = usage as RequestyUsage const inputTokens = requestyUsage?.prompt_tokens || 0 const outputTokens = requestyUsage?.completion_tokens || 0 @@ -64,8 +75,80 @@ export class RequestyHandler extends OpenAiHandler { } } - override async completePrompt(prompt: string): Promise { - this.models = await getModels("requesty") - return super.completePrompt(prompt) + override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + const model = await this.fetchModel() + + let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ + { role: "system", content: systemPrompt }, + ...convertToOpenAiMessages(messages), + ] + + let maxTokens = undefined + if (this.options.includeMaxTokens) { + maxTokens = model.info.maxTokens + } + + const temperature = this.options.modelTemperature + + const completionParams: RequestyChatCompletionParams = { + model: model.id, + max_tokens: maxTokens, + messages: openAiMessages, + temperature: temperature, + stream: true, + stream_options: { include_usage: true }, + } + + const stream = await this.client.chat.completions.create(completionParams) + + let lastUsage: any = undefined + + for await (const chunk of stream) { + const delta = chunk.choices[0]?.delta + if (delta?.content) { + yield { + type: "text", + text: delta.content, + } + } + + if (delta && "reasoning_content" in delta && delta.reasoning_content) { + yield { + type: "reasoning", + text: (delta.reasoning_content as string | undefined) || "", + } + } + + if (chunk.usage) { + lastUsage = chunk.usage + } + } + + if (lastUsage) { + yield this.processUsageMetrics(lastUsage, model.info) + } + } + + async completePrompt(prompt: string): Promise { + const model = await this.fetchModel() + + let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [{ role: "system", content: prompt }] + + let maxTokens = undefined + if (this.options.includeMaxTokens) { + maxTokens = model.info.maxTokens + } + + const temperature = this.options.modelTemperature + + const completionParams: RequestyChatCompletionParams = { + model: model.id, + max_tokens: maxTokens, + messages: openAiMessages, + temperature: temperature, + } + + const response: OpenAI.Chat.ChatCompletion = await this.client.chat.completions.create(completionParams) + return response.choices[0]?.message.content || "" } } diff --git a/src/api/providers/router-provider.ts b/src/api/providers/router-provider.ts index 5b680b1b1d0..a0decdcab41 100644 --- a/src/api/providers/router-provider.ts +++ b/src/api/providers/router-provider.ts @@ -2,7 +2,7 @@ import OpenAI from "openai" import { ApiHandlerOptions, RouterName, ModelRecord, ModelInfo } from "../../shared/api" import { BaseProvider } from "./base-provider" -import { getModels } from "./fetchers/cache" +import { getModels } from "./fetchers/modelCache" type RouterProviderOptions = { name: RouterName @@ -44,7 +44,7 @@ export abstract class RouterProvider extends BaseProvider { } public async fetchModel() { - this.models = await getModels(this.name) + this.models = await getModels(this.name, this.client.apiKey, this.client.baseURL) return this.getModel() } diff --git a/src/api/transform/__tests__/image-cleaning.test.ts b/src/api/transform/__tests__/image-cleaning.test.ts new file mode 100644 index 00000000000..cbb318531a1 --- /dev/null +++ b/src/api/transform/__tests__/image-cleaning.test.ts @@ -0,0 +1,336 @@ +import { ApiHandler } from "../.." +import { ApiMessage } from "../../../core/task-persistence/apiMessages" +import { maybeRemoveImageBlocks } from "../image-cleaning" +import { ModelInfo } from "../../../shared/api" + +describe("maybeRemoveImageBlocks", () => { + // Mock ApiHandler factory function + const createMockApiHandler = (supportsImages: boolean): ApiHandler => { + return { + getModel: jest.fn().mockReturnValue({ + id: "test-model", + info: { + supportsImages, + } as ModelInfo, + }), + createMessage: jest.fn(), + countTokens: jest.fn(), + } + } + + it("should handle empty messages array", () => { + const apiHandler = createMockApiHandler(true) + const messages: ApiMessage[] = [] + + const result = maybeRemoveImageBlocks(messages, apiHandler) + + expect(result).toEqual([]) + // No need to check if getModel was called since there are no messages to process + }) + + it("should not modify messages with no image blocks", () => { + const apiHandler = createMockApiHandler(true) + const messages: ApiMessage[] = [ + { + role: "user", + content: "Hello, world!", + }, + { + role: "assistant", + content: "Hi there!", + }, + ] + + const result = maybeRemoveImageBlocks(messages, apiHandler) + + expect(result).toEqual(messages) + // getModel is only called when content is an array, which is not the case here + }) + + it("should not modify messages with array content but no image blocks", () => { + const apiHandler = createMockApiHandler(true) + const messages: ApiMessage[] = [ + { + role: "user", + content: [ + { + type: "text", + text: "Hello, world!", + }, + { + type: "text", + text: "How are you?", + }, + ], + }, + ] + + const result = maybeRemoveImageBlocks(messages, apiHandler) + + expect(result).toEqual(messages) + expect(apiHandler.getModel).toHaveBeenCalled() + }) + + it("should not modify image blocks when API handler supports images", () => { + const apiHandler = createMockApiHandler(true) + const messages: ApiMessage[] = [ + { + role: "user", + content: [ + { + type: "text", + text: "Check out this image:", + }, + { + type: "image", + source: { + type: "base64", + media_type: "image/jpeg", + data: "base64-encoded-image-data", + }, + }, + ], + }, + ] + + const result = maybeRemoveImageBlocks(messages, apiHandler) + + // Should not modify the messages since the API handler supports images + expect(result).toEqual(messages) + expect(apiHandler.getModel).toHaveBeenCalled() + }) + + it("should convert image blocks to text descriptions when API handler doesn't support images", () => { + const apiHandler = createMockApiHandler(false) + const messages: ApiMessage[] = [ + { + role: "user", + content: [ + { + type: "text", + text: "Check out this image:", + }, + { + type: "image", + source: { + type: "base64", + media_type: "image/jpeg", + data: "base64-encoded-image-data", + }, + }, + ], + }, + ] + + const result = maybeRemoveImageBlocks(messages, apiHandler) + + // Should convert image blocks to text descriptions + expect(result).toEqual([ + { + role: "user", + content: [ + { + type: "text", + text: "Check out this image:", + }, + { + type: "text", + text: "[Referenced image in conversation]", + }, + ], + }, + ]) + expect(apiHandler.getModel).toHaveBeenCalled() + }) + + it("should handle mixed content messages with multiple text and image blocks", () => { + const apiHandler = createMockApiHandler(false) + const messages: ApiMessage[] = [ + { + role: "user", + content: [ + { + type: "text", + text: "Here are some images:", + }, + { + type: "image", + source: { + type: "base64", + media_type: "image/jpeg", + data: "image-data-1", + }, + }, + { + type: "text", + text: "And another one:", + }, + { + type: "image", + source: { + type: "base64", + media_type: "image/png", + data: "image-data-2", + }, + }, + ], + }, + ] + + const result = maybeRemoveImageBlocks(messages, apiHandler) + + // Should convert all image blocks to text descriptions + expect(result).toEqual([ + { + role: "user", + content: [ + { + type: "text", + text: "Here are some images:", + }, + { + type: "text", + text: "[Referenced image in conversation]", + }, + { + type: "text", + text: "And another one:", + }, + { + type: "text", + text: "[Referenced image in conversation]", + }, + ], + }, + ]) + expect(apiHandler.getModel).toHaveBeenCalled() + }) + + it("should handle multiple messages with image blocks", () => { + const apiHandler = createMockApiHandler(false) + const messages: ApiMessage[] = [ + { + role: "user", + content: [ + { + type: "text", + text: "Here's an image:", + }, + { + type: "image", + source: { + type: "base64", + media_type: "image/jpeg", + data: "image-data-1", + }, + }, + ], + }, + { + role: "assistant", + content: "I see the image!", + }, + { + role: "user", + content: [ + { + type: "text", + text: "Here's another image:", + }, + { + type: "image", + source: { + type: "base64", + media_type: "image/png", + data: "image-data-2", + }, + }, + ], + }, + ] + + const result = maybeRemoveImageBlocks(messages, apiHandler) + + // Should convert all image blocks to text descriptions + expect(result).toEqual([ + { + role: "user", + content: [ + { + type: "text", + text: "Here's an image:", + }, + { + type: "text", + text: "[Referenced image in conversation]", + }, + ], + }, + { + role: "assistant", + content: "I see the image!", + }, + { + role: "user", + content: [ + { + type: "text", + text: "Here's another image:", + }, + { + type: "text", + text: "[Referenced image in conversation]", + }, + ], + }, + ]) + expect(apiHandler.getModel).toHaveBeenCalled() + }) + + it("should preserve additional message properties", () => { + const apiHandler = createMockApiHandler(false) + const messages: ApiMessage[] = [ + { + role: "user", + content: [ + { + type: "text", + text: "Here's an image:", + }, + { + type: "image", + source: { + type: "base64", + media_type: "image/jpeg", + data: "image-data", + }, + }, + ], + ts: 1620000000000, + isSummary: true, + }, + ] + + const result = maybeRemoveImageBlocks(messages, apiHandler) + + // Should convert image blocks to text descriptions while preserving additional properties + expect(result).toEqual([ + { + role: "user", + content: [ + { + type: "text", + text: "Here's an image:", + }, + { + type: "text", + text: "[Referenced image in conversation]", + }, + ], + ts: 1620000000000, + isSummary: true, + }, + ]) + expect(apiHandler.getModel).toHaveBeenCalled() + }) +}) diff --git a/src/api/transform/gemini-format.ts b/src/api/transform/gemini-format.ts index be08d7ff7ba..ee22cff32a4 100644 --- a/src/api/transform/gemini-format.ts +++ b/src/api/transform/gemini-format.ts @@ -76,9 +76,3 @@ export function convertAnthropicMessageToGemini(message: Anthropic.Messages.Mess parts: convertAnthropicContentToGemini(message.content), } } - -const getContentLength = ({ parts }: Content): number => - parts?.reduce((length, { text }) => length + (text?.length ?? 0), 0) ?? 0 - -export const getMessagesLength = (contents: Content[]): number => - contents.reduce((length, content) => length + getContentLength(content), 0) diff --git a/src/api/transform/image-cleaning.ts b/src/api/transform/image-cleaning.ts new file mode 100644 index 00000000000..e5987bb59e5 --- /dev/null +++ b/src/api/transform/image-cleaning.ts @@ -0,0 +1,28 @@ +import { ApiHandler } from ".." +import { ApiMessage } from "../../core/task-persistence/apiMessages" + +/* Removes image blocks from messages if they are not supported by the Api Handler */ +export function maybeRemoveImageBlocks(messages: ApiMessage[], apiHandler: ApiHandler): ApiMessage[] { + return messages.map((message) => { + // Handle array content (could contain image blocks). + let { content } = message + if (Array.isArray(content)) { + if (!apiHandler.getModel().info.supportsImages) { + // Convert image blocks to text descriptions. + content = content.map((block) => { + if (block.type === "image") { + // Convert image blocks to text descriptions. + // Note: We can't access the actual image content/url due to API limitations, + // but we can indicate that an image was present in the conversation. + return { + type: "text", + text: "[Referenced image in conversation]", + } + } + return block + }) + } + } + return { ...message, content } + }) +} diff --git a/src/core/__tests__/read-file-maxReadFileLine.test.ts b/src/core/__tests__/read-file-maxReadFileLine.test.ts deleted file mode 100644 index f8508694549..00000000000 --- a/src/core/__tests__/read-file-maxReadFileLine.test.ts +++ /dev/null @@ -1,408 +0,0 @@ -// npx jest src/core/__tests__/read-file-maxReadFileLine.test.ts - -import * as path from "path" - -import { countFileLines } from "../../integrations/misc/line-counter" -import { readLines } from "../../integrations/misc/read-lines" -import { extractTextFromFile } from "../../integrations/misc/extract-text" -import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter" -import { isBinaryFile } from "isbinaryfile" -import { ReadFileToolUse } from "../../shared/tools" - -// Mock dependencies -jest.mock("../../integrations/misc/line-counter") -jest.mock("../../integrations/misc/read-lines") -jest.mock("../../integrations/misc/extract-text", () => { - const actual = jest.requireActual("../../integrations/misc/extract-text") - // Create a spy on the actual addLineNumbers function - const addLineNumbersSpy = jest.spyOn(actual, "addLineNumbers") - - return { - ...actual, - // Expose the spy so tests can access it - __addLineNumbersSpy: addLineNumbersSpy, - extractTextFromFile: jest.fn(), - } -}) - -// Get a reference to the spy -const addLineNumbersSpy = jest.requireMock("../../integrations/misc/extract-text").__addLineNumbersSpy - -jest.mock("../../services/tree-sitter") -jest.mock("isbinaryfile") -jest.mock("../ignore/RooIgnoreController", () => ({ - RooIgnoreController: class { - initialize() { - return Promise.resolve() - } - validateAccess() { - return true - } - }, -})) -jest.mock("fs/promises", () => ({ - mkdir: jest.fn().mockResolvedValue(undefined), - writeFile: jest.fn().mockResolvedValue(undefined), - readFile: jest.fn().mockResolvedValue("{}"), -})) -jest.mock("../../utils/fs", () => ({ - fileExistsAtPath: jest.fn().mockReturnValue(true), -})) - -// Mock path -jest.mock("path", () => { - const originalPath = jest.requireActual("path") - return { - ...originalPath, - resolve: jest.fn().mockImplementation((...args) => args.join("/")), - } -}) - -describe("read_file tool with maxReadFileLine setting", () => { - // Test data - const testFilePath = "test/file.txt" - const absoluteFilePath = "/test/file.txt" - const fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5" - const numberedFileContent = "1 | Line 1\n2 | Line 2\n3 | Line 3\n4 | Line 4\n5 | Line 5\n" - const sourceCodeDef = "\n\n# file.txt\n1--5 | Content" - const expectedFullFileXml = `${testFilePath}\n\n${numberedFileContent}\n` - - // Mocked functions with correct types - const mockedCountFileLines = countFileLines as jest.MockedFunction - const mockedReadLines = readLines as jest.MockedFunction - const mockedExtractTextFromFile = extractTextFromFile as jest.MockedFunction - const mockedParseSourceCodeDefinitionsForFile = parseSourceCodeDefinitionsForFile as jest.MockedFunction< - typeof parseSourceCodeDefinitionsForFile - > - - // Variable to control what content is used by the mock - set in beforeEach - let mockInputContent = "" - - const mockedIsBinaryFile = isBinaryFile as jest.MockedFunction - const mockedPathResolve = path.resolve as jest.MockedFunction - - // Mock instances - const mockCline: any = {} - let mockProvider: any - let toolResult: string | undefined - - beforeEach(() => { - jest.clearAllMocks() - - // Setup path resolution - mockedPathResolve.mockReturnValue(absoluteFilePath) - - // Setup mocks for file operations - mockedIsBinaryFile.mockResolvedValue(false) - - // Set the default content for the mock - mockInputContent = fileContent - - // Setup the extractTextFromFile mock implementation with the current mockInputContent - mockedExtractTextFromFile.mockImplementation((_filePath) => { - const actual = jest.requireActual("../../integrations/misc/extract-text") - return Promise.resolve(actual.addLineNumbers(mockInputContent)) - }) - - // No need to setup the extractTextFromFile mock implementation here - // as it's already defined at the module level - - // Setup mock provider - mockProvider = { - getState: jest.fn(), - deref: jest.fn().mockReturnThis(), - } - - // Setup Cline instance with mock methods - mockCline.cwd = "/" - mockCline.task = "Test" - mockCline.providerRef = mockProvider - mockCline.rooIgnoreController = { - validateAccess: jest.fn().mockReturnValue(true), - } - mockCline.say = jest.fn().mockResolvedValue(undefined) - mockCline.ask = jest.fn().mockResolvedValue(true) - mockCline.presentAssistantMessage = jest.fn() - mockCline.getFileContextTracker = jest.fn().mockReturnValue({ - trackFileContext: jest.fn().mockResolvedValue(undefined), - }) - mockCline.recordToolUsage = jest.fn().mockReturnValue(undefined) - mockCline.recordToolError = jest.fn().mockReturnValue(undefined) - // Reset tool result - toolResult = undefined - }) - - /** - * Helper function to execute the read file tool with different maxReadFileLine settings - */ - async function executeReadFileTool( - params: Partial = {}, - options: { - maxReadFileLine?: number - totalLines?: number - skipAddLineNumbersCheck?: boolean // Flag to skip addLineNumbers check - } = {}, - ): Promise { - // Configure mocks based on test scenario - const maxReadFileLine = options.maxReadFileLine ?? 500 - const totalLines = options.totalLines ?? 5 - - mockProvider.getState.mockResolvedValue({ maxReadFileLine }) - mockedCountFileLines.mockResolvedValue(totalLines) - - // Reset the spy before each test - addLineNumbersSpy.mockClear() - - // Create a tool use object - const toolUse: ReadFileToolUse = { - type: "tool_use", - name: "read_file", - params: { - path: testFilePath, - ...params, - }, - partial: false, - } - - // Import the tool implementation dynamically to avoid hoisting issues - const { readFileTool } = require("../tools/readFileTool") - - // Execute the tool - await readFileTool( - mockCline, - toolUse, - mockCline.ask, - jest.fn(), - (result: string) => { - toolResult = result - }, - (param: string, value: string) => value, - ) - - // Verify addLineNumbers was called appropriately - if (!options.skipAddLineNumbersCheck) { - expect(addLineNumbersSpy).toHaveBeenCalled() - } else { - expect(addLineNumbersSpy).not.toHaveBeenCalled() - } - - return toolResult - } - describe("when maxReadFileLine is negative", () => { - it("should read the entire file using extractTextFromFile", async () => { - // Setup - use default mockInputContent - mockInputContent = fileContent - - // Execute - const result = await executeReadFileTool({}, { maxReadFileLine: -1 }) - - // Verify - expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) - expect(mockedReadLines).not.toHaveBeenCalled() - expect(mockedParseSourceCodeDefinitionsForFile).not.toHaveBeenCalled() - expect(result).toBe(expectedFullFileXml) - }) - - it("should ignore range parameters and read entire file when maxReadFileLine is -1", async () => { - // Setup - use default mockInputContent - mockInputContent = fileContent - - // Execute with range parameters - const result = await executeReadFileTool( - { - start_line: "2", - end_line: "4", - }, - { maxReadFileLine: -1 }, - ) - - // Verify that extractTextFromFile is still used (not readLines) - expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) - expect(mockedReadLines).not.toHaveBeenCalled() - expect(mockedParseSourceCodeDefinitionsForFile).not.toHaveBeenCalled() - expect(result).toBe(expectedFullFileXml) - }) - - it("should not show line snippet in approval message when maxReadFileLine is -1", async () => { - // This test verifies the line snippet behavior for the approval message - // Setup - use default mockInputContent - mockInputContent = fileContent - - // Execute - we'll reuse executeReadFileTool to run the tool - await executeReadFileTool({}, { maxReadFileLine: -1 }) - - // Verify the empty line snippet for full read was passed to the approval message - // Look at the parameters passed to the 'ask' method in the approval message - const askCall = mockCline.ask.mock.calls[0] - const completeMessage = JSON.parse(askCall[1]) - - // Verify the reason (lineSnippet) is empty or undefined for full read - expect(completeMessage.reason).toBeFalsy() - }) - }) - - describe("when maxReadFileLine is 0", () => { - it("should return an empty content with source code definitions", async () => { - // Setup - for maxReadFileLine = 0, the implementation won't call readLines - mockedParseSourceCodeDefinitionsForFile.mockResolvedValue(sourceCodeDef) - - // Execute - skip addLineNumbers check as it's not called for maxReadFileLine=0 - const result = await executeReadFileTool( - {}, - { - maxReadFileLine: 0, - totalLines: 5, - skipAddLineNumbersCheck: true, - }, - ) - - // Verify - expect(mockedExtractTextFromFile).not.toHaveBeenCalled() - expect(mockedReadLines).not.toHaveBeenCalled() // Per implementation line 141 - expect(mockedParseSourceCodeDefinitionsForFile).toHaveBeenCalledWith( - absoluteFilePath, - mockCline.rooIgnoreController, - ) - - // Verify XML structure - expect(result).toContain(`${testFilePath}`) - expect(result).toContain("Showing only 0 of 5 total lines") - expect(result).toContain("") - expect(result).toContain("") - expect(result).toContain(sourceCodeDef.trim()) - expect(result).toContain("") - expect(result).not.toContain(" { - it("should read only maxReadFileLine lines and add source code definitions", async () => { - // Setup - const content = "Line 1\nLine 2\nLine 3" - mockedReadLines.mockResolvedValue(content) - mockedParseSourceCodeDefinitionsForFile.mockResolvedValue(sourceCodeDef) - - // Execute - const result = await executeReadFileTool({}, { maxReadFileLine: 3 }) - - // Verify - check behavior but not specific implementation details - expect(mockedExtractTextFromFile).not.toHaveBeenCalled() - expect(mockedReadLines).toHaveBeenCalled() - expect(mockedParseSourceCodeDefinitionsForFile).toHaveBeenCalledWith( - absoluteFilePath, - mockCline.rooIgnoreController, - ) - - // Verify XML structure - expect(result).toContain(`${testFilePath}`) - expect(result).toContain('') - expect(result).toContain("1 | Line 1") - expect(result).toContain("2 | Line 2") - expect(result).toContain("3 | Line 3") - expect(result).toContain("") - expect(result).toContain("Showing only 3 of 5 total lines") - expect(result).toContain("") - expect(result).toContain("") - expect(result).toContain(sourceCodeDef.trim()) - expect(result).toContain("") - expect(result).toContain("") - expect(result).toContain(sourceCodeDef.trim()) - }) - }) - - describe("when maxReadFileLine equals or exceeds file length", () => { - it("should use extractTextFromFile when maxReadFileLine > totalLines", async () => { - // Setup - mockedCountFileLines.mockResolvedValue(5) // File shorter than maxReadFileLine - mockInputContent = fileContent - - // Execute - const result = await executeReadFileTool({}, { maxReadFileLine: 10, totalLines: 5 }) - - // Verify - expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) - expect(result).toBe(expectedFullFileXml) - }) - - it("should read with extractTextFromFile when file has few lines", async () => { - // Setup - mockedCountFileLines.mockResolvedValue(3) // File shorter than maxReadFileLine - mockInputContent = fileContent - - // Execute - const result = await executeReadFileTool({}, { maxReadFileLine: 5, totalLines: 3 }) - - // Verify - expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) - expect(mockedReadLines).not.toHaveBeenCalled() - // Create a custom expected XML with lines="1-3" since totalLines is 3 - const expectedXml = `${testFilePath}\n\n${numberedFileContent}\n` - expect(result).toBe(expectedXml) - }) - }) - - describe("when file is binary", () => { - it("should always use extractTextFromFile regardless of maxReadFileLine", async () => { - // Setup - mockedIsBinaryFile.mockResolvedValue(true) - // For binary files, we're using a maxReadFileLine of 3 and totalLines is assumed to be 3 - mockedCountFileLines.mockResolvedValue(3) - - // For binary files, we need a special mock implementation that doesn't use addLineNumbers - // Save the original mock implementation - const originalMockImplementation = mockedExtractTextFromFile.getMockImplementation() - // Create a special mock implementation that doesn't call addLineNumbers - mockedExtractTextFromFile.mockImplementation(() => { - return Promise.resolve(numberedFileContent) - }) - - // Reset the spy to clear any previous calls - addLineNumbersSpy.mockClear() - - // Execute - skip addLineNumbers check as we're directly providing the numbered content - const result = await executeReadFileTool( - {}, - { - maxReadFileLine: 3, - totalLines: 3, - skipAddLineNumbersCheck: true, - }, - ) - - // Restore the original mock implementation after the test - mockedExtractTextFromFile.mockImplementation(originalMockImplementation) - - // Verify - expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) - expect(mockedReadLines).not.toHaveBeenCalled() - // Create a custom expected XML with lines="1-3" for binary files - const expectedXml = `${testFilePath}\n\n${numberedFileContent}\n` - expect(result).toBe(expectedXml) - }) - }) - - describe("with range parameters", () => { - it("should honor start_line and end_line when provided", async () => { - // Setup - mockedReadLines.mockResolvedValue("Line 2\nLine 3\nLine 4") - - // Execute using executeReadFileTool with range parameters - const rangeResult = await executeReadFileTool({ - start_line: "2", - end_line: "4", - }) - - // Verify - expect(mockedReadLines).toHaveBeenCalledWith(absoluteFilePath, 3, 1) // end_line - 1, start_line - 1 - expect(addLineNumbersSpy).toHaveBeenCalledWith(expect.any(String), 2) // start with proper line numbers - - // Verify XML structure with lines attribute - expect(rangeResult).toContain(`${testFilePath}`) - expect(rangeResult).toContain(``) - expect(rangeResult).toContain("2 | Line 2") - expect(rangeResult).toContain("3 | Line 3") - expect(rangeResult).toContain("4 | Line 4") - expect(rangeResult).toContain("") - }) - }) -}) diff --git a/src/core/assistant-message/__tests__/parseAssistantMessage.test.ts b/src/core/assistant-message/__tests__/parseAssistantMessage.test.ts new file mode 100644 index 00000000000..19f88a91d7d --- /dev/null +++ b/src/core/assistant-message/__tests__/parseAssistantMessage.test.ts @@ -0,0 +1,340 @@ +// npx jest src/core/assistant-message/__tests__/parseAssistantMessage.test.ts + +import { TextContent, ToolUse } from "../../../shared/tools" + +import { AssistantMessageContent, parseAssistantMessage as parseAssistantMessageV1 } from "../parseAssistantMessage" +import { parseAssistantMessageV2 } from "../parseAssistantMessageV2" + +const isEmptyTextContent = (block: AssistantMessageContent) => + block.type === "text" && (block as TextContent).content === "" + +;[parseAssistantMessageV1, parseAssistantMessageV2].forEach((parser, index) => { + describe(`parseAssistantMessageV${index + 1}`, () => { + describe("text content parsing", () => { + it("should parse a simple text message", () => { + const message = "This is a simple text message" + const result = parser(message) + + expect(result).toHaveLength(1) + expect(result[0]).toEqual({ + type: "text", + content: message, + partial: true, // Text is always partial when it's the last content + }) + }) + + it("should parse a multi-line text message", () => { + const message = "This is a multi-line\ntext message\nwith several lines" + const result = parser(message) + + expect(result).toHaveLength(1) + expect(result[0]).toEqual({ + type: "text", + content: message, + partial: true, // Text is always partial when it's the last content + }) + }) + + it("should mark text as partial when it's the last content in the message", () => { + const message = "This is a partial text" + const result = parser(message) + + expect(result).toHaveLength(1) + expect(result[0]).toEqual({ + type: "text", + content: message, + partial: true, + }) + }) + }) + + describe("tool use parsing", () => { + it("should parse a simple tool use", () => { + const message = "src/file.ts" + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("read_file") + expect(toolUse.params.path).toBe("src/file.ts") + expect(toolUse.partial).toBe(false) + }) + + it("should parse a tool use with multiple parameters", () => { + const message = + "src/file.ts1020" + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("read_file") + expect(toolUse.params.path).toBe("src/file.ts") + expect(toolUse.params.start_line).toBe("10") + expect(toolUse.params.end_line).toBe("20") + expect(toolUse.partial).toBe(false) + }) + + it("should mark tool use as partial when it's not closed", () => { + const message = "src/file.ts" + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("read_file") + expect(toolUse.params.path).toBe("src/file.ts") + expect(toolUse.partial).toBe(true) + }) + + it("should handle a partial parameter in a tool use", () => { + const message = "src/file.ts" + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("read_file") + expect(toolUse.params.path).toBe("src/file.ts") + expect(toolUse.partial).toBe(true) + }) + }) + + describe("mixed content parsing", () => { + it("should parse text followed by a tool use", () => { + const message = "Here's the file content: src/file.ts" + const result = parser(message) + + expect(result).toHaveLength(2) + + const textContent = result[0] as TextContent + expect(textContent.type).toBe("text") + expect(textContent.content).toBe("Here's the file content:") + expect(textContent.partial).toBe(false) + + const toolUse = result[1] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("read_file") + expect(toolUse.params.path).toBe("src/file.ts") + expect(toolUse.partial).toBe(false) + }) + + it("should parse a tool use followed by text", () => { + const message = "src/file.tsHere's what I found in the file." + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(2) + + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("read_file") + expect(toolUse.params.path).toBe("src/file.ts") + expect(toolUse.partial).toBe(false) + + const textContent = result[1] as TextContent + expect(textContent.type).toBe("text") + expect(textContent.content).toBe("Here's what I found in the file.") + expect(textContent.partial).toBe(true) + }) + + it("should parse multiple tool uses separated by text", () => { + const message = + "First file: src/file1.tsSecond file: src/file2.ts" + const result = parser(message) + + expect(result).toHaveLength(4) + + expect(result[0].type).toBe("text") + expect((result[0] as TextContent).content).toBe("First file:") + + expect(result[1].type).toBe("tool_use") + expect((result[1] as ToolUse).name).toBe("read_file") + expect((result[1] as ToolUse).params.path).toBe("src/file1.ts") + + expect(result[2].type).toBe("text") + expect((result[2] as TextContent).content).toBe("Second file:") + + expect(result[3].type).toBe("tool_use") + expect((result[3] as ToolUse).name).toBe("read_file") + expect((result[3] as ToolUse).params.path).toBe("src/file2.ts") + }) + }) + + describe("special cases", () => { + it("should handle the write_to_file tool with content that contains closing tags", () => { + const message = `src/file.ts + function example() { + // This has XML-like content: + return true; + } + 5` + + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("write_to_file") + expect(toolUse.params.path).toBe("src/file.ts") + expect(toolUse.params.line_count).toBe("5") + expect(toolUse.params.content).toContain("function example()") + expect(toolUse.params.content).toContain("// This has XML-like content: ") + expect(toolUse.params.content).toContain("return true;") + expect(toolUse.partial).toBe(false) + }) + + it("should handle empty messages", () => { + const message = "" + const result = parser(message) + + expect(result).toHaveLength(0) + }) + + it("should handle malformed tool use tags", () => { + const message = "This has a malformed tag" + const result = parser(message) + + expect(result).toHaveLength(1) + expect(result[0].type).toBe("text") + expect((result[0] as TextContent).content).toBe(message) + }) + + it("should handle tool use with no parameters", () => { + const message = "" + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("browser_action") + expect(Object.keys(toolUse.params).length).toBe(0) + expect(toolUse.partial).toBe(false) + }) + + it("should handle nested tool tags that aren't actually nested", () => { + const message = + "echo 'test.txt'" + + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("execute_command") + expect(toolUse.params.command).toBe("echo 'test.txt'") + expect(toolUse.partial).toBe(false) + }) + + it("should handle a tool use with a parameter containing XML-like content", () => { + const message = "
.*
src
" + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("search_files") + expect(toolUse.params.regex).toBe("
.*
") + expect(toolUse.params.path).toBe("src") + expect(toolUse.partial).toBe(false) + }) + + it("should handle consecutive tool uses without text in between", () => { + const message = + "file1.tsfile2.ts" + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(2) + + const toolUse1 = result[0] as ToolUse + expect(toolUse1.type).toBe("tool_use") + expect(toolUse1.name).toBe("read_file") + expect(toolUse1.params.path).toBe("file1.ts") + expect(toolUse1.partial).toBe(false) + + const toolUse2 = result[1] as ToolUse + expect(toolUse2.type).toBe("tool_use") + expect(toolUse2.name).toBe("read_file") + expect(toolUse2.params.path).toBe("file2.ts") + expect(toolUse2.partial).toBe(false) + }) + + it("should handle whitespace in parameters", () => { + const message = " src/file.ts " + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("read_file") + expect(toolUse.params.path).toBe("src/file.ts") + expect(toolUse.partial).toBe(false) + }) + + it("should handle multi-line parameters", () => { + const message = `file.ts + line 1 + line 2 + line 3 + 3` + const result = parser(message).filter((block) => !isEmptyTextContent(block)) + + expect(result).toHaveLength(1) + const toolUse = result[0] as ToolUse + expect(toolUse.type).toBe("tool_use") + expect(toolUse.name).toBe("write_to_file") + expect(toolUse.params.path).toBe("file.ts") + expect(toolUse.params.content).toContain("line 1") + expect(toolUse.params.content).toContain("line 2") + expect(toolUse.params.content).toContain("line 3") + expect(toolUse.params.line_count).toBe("3") + expect(toolUse.partial).toBe(false) + }) + + it("should handle a complex message with multiple content types", () => { + const message = `I'll help you with that task. + + src/index.ts + + Now let's modify the file: + + src/index.ts + // Updated content + console.log("Hello world"); + 2 + + Let's run the code: + + node src/index.ts` + + const result = parser(message) + + expect(result).toHaveLength(6) + + // First text block + expect(result[0].type).toBe("text") + expect((result[0] as TextContent).content).toBe("I'll help you with that task.") + + // First tool use (read_file) + expect(result[1].type).toBe("tool_use") + expect((result[1] as ToolUse).name).toBe("read_file") + + // Second text block + expect(result[2].type).toBe("text") + expect((result[2] as TextContent).content).toContain("Now let's modify the file:") + + // Second tool use (write_to_file) + expect(result[3].type).toBe("tool_use") + expect((result[3] as ToolUse).name).toBe("write_to_file") + + // Third text block + expect(result[4].type).toBe("text") + expect((result[4] as TextContent).content).toContain("Let's run the code:") + + // Third tool use (execute_command) + expect(result[5].type).toBe("tool_use") + expect((result[5] as ToolUse).name).toBe("execute_command") + }) + }) + }) +}) diff --git a/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts b/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts new file mode 100644 index 00000000000..bea161330b9 --- /dev/null +++ b/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts @@ -0,0 +1,109 @@ +// node --expose-gc --import tsx src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts + +import { performance } from "perf_hooks" +import { parseAssistantMessage as parseAssistantMessageV1 } from "../parseAssistantMessage" +import { parseAssistantMessageV2 } from "../parseAssistantMessageV2" + +const formatNumber = (num: number): string => { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +} + +const measureExecutionTime = (fn: Function, input: string, iterations: number = 1000): number => { + for (let i = 0; i < 10; i++) { + fn(input) + } + + const start = performance.now() + + for (let i = 0; i < iterations; i++) { + fn(input) + } + + const end = performance.now() + return (end - start) / iterations // Average time per iteration in ms. +} + +const measureMemoryUsage = ( + fn: Function, + input: string, + iterations: number = 100, +): { heapUsed: number; heapTotal: number } => { + if (global.gc) { + // Force garbage collection if available. + global.gc() + } else { + console.warn("No garbage collection hook! Run with --expose-gc for more accurate memory measurements.") + } + + const initialMemory = process.memoryUsage() + + for (let i = 0; i < iterations; i++) { + fn(input) + } + + const finalMemory = process.memoryUsage() + + return { + heapUsed: (finalMemory.heapUsed - initialMemory.heapUsed) / iterations, + heapTotal: (finalMemory.heapTotal - initialMemory.heapTotal) / iterations, + } +} + +const testCases = [ + { + name: "Simple text message", + input: "This is a simple text message without any tool uses.", + }, + { + name: "Message with a simple tool use", + input: "Let's read a file: src/file.ts", + }, + { + name: "Message with a complex tool use (write_to_file)", + input: "src/file.ts\nfunction example() {\n // This has XML-like content: \n return true;\n}\n5", + }, + { + name: "Message with multiple tool uses", + input: "First file: src/file1.ts\nSecond file: src/file2.ts\nLet's write a new file: src/file3.ts\nexport function newFunction() {\n return 'Hello world';\n}\n3", + }, + { + name: "Large message with repeated tool uses", + input: Array(50) + .fill( + 'src/file.ts\noutput.tsconsole.log("hello");1', + ) + .join("\n"), + }, +] + +const runBenchmark = () => { + const maxNameLength = testCases.reduce((max, testCase) => Math.max(max, testCase.name.length), 0) + const namePadding = maxNameLength + 2 + + console.log( + `| ${"Test Case".padEnd(namePadding)} | V1 Time (ms) | V2 Time (ms) | V1/V2 Ratio | V1 Heap (bytes) | V2 Heap (bytes) |`, + ) + console.log( + `| ${"-".repeat(namePadding)} | ------------ | ------------ | ----------- | ---------------- | ---------------- |`, + ) + + for (const testCase of testCases) { + const v1Time = measureExecutionTime(parseAssistantMessageV1, testCase.input) + const v2Time = measureExecutionTime(parseAssistantMessageV2, testCase.input) + const timeRatio = v1Time / v2Time + + const v1Memory = measureMemoryUsage(parseAssistantMessageV1, testCase.input) + const v2Memory = measureMemoryUsage(parseAssistantMessageV2, testCase.input) + + console.log( + `| ${testCase.name.padEnd(namePadding)} | ` + + `${v1Time.toFixed(4).padStart(12)} | ` + + `${v2Time.toFixed(4).padStart(12)} | ` + + `${timeRatio.toFixed(2).padStart(11)} | ` + + `${formatNumber(Math.round(v1Memory.heapUsed)).padStart(16)} | ` + + `${formatNumber(Math.round(v2Memory.heapUsed)).padStart(16)} |`, + ) + } +} + +runBenchmark() diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts index c53e88ed96b..72201b7722e 100644 --- a/src/core/assistant-message/index.ts +++ b/src/core/assistant-message/index.ts @@ -1 +1,2 @@ -export { type AssistantMessageContent, parseAssistantMessage } from "./parse-assistant-message" +export { type AssistantMessageContent, parseAssistantMessage } from "./parseAssistantMessage" +export { presentAssistantMessage } from "./presentAssistantMessage" diff --git a/src/core/assistant-message/parse-assistant-message.ts b/src/core/assistant-message/parseAssistantMessage.ts similarity index 73% rename from src/core/assistant-message/parse-assistant-message.ts rename to src/core/assistant-message/parseAssistantMessage.ts index 0cac4dfb989..f641cabdaa9 100644 --- a/src/core/assistant-message/parse-assistant-message.ts +++ b/src/core/assistant-message/parseAssistantMessage.ts @@ -3,7 +3,7 @@ import { toolNames, ToolName } from "../../schemas" export type AssistantMessageContent = TextContent | ToolUse -export function parseAssistantMessage(assistantMessage: string) { +export function parseAssistantMessage(assistantMessage: string): AssistantMessageContent[] { let contentBlocks: AssistantMessageContent[] = [] let currentTextContent: TextContent | undefined = undefined let currentTextContentStartIndex = 0 @@ -17,28 +17,28 @@ export function parseAssistantMessage(assistantMessage: string) { const char = assistantMessage[i] accumulator += char - // there should not be a param without a tool use + // There should not be a param without a tool use. if (currentToolUse && currentParamName) { const currentParamValue = accumulator.slice(currentParamValueStartIndex) const paramClosingTag = `` if (currentParamValue.endsWith(paramClosingTag)) { - // end of param value + // End of param value. currentToolUse.params[currentParamName] = currentParamValue.slice(0, -paramClosingTag.length).trim() currentParamName = undefined continue } else { - // partial param value is accumulating + // Partial param value is accumulating. continue } } - // no currentParamName + // No currentParamName. if (currentToolUse) { const currentToolValue = accumulator.slice(currentToolUseStartIndex) const toolUseClosingTag = `` if (currentToolValue.endsWith(toolUseClosingTag)) { - // end of a tool use + // End of a tool use. currentToolUse.partial = false contentBlocks.push(currentToolUse) currentToolUse = undefined @@ -47,23 +47,29 @@ export function parseAssistantMessage(assistantMessage: string) { const possibleParamOpeningTags = toolParamNames.map((name) => `<${name}>`) for (const paramOpeningTag of possibleParamOpeningTags) { if (accumulator.endsWith(paramOpeningTag)) { - // start of a new parameter + // Start of a new parameter. currentParamName = paramOpeningTag.slice(1, -1) as ToolParamName currentParamValueStartIndex = accumulator.length break } } - // there's no current param, and not starting a new param + // There's no current param, and not starting a new param. - // special case for write_to_file where file contents could contain the closing tag, in which case the param would have closed and we end up with the rest of the file contents here. To work around this, we get the string between the starting content tag and the LAST content tag. + // Special case for write_to_file where file contents could + // contain the closing tag, in which case the param would have + // closed and we end up with the rest of the file contents here. + // To work around this, we get the string between the starting + // ontent tag and the LAST content tag. const contentParamName: ToolParamName = "content" + if (currentToolUse.name === "write_to_file" && accumulator.endsWith(``)) { const toolContent = accumulator.slice(currentToolUseStartIndex) const contentStartTag = `<${contentParamName}>` const contentEndTag = `` const contentStartIndex = toolContent.indexOf(contentStartTag) + contentStartTag.length const contentEndIndex = toolContent.lastIndexOf(contentEndTag) + if (contentStartIndex !== -1 && contentEndIndex !== -1 && contentEndIndex > contentStartIndex) { currentToolUse.params[contentParamName] = toolContent .slice(contentStartIndex, contentEndIndex) @@ -71,32 +77,38 @@ export function parseAssistantMessage(assistantMessage: string) { } } - // partial tool value is accumulating + // Partial tool value is accumulating. continue } } - // no currentToolUse + // No currentToolUse. let didStartToolUse = false const possibleToolUseOpeningTags = toolNames.map((name) => `<${name}>`) + for (const toolUseOpeningTag of possibleToolUseOpeningTags) { if (accumulator.endsWith(toolUseOpeningTag)) { - // start of a new tool use + // Start of a new tool use. currentToolUse = { type: "tool_use", name: toolUseOpeningTag.slice(1, -1) as ToolName, params: {}, partial: true, } + currentToolUseStartIndex = accumulator.length - // this also indicates the end of the current text content + + // This also indicates the end of the current text content. if (currentTextContent) { currentTextContent.partial = false - // remove the partially accumulated tool use tag from the end of text (() + const toolParamOpenTags = new Map() + + for (const name of toolNames) { + toolUseOpenTags.set(`<${name}>`, name) + } + + for (const name of toolParamNames) { + toolParamOpenTags.set(`<${name}>`, name) + } + + const len = assistantMessage.length + + for (let i = 0; i < len; i++) { + const currentCharIndex = i + + // Parsing a tool parameter + if (currentToolUse && currentParamName) { + const closeTag = `` + // Check if the string *ending* at index `i` matches the closing tag + if ( + currentCharIndex >= closeTag.length - 1 && + assistantMessage.startsWith( + closeTag, + currentCharIndex - closeTag.length + 1, // Start checking from potential start of tag. + ) + ) { + // Found the closing tag for the parameter. + const value = assistantMessage + .slice( + currentParamValueStart, // Start after the opening tag. + currentCharIndex - closeTag.length + 1, // End before the closing tag. + ) + .trim() + currentToolUse.params[currentParamName] = value + currentParamName = undefined // Go back to parsing tool content. + // We don't continue loop here, need to check for tool close or other params at index i. + } else { + continue // Still inside param value, move to next char. + } + } + + // Parsing a tool use (but not a specific parameter). + if (currentToolUse && !currentParamName) { + // Ensure we are not inside a parameter already. + // Check if starting a new parameter. + let startedNewParam = false + + for (const [tag, paramName] of toolParamOpenTags.entries()) { + if ( + currentCharIndex >= tag.length - 1 && + assistantMessage.startsWith(tag, currentCharIndex - tag.length + 1) + ) { + currentParamName = paramName + currentParamValueStart = currentCharIndex + 1 // Value starts after the tag. + startedNewParam = true + break + } + } + + if (startedNewParam) { + continue // Handled start of param, move to next char. + } + + // Check if closing the current tool use. + const toolCloseTag = `` + + if ( + currentCharIndex >= toolCloseTag.length - 1 && + assistantMessage.startsWith(toolCloseTag, currentCharIndex - toolCloseTag.length + 1) + ) { + // End of the tool use found. + // Special handling for content params *before* finalizing the + // tool. + const toolContentSlice = assistantMessage.slice( + currentToolUseStart, // From after the tool opening tag. + currentCharIndex - toolCloseTag.length + 1, // To before the tool closing tag. + ) + + // Check if content parameter needs special handling + // (write_to_file/new_rule). + // This check is important if the closing tag was + // missed by the parameter parsing logic (e.g., if content is + // empty or parsing logic prioritizes tool close). + const contentParamName: ToolParamName = "content" + if ( + currentToolUse.name === "write_to_file" /* || currentToolUse.name === "new_rule" */ && + // !(contentParamName in currentToolUse.params) && // Only if not already parsed. + toolContentSlice.includes(`<${contentParamName}>`) // Check if tag exists. + ) { + const contentStartTag = `<${contentParamName}>` + const contentEndTag = `` + const contentStart = toolContentSlice.indexOf(contentStartTag) + + // Use `lastIndexOf` for robustness against nested tags. + const contentEnd = toolContentSlice.lastIndexOf(contentEndTag) + + if (contentStart !== -1 && contentEnd !== -1 && contentEnd > contentStart) { + const contentValue = toolContentSlice + .slice(contentStart + contentStartTag.length, contentEnd) + .trim() + + currentToolUse.params[contentParamName] = contentValue + } + } + + currentToolUse.partial = false // Mark as complete. + contentBlocks.push(currentToolUse) + currentToolUse = undefined // Reset state. + currentTextContentStart = currentCharIndex + 1 // Potential text starts after this tag. + continue // Move to next char. + } + + // If not starting a param and not closing the tool, continue + // accumulating tool content implicitly. + continue + } + + // Parsing text / looking for tool start. + if (!currentToolUse) { + // Check if starting a new tool use. + let startedNewTool = false + + for (const [tag, toolName] of toolUseOpenTags.entries()) { + if ( + currentCharIndex >= tag.length - 1 && + assistantMessage.startsWith(tag, currentCharIndex - tag.length + 1) + ) { + // End current text block if one was active. + if (currentTextContent) { + currentTextContent.content = assistantMessage + .slice( + currentTextContentStart, // From where text started. + currentCharIndex - tag.length + 1, // To before the tool tag starts. + ) + .trim() + + currentTextContent.partial = false // Ended because tool started. + + if (currentTextContent.content.length > 0) { + contentBlocks.push(currentTextContent) + } + + currentTextContent = undefined + } else { + // Check for any text between the last block and this tag. + const potentialText = assistantMessage + .slice( + currentTextContentStart, // From where text *might* have started. + currentCharIndex - tag.length + 1, // To before the tool tag starts. + ) + .trim() + + if (potentialText.length > 0) { + contentBlocks.push({ + type: "text", + content: potentialText, + partial: false, + }) + } + } + + // Start the new tool use. + currentToolUse = { + type: "tool_use", + name: toolName, + params: {}, + partial: true, // Assume partial until closing tag is found. + } + + currentToolUseStart = currentCharIndex + 1 // Tool content starts after the opening tag. + startedNewTool = true + + break + } + } + + if (startedNewTool) { + continue // Handled start of tool, move to next char. + } + + // If not starting a tool, it must be text content. + if (!currentTextContent) { + // Start a new text block if we aren't already in one. + currentTextContentStart = currentCharIndex // Text starts at the current character. + + // Check if the current char is the start of potential text *immediately* after a tag. + // This needs the previous state - simpler to let slicing handle it later. + // Resetting start index accurately is key. + // It should be the index *after* the last processed tag. + // The logic managing currentTextContentStart after closing tags handles this. + currentTextContent = { + type: "text", + content: "", // Will be determined by slicing at the end or when a tool starts + partial: true, + } + } + // Continue accumulating text implicitly; content is extracted later. + } + } + + // Finalize any open parameter within an open tool use. + if (currentToolUse && currentParamName) { + currentToolUse.params[currentParamName] = assistantMessage + .slice(currentParamValueStart) // From param start to end of string. + .trim() + // Tool use remains partial. + } + + // Finalize any open tool use (which might contain the finalized partial param). + if (currentToolUse) { + // Tool use is partial because the loop finished before its closing tag. + contentBlocks.push(currentToolUse) + } + // Finalize any trailing text content. + // Only possible if a tool use wasn't open at the very end. + else if (currentTextContent) { + currentTextContent.content = assistantMessage + .slice(currentTextContentStart) // From text start to end of string. + .trim() + + // Text is partial because the loop finished. + if (currentTextContent.content.length > 0) { + contentBlocks.push(currentTextContent) + } + } + + return contentBlocks +} diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts new file mode 100644 index 00000000000..ef2bb049630 --- /dev/null +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -0,0 +1,525 @@ +import cloneDeep from "clone-deep" +import { serializeError } from "serialize-error" + +import type { ToolName } from "../../schemas" + +import { defaultModeSlug, getModeBySlug } from "../../shared/modes" +import type { ToolParamName, ToolResponse } from "../../shared/tools" +import type { ClineAsk, ToolProgressStatus } from "../../shared/ExtensionMessage" + +import { telemetryService } from "../../services/telemetry/TelemetryService" + +import { fetchInstructionsTool } from "../tools/fetchInstructionsTool" +import { listFilesTool } from "../tools/listFilesTool" +import { readFileTool } from "../tools/readFileTool" +import { writeToFileTool } from "../tools/writeToFileTool" +import { applyDiffTool } from "../tools/applyDiffTool" +import { insertContentTool } from "../tools/insertContentTool" +import { searchAndReplaceTool } from "../tools/searchAndReplaceTool" +import { listCodeDefinitionNamesTool } from "../tools/listCodeDefinitionNamesTool" +import { searchFilesTool } from "../tools/searchFilesTool" +import { browserActionTool } from "../tools/browserActionTool" +import { executeCommandTool } from "../tools/executeCommandTool" +import { useMcpToolTool } from "../tools/useMcpToolTool" +import { accessMcpResourceTool } from "../tools/accessMcpResourceTool" +import { askFollowupQuestionTool } from "../tools/askFollowupQuestionTool" +import { switchModeTool } from "../tools/switchModeTool" +import { attemptCompletionTool } from "../tools/attemptCompletionTool" +import { newTaskTool } from "../tools/newTaskTool" + +import { checkpointSave } from "../checkpoints" + +import { formatResponse } from "../prompts/responses" +import { validateToolUse } from "../tools/validateToolUse" +import { Task } from "../task/Task" + +/** + * Processes and presents assistant message content to the user interface. + * + * This function is the core message handling system that: + * - Sequentially processes content blocks from the assistant's response. + * - Displays text content to the user. + * - Executes tool use requests with appropriate user approval. + * - Manages the flow of conversation by determining when to proceed to the next content block. + * - Coordinates file system checkpointing for modified files. + * - Controls the conversation state to determine when to continue to the next request. + * + * The function uses a locking mechanism to prevent concurrent execution and handles + * partial content blocks during streaming. It's designed to work with the streaming + * API response pattern, where content arrives incrementally and needs to be processed + * as it becomes available. + */ + +export async function presentAssistantMessage(cline: Task) { + if (cline.abort) { + throw new Error(`[Cline#presentAssistantMessage] task ${cline.taskId}.${cline.instanceId} aborted`) + } + + if (cline.presentAssistantMessageLocked) { + cline.presentAssistantMessageHasPendingUpdates = true + return + } + + cline.presentAssistantMessageLocked = true + cline.presentAssistantMessageHasPendingUpdates = false + + if (cline.currentStreamingContentIndex >= cline.assistantMessageContent.length) { + // This may happen if the last content block was completed before + // streaming could finish. If streaming is finished, and we're out of + // bounds then this means we already presented/executed the last + // content block and are ready to continue to next request. + if (cline.didCompleteReadingStream) { + cline.userMessageContentReady = true + } + + cline.presentAssistantMessageLocked = false + return + } + + const block = cloneDeep(cline.assistantMessageContent[cline.currentStreamingContentIndex]) // need to create copy bc while stream is updating the array, it could be updating the reference block properties too + + switch (block.type) { + case "text": { + if (cline.didRejectTool || cline.didAlreadyUseTool) { + break + } + + let content = block.content + + if (content) { + // Have to do this for partial and complete since sending + // content in thinking tags to markdown renderer will + // automatically be removed. + // Remove end substrings of (with optional line break + // after) and (with optional line break before). + // - Needs to be separate since we dont want to remove the line + // break before the first tag. + // - Needs to happen before the xml parsing below. + content = content.replace(/\s?/g, "") + content = content.replace(/\s?<\/thinking>/g, "") + + // Remove partial XML tag at the very end of the content (for + // tool use and thinking tags), Prevents scrollview from + // jumping when tags are automatically removed. + const lastOpenBracketIndex = content.lastIndexOf("<") + + if (lastOpenBracketIndex !== -1) { + const possibleTag = content.slice(lastOpenBracketIndex) + + // Check if there's a '>' after the last '<' (i.e., if the + // tag is complete) (complete thinking and tool tags will + // have been removed by now.) + const hasCloseBracket = possibleTag.includes(">") + + if (!hasCloseBracket) { + // Extract the potential tag name. + let tagContent: string + + if (possibleTag.startsWith(" { + switch (block.name) { + case "execute_command": + return `[${block.name} for '${block.params.command}']` + case "read_file": + return `[${block.name} for '${block.params.path}']` + case "fetch_instructions": + return `[${block.name} for '${block.params.task}']` + case "write_to_file": + return `[${block.name} for '${block.params.path}']` + case "apply_diff": + return `[${block.name} for '${block.params.path}']` + case "search_files": + return `[${block.name} for '${block.params.regex}'${ + block.params.file_pattern ? ` in '${block.params.file_pattern}'` : "" + }]` + case "insert_content": + return `[${block.name} for '${block.params.path}']` + case "search_and_replace": + return `[${block.name} for '${block.params.path}']` + case "list_files": + return `[${block.name} for '${block.params.path}']` + case "list_code_definition_names": + return `[${block.name} for '${block.params.path}']` + case "browser_action": + return `[${block.name} for '${block.params.action}']` + case "use_mcp_tool": + return `[${block.name} for '${block.params.server_name}']` + case "access_mcp_resource": + return `[${block.name} for '${block.params.server_name}']` + case "ask_followup_question": + return `[${block.name} for '${block.params.question}']` + case "attempt_completion": + return `[${block.name}]` + case "switch_mode": + return `[${block.name} to '${block.params.mode_slug}'${block.params.reason ? ` because: ${block.params.reason}` : ""}]` + case "new_task": { + const mode = block.params.mode ?? defaultModeSlug + const message = block.params.message ?? "(no message)" + const modeName = getModeBySlug(mode, customModes)?.name ?? mode + return `[${block.name} in ${modeName} mode: '${message}']` + } + } + } + + if (cline.didRejectTool) { + // Ignore any tool content after user has rejected tool once. + if (!block.partial) { + cline.userMessageContent.push({ + type: "text", + text: `Skipping tool ${toolDescription()} due to user rejecting a previous tool.`, + }) + } else { + // Partial tool after user rejected a previous tool. + cline.userMessageContent.push({ + type: "text", + text: `Tool ${toolDescription()} was interrupted and not executed due to user rejecting a previous tool.`, + }) + } + + break + } + + if (cline.didAlreadyUseTool) { + // Ignore any content after a tool has already been used. + cline.userMessageContent.push({ + type: "text", + text: `Tool [${block.name}] was not executed because a tool has already been used in this message. Only one tool may be used per message. You must assess the first tool's result before proceeding to use the next tool.`, + }) + + break + } + + const pushToolResult = (content: ToolResponse) => { + cline.userMessageContent.push({ type: "text", text: `${toolDescription()} Result:` }) + + if (typeof content === "string") { + cline.userMessageContent.push({ type: "text", text: content || "(tool did not return anything)" }) + } else { + cline.userMessageContent.push(...content) + } + + // Once a tool result has been collected, ignore all other tool + // uses since we should only ever present one tool result per + // message. + cline.didAlreadyUseTool = true + } + + const askApproval = async ( + type: ClineAsk, + partialMessage?: string, + progressStatus?: ToolProgressStatus, + ) => { + const { response, text, images } = await cline.ask(type, partialMessage, false, progressStatus) + + if (response !== "yesButtonClicked") { + // Handle both messageResponse and noButtonClicked with text. + if (text) { + await cline.say("user_feedback", text, images) + pushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images)) + } else { + pushToolResult(formatResponse.toolDenied()) + } + cline.didRejectTool = true + return false + } + + // Handle yesButtonClicked with text. + if (text) { + await cline.say("user_feedback", text, images) + pushToolResult(formatResponse.toolResult(formatResponse.toolApprovedWithFeedback(text), images)) + } + + return true + } + + const askFinishSubTaskApproval = async () => { + // Ask the user to approve this task has completed, and he has + // reviewed it, and we can declare task is finished and return + // control to the parent task to continue running the rest of + // the sub-tasks. + const toolMessage = JSON.stringify({ tool: "finishTask" }) + return await askApproval("tool", toolMessage) + } + + const handleError = async (action: string, error: Error) => { + const errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}` + + await cline.say( + "error", + `Error ${action}:\n${error.message ?? JSON.stringify(serializeError(error), null, 2)}`, + ) + + pushToolResult(formatResponse.toolError(errorString)) + } + + // If block is partial, remove partial closing tag so its not + // presented to user. + const removeClosingTag = (tag: ToolParamName, text?: string): string => { + if (!block.partial) { + return text || "" + } + + if (!text) { + return "" + } + + // This regex dynamically constructs a pattern to match the + // closing tag: + // - Optionally matches whitespace before the tag. + // - Matches '<' or ' `(?:${char})?`) + .join("")}$`, + "g", + ) + + return text.replace(tagRegex, "") + } + + if (block.name !== "browser_action") { + await cline.browserSession.closeBrowser() + } + + if (!block.partial) { + cline.recordToolUsage(block.name) + telemetryService.captureToolUsage(cline.taskId, block.name) + } + + // Validate tool use before execution. + const { mode, customModes } = (await cline.providerRef.deref()?.getState()) ?? {} + + try { + validateToolUse( + block.name as ToolName, + mode ?? defaultModeSlug, + customModes ?? [], + { apply_diff: cline.diffEnabled }, + block.params, + ) + } catch (error) { + cline.consecutiveMistakeCount++ + pushToolResult(formatResponse.toolError(error.message)) + break + } + + // Check for identical consecutive tool calls. + if (!block.partial) { + // Use the detector to check for repetition, passing the ToolUse + // block directly. + const repetitionCheck = cline.toolRepetitionDetector.check(block) + + // If execution is not allowed, notify user and break. + if (!repetitionCheck.allowExecution && repetitionCheck.askUser) { + // Handle repetition similar to mistake_limit_reached pattern. + const { response, text, images } = await cline.ask( + repetitionCheck.askUser.messageKey as ClineAsk, + repetitionCheck.askUser.messageDetail.replace("{toolName}", block.name), + ) + + if (response === "messageResponse") { + // Add user feedback to userContent. + cline.userMessageContent.push( + { + type: "text" as const, + text: `Tool repetition limit reached. User feedback: ${text}`, + }, + ...formatResponse.imageBlocks(images), + ) + + // Add user feedback to chat. + await cline.say("user_feedback", text, images) + + // Track tool repetition in telemetry. + telemetryService.captureConsecutiveMistakeError(cline.taskId) + } + + // Return tool result message about the repetition + pushToolResult( + formatResponse.toolError( + `Tool call repetition limit reached for ${block.name}. Please try a different approach.`, + ), + ) + break + } + } + + switch (block.name) { + case "write_to_file": + await writeToFileTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "apply_diff": + await applyDiffTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "insert_content": + await insertContentTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "search_and_replace": + await searchAndReplaceTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "read_file": + await readFileTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + + break + case "fetch_instructions": + await fetchInstructionsTool(cline, block, askApproval, handleError, pushToolResult) + break + case "list_files": + await listFilesTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "list_code_definition_names": + await listCodeDefinitionNamesTool( + cline, + block, + askApproval, + handleError, + pushToolResult, + removeClosingTag, + ) + break + case "search_files": + await searchFilesTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "browser_action": + await browserActionTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "execute_command": + await executeCommandTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "use_mcp_tool": + await useMcpToolTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "access_mcp_resource": + await accessMcpResourceTool( + cline, + block, + askApproval, + handleError, + pushToolResult, + removeClosingTag, + ) + break + case "ask_followup_question": + await askFollowupQuestionTool( + cline, + block, + askApproval, + handleError, + pushToolResult, + removeClosingTag, + ) + break + case "switch_mode": + await switchModeTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "new_task": + await newTaskTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag) + break + case "attempt_completion": + await attemptCompletionTool( + cline, + block, + askApproval, + handleError, + pushToolResult, + removeClosingTag, + toolDescription, + askFinishSubTaskApproval, + ) + break + } + + break + } + + const recentlyModifiedFiles = cline.fileContextTracker.getAndClearCheckpointPossibleFile() + + if (recentlyModifiedFiles.length > 0) { + // TODO: We can track what file changes were made and only + // checkpoint those files, this will be save storage. + await checkpointSave(cline) + } + + // Seeing out of bounds is fine, it means that the next too call is being + // built up and ready to add to assistantMessageContent to present. + // When you see the UI inactive during this, it means that a tool is + // breaking without presenting any UI. For example the write_to_file tool + // was breaking when relpath was undefined, and for invalid relpath it never + // presented UI. + // This needs to be placed here, if not then calling + // cline.presentAssistantMessage below would fail (sometimes) since it's + // locked. + cline.presentAssistantMessageLocked = false + + // NOTE: When tool is rejected, iterator stream is interrupted and it waits + // for `userMessageContentReady` to be true. Future calls to present will + // skip execution since `didRejectTool` and iterate until `contentIndex` is + // set to message length and it sets userMessageContentReady to true itself + // (instead of preemptively doing it in iterator). + if (!block.partial || cline.didRejectTool || cline.didAlreadyUseTool) { + // Block is finished streaming and executing. + if (cline.currentStreamingContentIndex === cline.assistantMessageContent.length - 1) { + // It's okay that we increment if !didCompleteReadingStream, it'll + // just return because out of bounds and as streaming continues it + // will call `presentAssitantMessage` if a new block is ready. If + // streaming is finished then we set `userMessageContentReady` to + // true when out of bounds. This gracefully allows the stream to + // continue on and all potential content blocks be presented. + // Last block is complete and it is finished executing + cline.userMessageContentReady = true // Will allow `pWaitFor` to continue. + } + + // Call next block if it exists (if not then read stream will call it + // when it's ready). + // Need to increment regardless, so when read stream calls this function + // again it will be streaming the next block. + cline.currentStreamingContentIndex++ + + if (cline.currentStreamingContentIndex < cline.assistantMessageContent.length) { + // There are already more content blocks to stream, so we'll call + // this function ourselves. + presentAssistantMessage(cline) + return + } + } + + // Block is partial, but the read stream may have finished. + if (cline.presentAssistantMessageHasPendingUpdates) { + presentAssistantMessage(cline) + } +} diff --git a/src/core/checkpoints/index.ts b/src/core/checkpoints/index.ts new file mode 100644 index 00000000000..3a5c2dde455 --- /dev/null +++ b/src/core/checkpoints/index.ts @@ -0,0 +1,296 @@ +import pWaitFor from "p-wait-for" +import * as vscode from "vscode" + +import { Task } from "../task/Task" + +import { getWorkspacePath } from "../../utils/path" + +import { ClineApiReqInfo } from "../../shared/ExtensionMessage" +import { getApiMetrics } from "../../shared/getApiMetrics" + +import { DIFF_VIEW_URI_SCHEME } from "../../integrations/editor/DiffViewProvider" + +import { telemetryService } from "../../services/telemetry/TelemetryService" +import { CheckpointServiceOptions, RepoPerTaskCheckpointService } from "../../services/checkpoints" + +export function getCheckpointService(cline: Task) { + if (!cline.enableCheckpoints) { + return undefined + } + + if (cline.checkpointService) { + return cline.checkpointService + } + + if (cline.checkpointServiceInitializing) { + console.log("[Cline#getCheckpointService] checkpoint service is still initializing") + return undefined + } + + const provider = cline.providerRef.deref() + + const log = (message: string) => { + console.log(message) + + try { + provider?.log(message) + } catch (err) { + // NO-OP + } + } + + console.log("[Cline#getCheckpointService] initializing checkpoints service") + + try { + const workspaceDir = getWorkspacePath() + + if (!workspaceDir) { + log("[Cline#getCheckpointService] workspace folder not found, disabling checkpoints") + cline.enableCheckpoints = false + return undefined + } + + const globalStorageDir = provider?.context.globalStorageUri.fsPath + + if (!globalStorageDir) { + log("[Cline#getCheckpointService] globalStorageDir not found, disabling checkpoints") + cline.enableCheckpoints = false + return undefined + } + + const options: CheckpointServiceOptions = { + taskId: cline.taskId, + workspaceDir, + shadowDir: globalStorageDir, + log, + } + + const service = RepoPerTaskCheckpointService.create(options) + + cline.checkpointServiceInitializing = true + + service.on("initialize", () => { + log("[Cline#getCheckpointService] service initialized") + + try { + const isCheckpointNeeded = + typeof cline.clineMessages.find(({ say }) => say === "checkpoint_saved") === "undefined" + + cline.checkpointService = service + cline.checkpointServiceInitializing = false + + if (isCheckpointNeeded) { + log("[Cline#getCheckpointService] no checkpoints found, saving initial checkpoint") + checkpointSave(cline) + } + } catch (err) { + log("[Cline#getCheckpointService] caught error in on('initialize'), disabling checkpoints") + cline.enableCheckpoints = false + } + }) + + service.on("checkpoint", ({ isFirst, fromHash: from, toHash: to }) => { + try { + provider?.postMessageToWebview({ type: "currentCheckpointUpdated", text: to }) + + cline + .say("checkpoint_saved", to, undefined, undefined, { isFirst, from, to }, undefined, { + isNonInteractive: true, + }) + .catch((err) => { + log("[Cline#getCheckpointService] caught unexpected error in say('checkpoint_saved')") + console.error(err) + }) + } catch (err) { + log("[Cline#getCheckpointService] caught unexpected error in on('checkpoint'), disabling checkpoints") + console.error(err) + cline.enableCheckpoints = false + } + }) + + log("[Cline#getCheckpointService] initializing shadow git") + + service.initShadowGit().catch((err) => { + log( + `[Cline#getCheckpointService] caught unexpected error in initShadowGit, disabling checkpoints (${err.message})`, + ) + + console.error(err) + cline.enableCheckpoints = false + }) + + return service + } catch (err) { + log("[Cline#getCheckpointService] caught unexpected error, disabling checkpoints") + cline.enableCheckpoints = false + return undefined + } +} + +async function getInitializedCheckpointService( + cline: Task, + { interval = 250, timeout = 15_000 }: { interval?: number; timeout?: number } = {}, +) { + const service = getCheckpointService(cline) + + if (!service || service.isInitialized) { + return service + } + + try { + await pWaitFor( + () => { + console.log("[Cline#getCheckpointService] waiting for service to initialize") + return service.isInitialized + }, + { interval, timeout }, + ) + + return service + } catch (err) { + return undefined + } +} + +export async function checkpointSave(cline: Task) { + const service = getCheckpointService(cline) + + if (!service) { + return + } + + if (!service.isInitialized) { + const provider = cline.providerRef.deref() + provider?.log("[checkpointSave] checkpoints didn't initialize in time, disabling checkpoints for this task") + cline.enableCheckpoints = false + return + } + + telemetryService.captureCheckpointCreated(cline.taskId) + + // Start the checkpoint process in the background. + return service.saveCheckpoint(`Task: ${cline.taskId}, Time: ${Date.now()}`).catch((err) => { + console.error("[Cline#checkpointSave] caught unexpected error, disabling checkpoints", err) + cline.enableCheckpoints = false + }) +} + +export type CheckpointRestoreOptions = { + ts: number + commitHash: string + mode: "preview" | "restore" +} + +export async function checkpointRestore(cline: Task, { ts, commitHash, mode }: CheckpointRestoreOptions) { + const service = await getInitializedCheckpointService(cline) + + if (!service) { + return + } + + const index = cline.clineMessages.findIndex((m) => m.ts === ts) + + if (index === -1) { + return + } + + const provider = cline.providerRef.deref() + + try { + await service.restoreCheckpoint(commitHash) + telemetryService.captureCheckpointRestored(cline.taskId) + await provider?.postMessageToWebview({ type: "currentCheckpointUpdated", text: commitHash }) + + if (mode === "restore") { + await cline.overwriteApiConversationHistory(cline.apiConversationHistory.filter((m) => !m.ts || m.ts < ts)) + + const deletedMessages = cline.clineMessages.slice(index + 1) + + const { totalTokensIn, totalTokensOut, totalCacheWrites, totalCacheReads, totalCost } = getApiMetrics( + cline.combineMessages(deletedMessages), + ) + + await cline.overwriteClineMessages(cline.clineMessages.slice(0, index + 1)) + + // TODO: Verify that this is working as expected. + await cline.say( + "api_req_deleted", + JSON.stringify({ + tokensIn: totalTokensIn, + tokensOut: totalTokensOut, + cacheWrites: totalCacheWrites, + cacheReads: totalCacheReads, + cost: totalCost, + } satisfies ClineApiReqInfo), + ) + } + + // The task is already cancelled by the provider beforehand, but we + // need to re-init to get the updated messages. + // + // This was take from Cline's implementation of the checkpoints + // feature. The cline instance will hang if we don't cancel twice, + // so this is currently necessary, but it seems like a complicated + // and hacky solution to a problem that I don't fully understand. + // I'd like to revisit this in the future and try to improve the + // task flow and the communication between the webview and the + // Cline instance. + provider?.cancelTask() + } catch (err) { + provider?.log("[checkpointRestore] disabling checkpoints for this task") + cline.enableCheckpoints = false + } +} + +export type CheckpointDiffOptions = { + ts: number + previousCommitHash?: string + commitHash: string + mode: "full" | "checkpoint" +} + +export async function checkpointDiff(cline: Task, { ts, previousCommitHash, commitHash, mode }: CheckpointDiffOptions) { + const service = await getInitializedCheckpointService(cline) + + if (!service) { + return + } + + telemetryService.captureCheckpointDiffed(cline.taskId) + + if (!previousCommitHash && mode === "checkpoint") { + const previousCheckpoint = cline.clineMessages + .filter(({ say }) => say === "checkpoint_saved") + .sort((a, b) => b.ts - a.ts) + .find((message) => message.ts < ts) + + previousCommitHash = previousCheckpoint?.text + } + + try { + const changes = await service.getDiff({ from: previousCommitHash, to: commitHash }) + + if (!changes?.length) { + vscode.window.showInformationMessage("No changes found.") + return + } + + await vscode.commands.executeCommand( + "vscode.changes", + mode === "full" ? "Changes since task started" : "Changes since previous checkpoint", + changes.map((change) => [ + vscode.Uri.file(change.paths.absolute), + vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${change.paths.relative}`).with({ + query: Buffer.from(change.content.before ?? "").toString("base64"), + }), + vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${change.paths.relative}`).with({ + query: Buffer.from(change.content.after ?? "").toString("base64"), + }), + ]), + ) + } catch (err) { + const provider = cline.providerRef.deref() + provider?.log("[checkpointDiff] disabling checkpoints for this task") + cline.enableCheckpoints = false + } +} diff --git a/src/core/condense/__tests__/index.test.ts b/src/core/condense/__tests__/index.test.ts new file mode 100644 index 00000000000..10769589f86 --- /dev/null +++ b/src/core/condense/__tests__/index.test.ts @@ -0,0 +1,228 @@ +import { describe, expect, it, jest, beforeEach } from "@jest/globals" +import { ApiHandler } from "../../../api" +import { ApiMessage } from "../../task-persistence/apiMessages" +import { maybeRemoveImageBlocks } from "../../../api/transform/image-cleaning" +import { summarizeConversation, getMessagesSinceLastSummary, N_MESSAGES_TO_KEEP } from "../index" + +// Mock dependencies +jest.mock("../../../api/transform/image-cleaning", () => ({ + maybeRemoveImageBlocks: jest.fn((messages: ApiMessage[], _apiHandler: ApiHandler) => [...messages]), +})) + +describe("getMessagesSinceLastSummary", () => { + it("should return all messages when there is no summary", () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + ] + + const result = getMessagesSinceLastSummary(messages) + expect(result).toEqual(messages) + }) + + it("should return messages since the last summary", () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "assistant", content: "Summary of conversation", ts: 3, isSummary: true }, + { role: "user", content: "How are you?", ts: 4 }, + { role: "assistant", content: "I'm good", ts: 5 }, + ] + + const result = getMessagesSinceLastSummary(messages) + expect(result).toEqual([ + { role: "assistant", content: "Summary of conversation", ts: 3, isSummary: true }, + { role: "user", content: "How are you?", ts: 4 }, + { role: "assistant", content: "I'm good", ts: 5 }, + ]) + }) + + it("should handle multiple summary messages and return since the last one", () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "First summary", ts: 2, isSummary: true }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "Second summary", ts: 4, isSummary: true }, + { role: "user", content: "What's new?", ts: 5 }, + ] + + const result = getMessagesSinceLastSummary(messages) + expect(result).toEqual([ + { role: "assistant", content: "Second summary", ts: 4, isSummary: true }, + { role: "user", content: "What's new?", ts: 5 }, + ]) + }) + + it("should handle empty messages array", () => { + const result = getMessagesSinceLastSummary([]) + expect(result).toEqual([]) + }) +}) + +describe("summarizeConversation", () => { + // Mock ApiHandler + let mockApiHandler: ApiHandler + let mockStream: AsyncGenerator + + beforeEach(() => { + // Reset mocks + jest.clearAllMocks() + + // Setup mock stream + mockStream = (async function* () { + yield { type: "text" as const, text: "This is " } + yield { type: "text" as const, text: "a summary" } + })() + + // Setup mock API handler + mockApiHandler = { + createMessage: jest.fn().mockReturnValue(mockStream), + countTokens: jest.fn().mockImplementation(() => Promise.resolve(100)), + getModel: jest.fn().mockReturnValue({ + id: "test-model", + info: { + contextWindow: 8000, + supportsImages: true, + supportsComputerUse: true, + supportsVision: true, + maxTokens: 4000, + supportsPromptCache: true, + maxCachePoints: 10, + minTokensPerCachePoint: 100, + cachableFields: ["system", "messages"], + }, + }), + } as unknown as ApiHandler + }) + + it("should not summarize when there are not enough messages", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + ] + + const result = await summarizeConversation(messages, mockApiHandler) + expect(result).toEqual(messages) + expect(mockApiHandler.createMessage).not.toHaveBeenCalled() + }) + + it("should not summarize when there was a recent summary", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6, isSummary: true }, // Recent summary + { role: "user", content: "Tell me more", ts: 7 }, + ] + + const result = await summarizeConversation(messages, mockApiHandler) + expect(result).toEqual(messages) + expect(mockApiHandler.createMessage).not.toHaveBeenCalled() + }) + + it("should summarize conversation and insert summary message", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + const result = await summarizeConversation(messages, mockApiHandler) + + // Check that the API was called correctly + expect(mockApiHandler.createMessage).toHaveBeenCalled() + expect(maybeRemoveImageBlocks).toHaveBeenCalled() + + // Verify the structure of the result + // The result should be: original messages (except last N) + summary + last N messages + expect(result.length).toBe(messages.length + 1) // Original + summary + + // Check that the summary message was inserted correctly + const summaryMessage = result[result.length - N_MESSAGES_TO_KEEP - 1] + expect(summaryMessage.role).toBe("assistant") + expect(summaryMessage.content).toBe("This is a summary") + expect(summaryMessage.isSummary).toBe(true) + + // Check that the last N_MESSAGES_TO_KEEP messages are preserved + const lastMessages = messages.slice(-N_MESSAGES_TO_KEEP) + expect(result.slice(-N_MESSAGES_TO_KEEP)).toEqual(lastMessages) + }) + + it("should handle empty summary response", async () => { + // We need enough messages to trigger summarization + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + // Mock console.warn before we call the function + const originalWarn = console.warn + const mockWarn = jest.fn() + console.warn = mockWarn + + // Setup empty summary response + const emptyStream = (async function* () { + yield { type: "text" as const, text: "" } + })() + + // Create a new mock for createMessage that returns empty stream + const createMessageMock = jest.fn().mockReturnValue(emptyStream) + mockApiHandler.createMessage = createMessageMock as any + + // We need to mock maybeRemoveImageBlocks to return the expected messages + ;(maybeRemoveImageBlocks as jest.Mock).mockImplementationOnce((messages: any) => { + return messages.map(({ role, content }: { role: string; content: any }) => ({ role, content })) + }) + + const result = await summarizeConversation(messages, mockApiHandler) + + // Should return original messages when summary is empty + expect(result).toEqual(messages) + expect(mockWarn).toHaveBeenCalledWith("Received empty summary from API") + + // Restore console.warn + console.warn = originalWarn + }) + + it("should correctly format the request to the API", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + await summarizeConversation(messages, mockApiHandler) + + // Verify the final request message + const expectedFinalMessage = { + role: "user", + content: "Summarize the conversation so far, as described in the prompt instructions.", + } + + // Verify that createMessage was called with the correct prompt + expect(mockApiHandler.createMessage).toHaveBeenCalledWith( + expect.stringContaining("Your task is to create a detailed summary of the conversation"), + expect.any(Array), + ) + + // Check that maybeRemoveImageBlocks was called with the correct messages + const mockCallArgs = (maybeRemoveImageBlocks as jest.Mock).mock.calls[0][0] as any[] + expect(mockCallArgs[mockCallArgs.length - 1]).toEqual(expectedFinalMessage) + }) +}) diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts new file mode 100644 index 00000000000..2a88dbfccee --- /dev/null +++ b/src/core/condense/index.ts @@ -0,0 +1,105 @@ +import Anthropic from "@anthropic-ai/sdk" +import { ApiHandler } from "../../api" +import { ApiMessage } from "../task-persistence/apiMessages" +import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning" + +export const N_MESSAGES_TO_KEEP = 3 + +const SUMMARY_PROMPT = `\ +Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions. +This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing with the conversation and supporting any continuing tasks. + +Your summary should be structured as follows: +Context: The context to continue the conversation with. If applicable based on the current task, this should include: + 1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user. This should be written to allow someone to be able to follow the general overarching conversation flow. + 2. Current Work: Describe in detail what was being worked on prior to this request to summarize the conversation. Pay special attention to the more recent messages in the conversation. + 3. Key Technical Concepts: List all important technical concepts, technologies, coding conventions, and frameworks discussed, which might be relevant for continuing with this work. + 4. Relevant Files and Code: If applicable, enumerate specific files and code sections examined, modified, or created for the task continuation. Pay special attention to the most recent messages and changes. + 5. Problem Solving: Document problems solved thus far and any ongoing troubleshooting efforts. + 6. Pending Tasks and Next Steps: Outline all pending tasks that you have explicitly been asked to work on, as well as list the next steps you will take for all outstanding work, if applicable. Include code snippets where they add clarity. For any next steps, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no information loss in context between tasks. + +Example summary structure: +1. Previous Conversation: + [Detailed description] +2. Current Work: + [Detailed description] +3. Key Technical Concepts: + - [Concept 1] + - [Concept 2] + - [...] +4. Relevant Files and Code: + - [File Name 1] + - [Summary of why this file is important] + - [Summary of the changes made to this file, if any] + - [Important Code Snippet] + - [File Name 2] + - [Important Code Snippet] + - [...] +5. Problem Solving: + [Detailed description] +6. Pending Tasks and Next Steps: + - [Task 1 details & next steps] + - [Task 2 details & next steps] + - [...] + +Output only the summary of the conversation so far, without any additional commentary or explanation. +` + +/** + * Summarizes the conversation messages using an LLM call + * + * @param {ApiMessage[]} messages - The conversation messages + * @param {ApiHandler} apiHandler - The API handler to use for token counting. + * @returns {ApiMessage[]} - The input messages, potentially including a new summary message before the last message. + */ +export async function summarizeConversation(messages: ApiMessage[], apiHandler: ApiHandler): Promise { + const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(0, -N_MESSAGES_TO_KEEP)) + if (messagesToSummarize.length <= 1) { + return messages // Not enough messages to warrant a summary + } + const keepMessages = messages.slice(-N_MESSAGES_TO_KEEP) + for (const message of keepMessages) { + if (message.isSummary) { + return messages // We recently summarized these messages; it's too soon to summarize again. + } + } + const finalRequestMessage: Anthropic.MessageParam = { + role: "user", + content: "Summarize the conversation so far, as described in the prompt instructions.", + } + const requestMessages = maybeRemoveImageBlocks([...messagesToSummarize, finalRequestMessage], apiHandler).map( + ({ role, content }) => ({ role, content }), + ) + // Note: this doesn't need to be a stream, consider using something like apiHandler.completePrompt + const stream = apiHandler.createMessage(SUMMARY_PROMPT, requestMessages) + let summary = "" + // TODO(canyon): compute usage and cost for this operation and update the global metrics. + for await (const chunk of stream) { + if (chunk.type === "text") { + summary += chunk.text + } + } + summary = summary.trim() + if (summary.length === 0) { + console.warn("Received empty summary from API") + return messages + } + const summaryMessage: ApiMessage = { + role: "assistant", + content: summary, + ts: keepMessages[0].ts, + isSummary: true, + } + + return [...messages.slice(0, -N_MESSAGES_TO_KEEP), summaryMessage, ...keepMessages] +} + +/* Returns the list of all messages since the last summary message, including the summary. Returns all messages if there is no summary. */ +export function getMessagesSinceLastSummary(messages: ApiMessage[]): ApiMessage[] { + let lastSummaryIndexReverse = [...messages].reverse().findIndex((message) => message.isSummary) + if (lastSummaryIndexReverse === -1) { + return messages + } + const lastSummaryIndex = messages.length - lastSummaryIndexReverse - 1 + return messages.slice(lastSummaryIndex) +} diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index ca53cba8892..d22a87e0974 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -1,11 +1,14 @@ import { ExtensionContext } from "vscode" import { z, ZodError } from "zod" -import { providerSettingsSchema, ApiConfigMeta } from "../../schemas" +import { providerSettingsSchema, ProviderSettingsEntry, providerSettingsSchemaDiscriminated } from "../../schemas" import { Mode, modes } from "../../shared/modes" import { telemetryService } from "../../services/telemetry/TelemetryService" const providerSettingsWithIdSchema = providerSettingsSchema.extend({ id: z.string().optional() }) +const discriminatedProviderSettingsWithIdSchema = providerSettingsSchemaDiscriminated.and( + z.object({ id: z.string().optional() }), +) type ProviderSettingsWithId = z.infer @@ -79,6 +82,18 @@ export class ProviderSettingsManager { let isDirty = false + // Migrate existing installs to have per-mode API config map + if (!providerProfiles.modeApiConfigs) { + // Use the currently selected config for all modes initially + const currentName = providerProfiles.currentApiConfigName + const seedId = + providerProfiles.apiConfigs[currentName]?.id ?? + Object.values(providerProfiles.apiConfigs)[0]?.id ?? + this.defaultConfigId + providerProfiles.modeApiConfigs = Object.fromEntries(modes.map((m) => [m.slug, seedId])) + isDirty = true + } + // Ensure all configs have IDs. for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) { if (!apiConfig.id) { @@ -211,7 +226,7 @@ export class ProviderSettingsManager { /** * List all available configs with metadata. */ - public async listConfig(): Promise { + public async listConfig(): Promise { try { return await this.lock(async () => { const providerProfiles = await this.load() @@ -232,66 +247,81 @@ export class ProviderSettingsManager { * Preserves the ID from the input 'config' object if it exists, * otherwise generates a new one (for creation scenarios). */ - public async saveConfig(name: string, config: ProviderSettingsWithId) { + public async saveConfig(name: string, config: ProviderSettingsWithId): Promise { try { return await this.lock(async () => { const providerProfiles = await this.load() // Preserve the existing ID if this is an update to an existing config. const existingId = providerProfiles.apiConfigs[name]?.id - providerProfiles.apiConfigs[name] = { ...config, id: config.id || existingId || this.generateId() } + const id = config.id || existingId || this.generateId() + + // Filter out settings from other providers. + const filteredConfig = providerSettingsSchemaDiscriminated.parse(config) + providerProfiles.apiConfigs[name] = { ...filteredConfig, id } await this.store(providerProfiles) + return id }) } catch (error) { throw new Error(`Failed to save config: ${error}`) } } - /** - * Load a config by name and set it as the current config. - */ - public async loadConfig(name: string) { + public async getProfile( + params: { name: string } | { id: string }, + ): Promise { try { return await this.lock(async () => { const providerProfiles = await this.load() - const providerSettings = providerProfiles.apiConfigs[name] + let name: string + let providerSettings: ProviderSettingsWithId - if (!providerSettings) { - throw new Error(`Config '${name}' not found`) - } + if ("name" in params) { + name = params.name - providerProfiles.currentApiConfigName = name - await this.store(providerProfiles) + if (!providerProfiles.apiConfigs[name]) { + throw new Error(`Config with name '${name}' not found`) + } + + providerSettings = providerProfiles.apiConfigs[name] + } else { + const id = params.id + + const entry = Object.entries(providerProfiles.apiConfigs).find( + ([_, apiConfig]) => apiConfig.id === id, + ) - return providerSettings + if (!entry) { + throw new Error(`Config with ID '${id}' not found`) + } + + name = entry[0] + providerSettings = entry[1] + } + + return { name, ...providerSettings } }) } catch (error) { - throw new Error(`Failed to load config: ${error}`) + throw new Error(`Failed to get profile: ${error instanceof Error ? error.message : error}`) } } /** - * Load a config by ID and set it as the current config. + * Activate a profile by name or ID. */ - public async loadConfigById(id: string) { + public async activateProfile( + params: { name: string } | { id: string }, + ): Promise { + const { name, ...providerSettings } = await this.getProfile(params) + try { return await this.lock(async () => { const providerProfiles = await this.load() - const providerSettings = Object.entries(providerProfiles.apiConfigs).find( - ([_, apiConfig]) => apiConfig.id === id, - ) - - if (!providerSettings) { - throw new Error(`Config with ID '${id}' not found`) - } - - const [name, apiConfig] = providerSettings providerProfiles.currentApiConfigName = name await this.store(providerProfiles) - - return { config: apiConfig, name } + return { name, ...providerSettings } }) } catch (error) { - throw new Error(`Failed to load config by ID: ${error}`) + throw new Error(`Failed to activate profile: ${error instanceof Error ? error.message : error}`) } } @@ -340,8 +370,12 @@ export class ProviderSettingsManager { try { return await this.lock(async () => { const providerProfiles = await this.load() - const { modeApiConfigs = {} } = providerProfiles - modeApiConfigs[mode] = configId + // Ensure the per-mode config map exists + if (!providerProfiles.modeApiConfigs) { + providerProfiles.modeApiConfigs = {} + } + // Assign the chosen config ID to this mode + providerProfiles.modeApiConfigs[mode] = configId await this.store(providerProfiles) }) } catch (error) { @@ -365,7 +399,15 @@ export class ProviderSettingsManager { public async export() { try { - return await this.lock(async () => providerProfilesSchema.parse(await this.load())) + return await this.lock(async () => { + const profiles = providerProfilesSchema.parse(await this.load()) + const configs = profiles.apiConfigs + for (const name in configs) { + // Avoid leaking properties from other providers. + configs[name] = discriminatedProviderSettingsWithIdSchema.parse(configs[name]) + } + return profiles + }) } catch (error) { throw new Error(`Failed to export provider profiles: ${error}`) } diff --git a/src/core/config/__tests__/ProviderSettingsManager.test.ts b/src/core/config/__tests__/ProviderSettingsManager.test.ts index 3cacc4c8b72..7e505440958 100644 --- a/src/core/config/__tests__/ProviderSettingsManager.test.ts +++ b/src/core/config/__tests__/ProviderSettingsManager.test.ts @@ -53,6 +53,7 @@ describe("ProviderSettingsManager", () => { fuzzyMatchThreshold: 1.0, }, }, + modeApiConfigs: {}, migrations: { rateLimitSecondsMigrated: true, diffSettingsMigrated: true, @@ -220,8 +221,9 @@ describe("ProviderSettingsManager", () => { ) const newConfig: ProviderSettings = { - apiProvider: "anthropic", - apiKey: "test-key", + apiProvider: "vertex", + apiModelId: "gemini-2.5-flash-preview-04-17", + vertexKeyFile: "test-key-file", } await providerSettingsManager.saveConfig("test", newConfig) @@ -246,10 +248,58 @@ describe("ProviderSettingsManager", () => { }, } - expect(mockSecrets.store).toHaveBeenCalledWith( - "roo_cline_config_api_config", - JSON.stringify(expectedConfig, null, 2), + expect(mockSecrets.store.mock.calls[0][0]).toEqual("roo_cline_config_api_config") + expect(storedConfig).toEqual(expectedConfig) + }) + + it("should only save provider relevant settings", async () => { + mockSecrets.get.mockResolvedValue( + JSON.stringify({ + currentApiConfigName: "default", + apiConfigs: { + default: {}, + }, + modeApiConfigs: { + code: "default", + architect: "default", + ask: "default", + }, + }), ) + + const newConfig: ProviderSettings = { + apiProvider: "anthropic", + apiKey: "test-key", + } + const newConfigWithExtra: ProviderSettings = { + ...newConfig, + openRouterApiKey: "another-key", + } + + await providerSettingsManager.saveConfig("test", newConfigWithExtra) + + // Get the actual stored config to check the generated ID + const storedConfig = JSON.parse(mockSecrets.store.mock.lastCall[1]) + const testConfigId = storedConfig.apiConfigs.test.id + + const expectedConfig = { + currentApiConfigName: "default", + apiConfigs: { + default: {}, + test: { + ...newConfig, + id: testConfigId, + }, + }, + modeApiConfigs: { + code: "default", + architect: "default", + ask: "default", + }, + } + + expect(mockSecrets.store.mock.calls[0][0]).toEqual("roo_cline_config_api_config") + expect(storedConfig).toEqual(expectedConfig) }) it("should update existing config", async () => { @@ -290,10 +340,9 @@ describe("ProviderSettingsManager", () => { }, } - expect(mockSecrets.store).toHaveBeenCalledWith( - "roo_cline_config_api_config", - JSON.stringify(expectedConfig, null, 2), - ) + const storedConfig = JSON.parse(mockSecrets.store.mock.lastCall[1]) + expect(mockSecrets.store.mock.lastCall[0]).toEqual("roo_cline_config_api_config") + expect(storedConfig).toEqual(expectedConfig) }) it("should throw error if secrets storage fails", async () => { @@ -390,17 +439,15 @@ describe("ProviderSettingsManager", () => { mockGlobalState.get.mockResolvedValue(42) mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig)) - const config = await providerSettingsManager.loadConfig("test") + const { name, ...providerSettings } = await providerSettingsManager.activateProfile({ name: "test" }) - expect(config).toEqual({ - apiProvider: "anthropic", - apiKey: "test-key", - id: "test-id", - }) + expect(name).toBe("test") + expect(providerSettings).toEqual({ apiProvider: "anthropic", apiKey: "test-key", id: "test-id" }) - // Get the stored config to check the structure + // Get the stored config to check the structure. const storedConfig = JSON.parse(mockSecrets.store.mock.calls[1][1]) expect(storedConfig.currentApiConfigName).toBe("test") + expect(storedConfig.apiConfigs.test).toEqual({ apiProvider: "anthropic", apiKey: "test-key", @@ -412,17 +459,12 @@ describe("ProviderSettingsManager", () => { mockSecrets.get.mockResolvedValue( JSON.stringify({ currentApiConfigName: "default", - apiConfigs: { - default: { - config: {}, - id: "default", - }, - }, + apiConfigs: { default: { config: {}, id: "default" } }, }), ) - await expect(providerSettingsManager.loadConfig("nonexistent")).rejects.toThrow( - "Config 'nonexistent' not found", + await expect(providerSettingsManager.activateProfile({ name: "nonexistent" })).rejects.toThrow( + "Config with name 'nonexistent' not found", ) }) @@ -430,20 +472,13 @@ describe("ProviderSettingsManager", () => { mockSecrets.get.mockResolvedValue( JSON.stringify({ currentApiConfigName: "default", - apiConfigs: { - test: { - config: { - apiProvider: "anthropic", - }, - id: "test-id", - }, - }, + apiConfigs: { test: { config: { apiProvider: "anthropic" }, id: "test-id" } }, }), ) mockSecrets.store.mockRejectedValueOnce(new Error("Storage failed")) - await expect(providerSettingsManager.loadConfig("test")).rejects.toThrow( - "Failed to load config: Error: Failed to write provider profiles to secrets: Error: Storage failed", + await expect(providerSettingsManager.activateProfile({ name: "test" })).rejects.toThrow( + "Failed to activate profile: Failed to write provider profiles to secrets: Error: Storage failed", ) }) @@ -493,12 +528,7 @@ describe("ProviderSettingsManager", () => { mockSecrets.get.mockResolvedValue( JSON.stringify({ currentApiConfigName: "test", - apiConfigs: { - test: { - apiProvider: "anthropic", - id: "test-id", - }, - }, + apiConfigs: { test: { apiProvider: "anthropic", id: "test-id" } }, }), ) @@ -513,18 +543,8 @@ describe("ProviderSettingsManager", () => { it("should return true for existing config", async () => { const existingConfig: ProviderProfiles = { currentApiConfigName: "default", - apiConfigs: { - default: { - id: "default", - }, - test: { - apiProvider: "anthropic", - id: "test-id", - }, - }, - migrations: { - rateLimitSecondsMigrated: false, - }, + apiConfigs: { default: { id: "default" }, test: { apiProvider: "anthropic", id: "test-id" } }, + migrations: { rateLimitSecondsMigrated: false }, } mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig)) @@ -535,10 +555,7 @@ describe("ProviderSettingsManager", () => { it("should return false for non-existent config", async () => { mockSecrets.get.mockResolvedValue( - JSON.stringify({ - currentApiConfigName: "default", - apiConfigs: { default: {} }, - }), + JSON.stringify({ currentApiConfigName: "default", apiConfigs: { default: {} } }), ) const hasConfig = await providerSettingsManager.hasConfig("nonexistent") diff --git a/src/core/diff/strategies/__tests__/multi-search-replace.test.ts b/src/core/diff/strategies/__tests__/multi-search-replace.test.ts index 37edcccb62f..dc971d3a10c 100644 --- a/src/core/diff/strategies/__tests__/multi-search-replace.test.ts +++ b/src/core/diff/strategies/__tests__/multi-search-replace.test.ts @@ -2332,6 +2332,54 @@ function two() { function three() { return "three"; +}`) + } + }) + + it("should deduce start_line when include line number in search and replace content", async () => { + const originalContent = ` +function one() { + return 1; +} + +function process() { + return "target"; +} + +function process() { + return "target"; +} + +function two() { + return 2; +} +`.trim() + const diffContent = `test.ts +<<<<<<< SEARCH +9 | function process() { +10 | return "target"; +======= +9 | function process2() { +10 | return "target222"; +>>>>>>> REPLACE` + + const result = await strategy.applyDiff(originalContent, diffContent) + expect(result.success).toBe(true) + if (result.success) { + expect(result.content).toBe(`function one() { + return 1; +} + +function process() { + return "target"; +} + +function process2() { + return "target222"; +} + +function two() { + return 2; }`) } }) diff --git a/src/core/diff/strategies/multi-search-replace.ts b/src/core/diff/strategies/multi-search-replace.ts index 7c07f06ba0b..144b794f758 100644 --- a/src/core/diff/strategies/multi-search-replace.ts +++ b/src/core/diff/strategies/multi-search-replace.ts @@ -380,6 +380,10 @@ Only use a single line of '=======' between search and replacement content, beca (everyLineHasLineNumbers(searchContent) && everyLineHasLineNumbers(replaceContent)) || (everyLineHasLineNumbers(searchContent) && replaceContent.trim() === "") + if (hasAllLineNumbers && startLine === 0) { + startLine = parseInt(searchContent.split("\n")[0].split("|")[0]) + } + if (hasAllLineNumbers) { searchContent = stripLineNumbers(searchContent) replaceContent = stripLineNumbers(replaceContent) diff --git a/src/core/environment/__tests__/getEnvironmentDetails.test.ts b/src/core/environment/__tests__/getEnvironmentDetails.test.ts new file mode 100644 index 00000000000..008b0de14e2 --- /dev/null +++ b/src/core/environment/__tests__/getEnvironmentDetails.test.ts @@ -0,0 +1,316 @@ +// npx jest src/core/environment/__tests__/getEnvironmentDetails.test.ts + +import pWaitFor from "p-wait-for" +import delay from "delay" + +import { getEnvironmentDetails } from "../getEnvironmentDetails" +import { EXPERIMENT_IDS, experiments } from "../../../shared/experiments" +import { defaultModeSlug, getFullModeDetails, getModeBySlug, isToolAllowedForMode } from "../../../shared/modes" +import { getApiMetrics } from "../../../shared/getApiMetrics" +import { listFiles } from "../../../services/glob/list-files" +import { TerminalRegistry } from "../../../integrations/terminal/TerminalRegistry" +import { Terminal } from "../../../integrations/terminal/Terminal" +import { arePathsEqual } from "../../../utils/path" +import { FileContextTracker } from "../../context-tracking/FileContextTracker" +import { ApiHandler } from "../../../api/index" +import { ClineProvider } from "../../webview/ClineProvider" +import { RooIgnoreController } from "../../ignore/RooIgnoreController" +import { formatResponse } from "../../prompts/responses" +import { Task } from "../../task/Task" + +jest.mock("vscode", () => ({ + window: { + tabGroups: { all: [], onDidChangeTabs: jest.fn() }, + visibleTextEditors: [], + }, + env: { + language: "en-US", + }, +})) + +jest.mock("p-wait-for") + +jest.mock("delay") + +jest.mock("execa", () => ({ + execa: jest.fn(), +})) + +jest.mock("../../../shared/experiments") +jest.mock("../../../shared/modes") +jest.mock("../../../shared/getApiMetrics") +jest.mock("../../../services/glob/list-files") +jest.mock("../../../integrations/terminal/TerminalRegistry") +jest.mock("../../../integrations/terminal/Terminal") +jest.mock("../../../utils/path") +jest.mock("../../prompts/responses") + +describe("getEnvironmentDetails", () => { + const mockCwd = "/test/path" + const mockTaskId = "test-task-id" + + type MockTerminal = { + id: string + getLastCommand: jest.Mock + getProcessesWithOutput: jest.Mock + cleanCompletedProcessQueue?: jest.Mock + } + + let mockCline: Partial + let mockProvider: any + let mockState: any + + beforeEach(() => { + jest.clearAllMocks() + + mockState = { + terminalOutputLineLimit: 100, + maxWorkspaceFiles: 50, + maxOpenTabsContext: 10, + mode: "code", + customModes: [], + experiments: {}, + customInstructions: "test instructions", + language: "en", + showRooIgnoredFiles: true, + } + + mockProvider = { + getState: jest.fn().mockResolvedValue(mockState), + } + + mockCline = { + cwd: mockCwd, + taskId: mockTaskId, + didEditFile: false, + fileContextTracker: { + getAndClearRecentlyModifiedFiles: jest.fn().mockReturnValue([]), + } as unknown as FileContextTracker, + rooIgnoreController: { + filterPaths: jest.fn((paths: string[]) => paths.join("\n")), + cwd: mockCwd, + ignoreInstance: {}, + disposables: [], + rooIgnoreContent: "", + isPathIgnored: jest.fn(), + getIgnoreContent: jest.fn(), + updateIgnoreContent: jest.fn(), + addToIgnore: jest.fn(), + removeFromIgnore: jest.fn(), + dispose: jest.fn(), + } as unknown as RooIgnoreController, + clineMessages: [], + api: { + getModel: jest.fn().mockReturnValue({ id: "test-model", info: { contextWindow: 100000 } }), + createMessage: jest.fn(), + countTokens: jest.fn(), + } as unknown as ApiHandler, + diffEnabled: true, + providerRef: { + deref: jest.fn().mockReturnValue(mockProvider), + [Symbol.toStringTag]: "WeakRef", + } as unknown as WeakRef, + } + + // Mock other dependencies. + ;(getApiMetrics as jest.Mock).mockReturnValue({ contextTokens: 50000, totalCost: 0.25 }) + ;(getFullModeDetails as jest.Mock).mockResolvedValue({ + name: "💻 Code", + roleDefinition: "You are a code assistant", + customInstructions: "Custom instructions", + }) + ;(isToolAllowedForMode as jest.Mock).mockReturnValue(true) + ;(listFiles as jest.Mock).mockResolvedValue([["file1.ts", "file2.ts"], false]) + ;(formatResponse.formatFilesList as jest.Mock).mockReturnValue("file1.ts\nfile2.ts") + ;(arePathsEqual as jest.Mock).mockReturnValue(false) + ;(Terminal.compressTerminalOutput as jest.Mock).mockImplementation((output: string) => output) + ;(TerminalRegistry.getTerminals as jest.Mock).mockReturnValue([]) + ;(TerminalRegistry.getBackgroundTerminals as jest.Mock).mockReturnValue([]) + ;(TerminalRegistry.isProcessHot as jest.Mock).mockReturnValue(false) + ;(TerminalRegistry.getUnretrievedOutput as jest.Mock).mockReturnValue("") + ;(pWaitFor as unknown as jest.Mock).mockResolvedValue(undefined) + ;(delay as jest.Mock).mockResolvedValue(undefined) + }) + + it("should return basic environment details", async () => { + const result = await getEnvironmentDetails(mockCline as Task) + + expect(result).toContain("") + expect(result).toContain("") + expect(result).toContain("# VSCode Visible Files") + expect(result).toContain("# VSCode Open Tabs") + expect(result).toContain("# Current Time") + expect(result).toContain("# Current Context Size (Tokens)") + expect(result).toContain("# Current Cost") + expect(result).toContain("# Current Mode") + expect(result).toContain("test-model") + + expect(mockProvider.getState).toHaveBeenCalled() + + expect(getFullModeDetails).toHaveBeenCalledWith("code", [], undefined, { + cwd: mockCwd, + globalCustomInstructions: "test instructions", + language: "en", + }) + + expect(getApiMetrics).toHaveBeenCalledWith(mockCline.clineMessages) + }) + + it("should include file details when includeFileDetails is true", async () => { + const result = await getEnvironmentDetails(mockCline as Task, true) + expect(result).toContain("# Current Workspace Directory") + expect(result).toContain("Files") + + expect(listFiles).toHaveBeenCalledWith(mockCwd, true, 50) + + expect(formatResponse.formatFilesList).toHaveBeenCalledWith( + mockCwd, + ["file1.ts", "file2.ts"], + false, + mockCline.rooIgnoreController, + true, + ) + }) + + it("should not include file details when includeFileDetails is false", async () => { + await getEnvironmentDetails(mockCline as Task, false) + expect(listFiles).not.toHaveBeenCalled() + expect(formatResponse.formatFilesList).not.toHaveBeenCalled() + }) + + it("should handle desktop directory specially", async () => { + ;(arePathsEqual as jest.Mock).mockReturnValue(true) + const result = await getEnvironmentDetails(mockCline as Task, true) + expect(result).toContain("Desktop files not shown automatically") + expect(listFiles).not.toHaveBeenCalled() + }) + + it("should include recently modified files if any", async () => { + ;(mockCline.fileContextTracker!.getAndClearRecentlyModifiedFiles as jest.Mock).mockReturnValue([ + "modified1.ts", + "modified2.ts", + ]) + + const result = await getEnvironmentDetails(mockCline as Task) + + expect(result).toContain("# Recently Modified Files") + expect(result).toContain("modified1.ts") + expect(result).toContain("modified2.ts") + }) + + it("should include active terminal information", async () => { + const mockActiveTerminal = { + id: "terminal-1", + getLastCommand: jest.fn().mockReturnValue("npm test"), + getProcessesWithOutput: jest.fn().mockReturnValue([]), + } as MockTerminal + + ;(TerminalRegistry.getTerminals as jest.Mock).mockReturnValue([mockActiveTerminal]) + ;(TerminalRegistry.getUnretrievedOutput as jest.Mock).mockReturnValue("Test output") + + const result = await getEnvironmentDetails(mockCline as Task) + + expect(result).toContain("# Actively Running Terminals") + expect(result).toContain("Original command: `npm test`") + expect(result).toContain("Test output") + + mockCline.didEditFile = true + await getEnvironmentDetails(mockCline as Task) + expect(delay).toHaveBeenCalledWith(300) + + expect(pWaitFor).toHaveBeenCalled() + }) + + it("should include inactive terminals with output", async () => { + const mockProcess = { + command: "npm build", + getUnretrievedOutput: jest.fn().mockReturnValue("Build output"), + } + + const mockInactiveTerminal = { + id: "terminal-2", + getProcessesWithOutput: jest.fn().mockReturnValue([mockProcess]), + cleanCompletedProcessQueue: jest.fn(), + } as MockTerminal + + ;(TerminalRegistry.getTerminals as jest.Mock).mockImplementation((active: boolean) => + active ? [] : [mockInactiveTerminal], + ) + + const result = await getEnvironmentDetails(mockCline as Task) + + expect(result).toContain("# Inactive Terminals with Completed Process Output") + expect(result).toContain("Terminal terminal-2") + expect(result).toContain("Command: `npm build`") + expect(result).toContain("Build output") + + expect(mockInactiveTerminal.cleanCompletedProcessQueue).toHaveBeenCalled() + }) + + it("should include warning when file writing is not allowed", async () => { + ;(isToolAllowedForMode as jest.Mock).mockReturnValue(false) + ;(getModeBySlug as jest.Mock).mockImplementation((slug: string) => { + if (slug === "code") { + return { name: "💻 Code" } + } + + if (slug === defaultModeSlug) { + return { name: "Default Mode" } + } + + return null + }) + + const result = await getEnvironmentDetails(mockCline as Task) + + expect(result).toContain("NOTE: You are currently in '💻 Code' mode, which does not allow write operations") + }) + + it("should include experiment-specific details when Power Steering is enabled", async () => { + mockState.experiments = { [EXPERIMENT_IDS.POWER_STEERING]: true } + ;(experiments.isEnabled as jest.Mock).mockReturnValue(true) + + const result = await getEnvironmentDetails(mockCline as Task) + + expect(result).toContain("You are a code assistant") + expect(result).toContain("Custom instructions") + }) + + it("should handle missing provider or state", async () => { + // Mock provider to return null. + mockCline.providerRef!.deref = jest.fn().mockReturnValue(null) + + const result = await getEnvironmentDetails(mockCline as Task) + + // Verify the function still returns a result. + expect(result).toContain("") + expect(result).toContain("") + + // Mock provider to return null state. + mockCline.providerRef!.deref = jest.fn().mockReturnValue({ + getState: jest.fn().mockResolvedValue(null), + }) + + const result2 = await getEnvironmentDetails(mockCline as Task) + + // Verify the function still returns a result. + expect(result2).toContain("") + expect(result2).toContain("") + }) + + it("should handle errors gracefully", async () => { + ;(pWaitFor as unknown as jest.Mock).mockRejectedValue(new Error("Test error")) + + const mockErrorTerminal = { + id: "terminal-1", + getLastCommand: jest.fn().mockReturnValue("npm test"), + getProcessesWithOutput: jest.fn().mockReturnValue([]), + } as MockTerminal + + ;(TerminalRegistry.getTerminals as jest.Mock).mockReturnValue([mockErrorTerminal]) + ;(TerminalRegistry.getBackgroundTerminals as jest.Mock).mockReturnValue([]) + ;(mockCline.fileContextTracker!.getAndClearRecentlyModifiedFiles as jest.Mock).mockReturnValue([]) + + await expect(getEnvironmentDetails(mockCline as Task)).resolves.not.toThrow() + }) +}) diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts new file mode 100644 index 00000000000..3d8a9cdbc3a --- /dev/null +++ b/src/core/environment/getEnvironmentDetails.ts @@ -0,0 +1,269 @@ +import path from "path" +import os from "os" + +import * as vscode from "vscode" +import pWaitFor from "p-wait-for" +import delay from "delay" + +import { EXPERIMENT_IDS, experiments as Experiments, ExperimentId } from "../../shared/experiments" +import { formatLanguage } from "../../shared/language" +import { defaultModeSlug, getFullModeDetails, getModeBySlug, isToolAllowedForMode } from "../../shared/modes" +import { getApiMetrics } from "../../shared/getApiMetrics" +import { listFiles } from "../../services/glob/list-files" +import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry" +import { Terminal } from "../../integrations/terminal/Terminal" +import { arePathsEqual } from "../../utils/path" +import { formatResponse } from "../prompts/responses" + +import { Task } from "../task/Task" + +export async function getEnvironmentDetails(cline: Task, includeFileDetails: boolean = false) { + let details = "" + + const clineProvider = cline.providerRef.deref() + const state = await clineProvider?.getState() + const { terminalOutputLineLimit = 500, maxWorkspaceFiles = 200 } = state ?? {} + + // It could be useful for cline to know if the user went from one or no + // file to another between messages, so we always include this context. + details += "\n\n# VSCode Visible Files" + + const visibleFilePaths = vscode.window.visibleTextEditors + ?.map((editor) => editor.document?.uri?.fsPath) + .filter(Boolean) + .map((absolutePath) => path.relative(cline.cwd, absolutePath)) + .slice(0, maxWorkspaceFiles) + + // Filter paths through rooIgnoreController + const allowedVisibleFiles = cline.rooIgnoreController + ? cline.rooIgnoreController.filterPaths(visibleFilePaths) + : visibleFilePaths.map((p) => p.toPosix()).join("\n") + + if (allowedVisibleFiles) { + details += `\n${allowedVisibleFiles}` + } else { + details += "\n(No visible files)" + } + + details += "\n\n# VSCode Open Tabs" + const { maxOpenTabsContext } = state ?? {} + const maxTabs = maxOpenTabsContext ?? 20 + const openTabPaths = vscode.window.tabGroups.all + .flatMap((group) => group.tabs) + .map((tab) => (tab.input as vscode.TabInputText)?.uri?.fsPath) + .filter(Boolean) + .map((absolutePath) => path.relative(cline.cwd, absolutePath).toPosix()) + .slice(0, maxTabs) + + // Filter paths through rooIgnoreController + const allowedOpenTabs = cline.rooIgnoreController + ? cline.rooIgnoreController.filterPaths(openTabPaths) + : openTabPaths.map((p) => p.toPosix()).join("\n") + + if (allowedOpenTabs) { + details += `\n${allowedOpenTabs}` + } else { + details += "\n(No open tabs)" + } + + // Get task-specific and background terminals. + const busyTerminals = [ + ...TerminalRegistry.getTerminals(true, cline.taskId), + ...TerminalRegistry.getBackgroundTerminals(true), + ] + + const inactiveTerminals = [ + ...TerminalRegistry.getTerminals(false, cline.taskId), + ...TerminalRegistry.getBackgroundTerminals(false), + ] + + if (busyTerminals.length > 0) { + if (cline.didEditFile) { + await delay(300) // Delay after saving file to let terminals catch up. + } + + // Wait for terminals to cool down. + await pWaitFor(() => busyTerminals.every((t) => !TerminalRegistry.isProcessHot(t.id)), { + interval: 100, + timeout: 5_000, + }).catch(() => {}) + } + + // Reset, this lets us know when to wait for saved files to update terminals. + cline.didEditFile = false + + // Waiting for updated diagnostics lets terminal output be the most + // up-to-date possible. + let terminalDetails = "" + + if (busyTerminals.length > 0) { + // Terminals are cool, let's retrieve their output. + terminalDetails += "\n\n# Actively Running Terminals" + + for (const busyTerminal of busyTerminals) { + terminalDetails += `\n## Original command: \`${busyTerminal.getLastCommand()}\`` + let newOutput = TerminalRegistry.getUnretrievedOutput(busyTerminal.id) + + if (newOutput) { + newOutput = Terminal.compressTerminalOutput(newOutput, terminalOutputLineLimit) + terminalDetails += `\n### New Output\n${newOutput}` + } + } + } + + // First check if any inactive terminals in this task have completed + // processes with output. + const terminalsWithOutput = inactiveTerminals.filter((terminal) => { + const completedProcesses = terminal.getProcessesWithOutput() + return completedProcesses.length > 0 + }) + + // Only add the header if there are terminals with output. + if (terminalsWithOutput.length > 0) { + terminalDetails += "\n\n# Inactive Terminals with Completed Process Output" + + // Process each terminal with output. + for (const inactiveTerminal of terminalsWithOutput) { + let terminalOutputs: string[] = [] + + // Get output from completed processes queue. + const completedProcesses = inactiveTerminal.getProcessesWithOutput() + + for (const process of completedProcesses) { + let output = process.getUnretrievedOutput() + + if (output) { + output = Terminal.compressTerminalOutput(output, terminalOutputLineLimit) + terminalOutputs.push(`Command: \`${process.command}\`\n${output}`) + } + } + + // Clean the queue after retrieving output. + inactiveTerminal.cleanCompletedProcessQueue() + + // Add this terminal's outputs to the details. + if (terminalOutputs.length > 0) { + terminalDetails += `\n## Terminal ${inactiveTerminal.id}` + terminalOutputs.forEach((output) => { + terminalDetails += `\n### New Output\n${output}` + }) + } + } + } + + // console.log(`[Cline#getEnvironmentDetails] terminalDetails: ${terminalDetails}`) + + // Add recently modified files section. + const recentlyModifiedFiles = cline.fileContextTracker.getAndClearRecentlyModifiedFiles() + + if (recentlyModifiedFiles.length > 0) { + details += + "\n\n# Recently Modified Files\nThese files have been modified since you last accessed them (file was just edited so you may need to re-read it before editing):" + for (const filePath of recentlyModifiedFiles) { + details += `\n${filePath}` + } + } + + if (terminalDetails) { + details += terminalDetails + } + + // Add current time information with timezone. + const now = new Date() + + const formatter = new Intl.DateTimeFormat(undefined, { + year: "numeric", + month: "numeric", + day: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + hour12: true, + }) + + const timeZone = formatter.resolvedOptions().timeZone + const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation + const timeZoneOffsetHours = Math.floor(Math.abs(timeZoneOffset)) + const timeZoneOffsetMinutes = Math.abs(Math.round((Math.abs(timeZoneOffset) - timeZoneOffsetHours) * 60)) + const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : "-"}${timeZoneOffsetHours}:${timeZoneOffsetMinutes.toString().padStart(2, "0")}` + details += `\n\n# Current Time\n${formatter.format(now)} (${timeZone}, UTC${timeZoneOffsetStr})` + + // Add context tokens information. + const { contextTokens, totalCost } = getApiMetrics(cline.clineMessages) + const { id: modelId, info: modelInfo } = cline.api.getModel() + const contextWindow = modelInfo.contextWindow + + const contextPercentage = + contextTokens && contextWindow ? Math.round((contextTokens / contextWindow) * 100) : undefined + + details += `\n\n# Current Context Size (Tokens)\n${contextTokens ? `${contextTokens.toLocaleString()} (${contextPercentage}%)` : "(Not available)"}` + details += `\n\n# Current Cost\n${totalCost !== null ? `$${totalCost.toFixed(2)}` : "(Not available)"}` + + // Add current mode and any mode-specific warnings. + const { + mode, + customModes, + customModePrompts, + experiments = {} as Record, + customInstructions: globalCustomInstructions, + language, + } = state ?? {} + + const currentMode = mode ?? defaultModeSlug + + const modeDetails = await getFullModeDetails(currentMode, customModes, customModePrompts, { + cwd: cline.cwd, + globalCustomInstructions, + language: language ?? formatLanguage(vscode.env.language), + }) + + details += `\n\n# Current Mode\n` + details += `${currentMode}\n` + details += `${modeDetails.name}\n` + details += `${modelId}\n` + + if (Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.POWER_STEERING)) { + details += `${modeDetails.roleDefinition}\n` + + if (modeDetails.customInstructions) { + details += `${modeDetails.customInstructions}\n` + } + } + + // Add warning if not in code mode. + if ( + !isToolAllowedForMode("write_to_file", currentMode, customModes ?? [], { apply_diff: cline.diffEnabled }) && + !isToolAllowedForMode("apply_diff", currentMode, customModes ?? [], { apply_diff: cline.diffEnabled }) + ) { + const currentModeName = getModeBySlug(currentMode, customModes)?.name ?? currentMode + const defaultModeName = getModeBySlug(defaultModeSlug, customModes)?.name ?? defaultModeSlug + details += `\n\nNOTE: You are currently in '${currentModeName}' mode, which does not allow write operations. To write files, the user will need to switch to a mode that supports file writing, such as '${defaultModeName}' mode.` + } + + if (includeFileDetails) { + details += `\n\n# Current Workspace Directory (${cline.cwd.toPosix()}) Files\n` + const isDesktop = arePathsEqual(cline.cwd, path.join(os.homedir(), "Desktop")) + + if (isDesktop) { + // Don't want to immediately access desktop since it would show + // permission popup. + details += "(Desktop files not shown automatically. Use list_files to explore if needed.)" + } else { + const maxFiles = maxWorkspaceFiles ?? 200 + const [files, didHitLimit] = await listFiles(cline.cwd, true, maxFiles) + const { showRooIgnoredFiles = true } = state ?? {} + + const result = formatResponse.formatFilesList( + cline.cwd, + files, + didHitLimit, + cline.rooIgnoreController, + showRooIgnoredFiles, + ) + + details += result + } + } + + return `\n${details.trim()}\n` +} diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts index 2e75a10ce3d..d53a3ff3ed7 100644 --- a/src/core/mentions/index.ts +++ b/src/core/mentions/index.ts @@ -4,14 +4,17 @@ import * as path from "path" import * as vscode from "vscode" import { isBinaryFile } from "isbinaryfile" -import { openFile } from "../../integrations/misc/open-file" -import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher" import { mentionRegexGlobal, unescapeSpaces } from "../../shared/context-mentions" -import { extractTextFromFile } from "../../integrations/misc/extract-text" -import { diagnosticsToProblemsString } from "../../integrations/diagnostics" import { getCommitInfo, getWorkingState } from "../../utils/git" import { getWorkspacePath } from "../../utils/path" + +import { openFile } from "../../integrations/misc/open-file" +import { extractTextFromFile } from "../../integrations/misc/extract-text" +import { diagnosticsToProblemsString } from "../../integrations/diagnostics" + +import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher" + import { FileContextTracker } from "../context-tracking/FileContextTracker" export async function openMention(mention?: string): Promise { @@ -273,3 +276,6 @@ export async function getLatestTerminalOutput(): Promise { await vscode.env.clipboard.writeText(originalClipboard) } } + +// Export processUserContentMentions from its own file +export { processUserContentMentions } from "./processUserContentMentions" diff --git a/src/core/mentions/processUserContentMentions.ts b/src/core/mentions/processUserContentMentions.ts new file mode 100644 index 00000000000..2b69486a867 --- /dev/null +++ b/src/core/mentions/processUserContentMentions.ts @@ -0,0 +1,81 @@ +import { Anthropic } from "@anthropic-ai/sdk" +import { parseMentions } from "./index" +import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher" +import { FileContextTracker } from "../context-tracking/FileContextTracker" + +/** + * Process mentions in user content, specifically within task and feedback tags + */ +export async function processUserContentMentions({ + userContent, + cwd, + urlContentFetcher, + fileContextTracker, +}: { + userContent: Anthropic.Messages.ContentBlockParam[] + cwd: string + urlContentFetcher: UrlContentFetcher + fileContextTracker: FileContextTracker +}) { + // Process userContent array, which contains various block types: + // TextBlockParam, ImageBlockParam, ToolUseBlockParam, and ToolResultBlockParam. + // We need to apply parseMentions() to: + // 1. All TextBlockParam's text (first user message with task) + // 2. ToolResultBlockParam's content/context text arrays if it contains + // "" (see formatToolDeniedFeedback, attemptCompletion, + // executeCommand, and consecutiveMistakeCount >= 3) or "" + // (see askFollowupQuestion), we place all user generated content in + // these tags so they can effectively be used as markers for when we + // should parse mentions). + return Promise.all( + userContent.map(async (block) => { + const shouldProcessMentions = (text: string) => text.includes("") || text.includes("") + + if (block.type === "text") { + if (shouldProcessMentions(block.text)) { + return { + ...block, + text: await parseMentions(block.text, cwd, urlContentFetcher, fileContextTracker), + } + } + + return block + } else if (block.type === "tool_result") { + if (typeof block.content === "string") { + if (shouldProcessMentions(block.content)) { + return { + ...block, + content: await parseMentions(block.content, cwd, urlContentFetcher, fileContextTracker), + } + } + + return block + } else if (Array.isArray(block.content)) { + const parsedContent = await Promise.all( + block.content.map(async (contentBlock) => { + if (contentBlock.type === "text" && shouldProcessMentions(contentBlock.text)) { + return { + ...contentBlock, + text: await parseMentions( + contentBlock.text, + cwd, + urlContentFetcher, + fileContextTracker, + ), + } + } + + return contentBlock + }), + ) + + return { ...block, content: parsedContent } + } + + return block + } + + return block + }), + ) +} diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap index 34f9fa45b77..f8f115c4ca3 100644 --- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap +++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap @@ -5,27 +5,33 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -348,10 +354,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -485,27 +491,33 @@ exports[`SYSTEM_PROMPT should exclude diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -828,10 +840,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -965,27 +977,33 @@ exports[`SYSTEM_PROMPT should explicitly handle undefined mcpHub 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -1308,10 +1326,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -1445,27 +1463,33 @@ exports[`SYSTEM_PROMPT should handle different browser viewport sizes 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -1841,10 +1865,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -1981,27 +2005,33 @@ exports[`SYSTEM_PROMPT should include MCP server info when mcpHub is provided 1` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -2373,10 +2403,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -2529,27 +2559,33 @@ exports[`SYSTEM_PROMPT should include browser actions when supportsComputerUse i ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -2925,10 +2961,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -3065,27 +3101,33 @@ exports[`SYSTEM_PROMPT should include diff strategy tool description when diffEn ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -3508,10 +3550,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -3645,27 +3687,33 @@ exports[`SYSTEM_PROMPT should maintain consistent system prompt 1`] = ` ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -3988,10 +4036,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -4167,27 +4215,33 @@ exports[`addCustomInstructions should exclude MCP server creation info when disa ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -4559,10 +4613,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -4724,27 +4778,33 @@ exports[`addCustomInstructions should generate correct prompt for architect mode ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -5045,10 +5105,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -5195,27 +5255,33 @@ exports[`addCustomInstructions should generate correct prompt for ask mode 1`] = ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -5413,10 +5479,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: @@ -5539,7 +5605,7 @@ Language Preference: You should always speak and think in the "en" language. Mode-specific Instructions: -You can analyze code, explain concepts, and access external resources. Make sure to answer the user's questions and don't rush to switch to implementing code. Include Mermaid diagrams if they help make your response clearer. +You can analyze code, explain concepts, and access external resources. Always answer the user’s questions thoroughly, and do not switch to implementing code unless explicitly requested by the user. Include Mermaid diagrams when they clarify your response. Rules: # Rules from .clinerules-ask: @@ -5583,27 +5649,33 @@ exports[`addCustomInstructions should include MCP server creation info when enab ==== +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + TOOL USE You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution. +Always use the actual tool name as the XML tag name for proper parsing and execution. # Tools @@ -5975,10 +6047,10 @@ Example: Requesting to switch to code mode ## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: diff --git a/src/core/prompts/instructions/create-mode.ts b/src/core/prompts/instructions/create-mode.ts index ad02f6b6ab5..fe7509c1de0 100644 --- a/src/core/prompts/instructions/create-mode.ts +++ b/src/core/prompts/instructions/create-mode.ts @@ -26,7 +26,9 @@ If asked to create a project mode, create it in ${AGENT_MODES_FILE_NAME} in the * roleDefinition: A detailed description of the mode's role and capabilities * groups: Array of allowed tool groups (can be empty). Each group can be specified either as a string (e.g., "edit" to allow editing any file) or with file restrictions (e.g., ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }] to only allow editing markdown files) -- The customInstructions field is optional. +- The following fields are optional but highly recommended: + * whenToUse: A clear description of when this mode should be selected and what types of tasks it's best suited for. This helps the Orchestrator mode make better decisions. + * customInstructions: Additional instructions for how the mode should operate - For multi-line text, include newline characters in the string like "This is the first line.\\nThis is the next line.\\n\\nThis is a double line break." @@ -37,6 +39,7 @@ Both files should follow this structure: "slug": "designer", // Required: unique slug with lowercase letters, numbers, and hyphens "name": "Designer", // Required: mode display name "roleDefinition": "You are PearAI (Powered by Roo Code / Cline), a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty + "whenToUse": "Use this mode when creating or modifying UI components, implementing design systems, or ensuring responsive web interfaces. This mode is especially effective with CSS, HTML, and modern frontend frameworks.", // Optional but recommended "groups": [ // Required: array of tool groups (can be empty) "read", // Read files group (read_file, fetch_instructions, search_files, list_files, list_code_definition_names) "edit", // Edit files group (apply_diff, write_to_file) - allows editing any file diff --git a/src/core/prompts/responses.ts b/src/core/prompts/responses.ts index 9c82a848804..8238b969e2b 100644 --- a/src/core/prompts/responses.ts +++ b/src/core/prompts/responses.ts @@ -185,15 +185,15 @@ const formatImagesIntoBlocks = (images?: string[]): Anthropic.ImageBlockParam[] const toolUseInstructionsReminder = `# Reminder: Instructions for Tool Use -Tool uses are formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the attempt_completion tool: @@ -201,4 +201,4 @@ I have completed the task... -Always adhere to this format for all tool uses to ensure proper parsing and execution.` +Always use the actual tool name as the XML tag name for proper parsing and execution.` diff --git a/src/core/prompts/sections/index.ts b/src/core/prompts/sections/index.ts index a9f8c9e4c6d..d06dbbfde1d 100644 --- a/src/core/prompts/sections/index.ts +++ b/src/core/prompts/sections/index.ts @@ -7,3 +7,4 @@ export { getMcpServersSection } from "./mcp-servers" export { getToolUseGuidelinesSection } from "./tool-use-guidelines" export { getCapabilitiesSection } from "./capabilities" export { getModesSection } from "./modes" +export { markdownFormattingSection } from "./markdown-formatting" diff --git a/src/core/prompts/sections/markdown-formatting.ts b/src/core/prompts/sections/markdown-formatting.ts new file mode 100644 index 00000000000..fe152a1a41d --- /dev/null +++ b/src/core/prompts/sections/markdown-formatting.ts @@ -0,0 +1,7 @@ +export function markdownFormattingSection(): string { + return `==== + +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reterence as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in ` +} diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts index 0fa5e065760..ff12098d5e9 100644 --- a/src/core/prompts/sections/modes.ts +++ b/src/core/prompts/sections/modes.ts @@ -16,7 +16,19 @@ export async function getModesSection(context: vscode.ExtensionContext): Promise MODES - These are the currently available modes: -${allModes.map((mode: ModeConfig) => ` * "${mode.name}" mode (${mode.slug}) - ${mode.roleDefinition.split(".")[0]}`).join("\n")}` +${allModes + .map((mode: ModeConfig) => { + let description: string + if (mode.whenToUse && mode.whenToUse.trim() !== "") { + // Use whenToUse as the primary description, indenting subsequent lines for readability + description = mode.whenToUse.replace(/\n/g, "\n ") + } else { + // Fallback to the first sentence of roleDefinition if whenToUse is not available + description = mode.roleDefinition.split(".")[0] + } + return ` * "${mode.name}" mode (${mode.slug}) - ${description}` + }) + .join("\n")}` modesContent += ` If the user asks you to create or edit a new mode for this project, you should read the instructions by using the fetch_instructions tool, like this: diff --git a/src/core/prompts/sections/tool-use.ts b/src/core/prompts/sections/tool-use.ts index e5671871ce7..b75e4dad921 100644 --- a/src/core/prompts/sections/tool-use.ts +++ b/src/core/prompts/sections/tool-use.ts @@ -7,19 +7,19 @@ You have access to a set of tools that are executed upon the user's approval. Yo # Tool Use Formatting -Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure: +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: - + value1 value2 ... - + -For example: +For example, to use the read_file tool: src/main.js -Always adhere to this format for the tool use to ensure proper parsing and execution.` +Always use the actual tool name as the XML tag name for proper parsing and execution.` } diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index f56a9476637..92eba6bdc7c 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -24,6 +24,7 @@ import { getCapabilitiesSection, getModesSection, addCustomInstructions, + markdownFormattingSection, } from "./sections" import { loadSystemPromptFile } from "./sections/custom-system-prompt" import { formatLanguage } from "../../shared/language" @@ -65,6 +66,8 @@ async function generatePrompt( const basePrompt = `${roleDefinition} +${markdownFormattingSection()} + ${getSharedToolUseSection()} ${getToolDescriptionsForMode( diff --git a/src/core/prompts/tools/new-task.ts b/src/core/prompts/tools/new-task.ts index 1bf8848aef4..7301b7b422d 100644 --- a/src/core/prompts/tools/new-task.ts +++ b/src/core/prompts/tools/new-task.ts @@ -2,10 +2,10 @@ import { ToolArgs } from "./types" export function getNewTaskDescription(_args: ToolArgs): string { return `## new_task -Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. +Description: This will let you create a new task instance in the chosen mode using your provided message. Parameters: -- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). - message: (required) The initial user message or instructions for this new task. Usage: diff --git a/src/core/sliding-window/__tests__/sliding-window.test.ts b/src/core/sliding-window/__tests__/sliding-window.test.ts index 16af2d46301..7890b55ec88 100644 --- a/src/core/sliding-window/__tests__/sliding-window.test.ts +++ b/src/core/sliding-window/__tests__/sliding-window.test.ts @@ -10,6 +10,7 @@ import { truncateConversation, truncateConversationIfNeeded, } from "../index" +import { ApiMessage } from "../../task-persistence/apiMessages" // Create a mock ApiHandler for testing class MockApiHandler extends BaseProvider { @@ -41,7 +42,7 @@ const mockApiHandler = new MockApiHandler() */ describe("truncateConversation", () => { it("should retain the first message", () => { - const messages: Anthropic.Messages.MessageParam[] = [ + const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, { role: "user", content: "Third message" }, @@ -58,7 +59,7 @@ describe("truncateConversation", () => { }) it("should remove the specified fraction of messages (rounded to even number)", () => { - const messages: Anthropic.Messages.MessageParam[] = [ + const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, { role: "user", content: "Third message" }, @@ -77,7 +78,7 @@ describe("truncateConversation", () => { }) it("should round to an even number of messages to remove", () => { - const messages: Anthropic.Messages.MessageParam[] = [ + const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, { role: "user", content: "Third message" }, @@ -96,7 +97,7 @@ describe("truncateConversation", () => { }) it("should handle edge case with fracToRemove = 0", () => { - const messages: Anthropic.Messages.MessageParam[] = [ + const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, { role: "user", content: "Third message" }, @@ -108,7 +109,7 @@ describe("truncateConversation", () => { }) it("should handle edge case with fracToRemove = 1", () => { - const messages: Anthropic.Messages.MessageParam[] = [ + const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, { role: "user", content: "Third message" }, @@ -224,7 +225,7 @@ describe("truncateConversationIfNeeded", () => { maxTokens, }) - const messages: Anthropic.Messages.MessageParam[] = [ + const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, { role: "user", content: "Third message" }, @@ -328,7 +329,7 @@ describe("truncateConversationIfNeeded", () => { // Test case 1: Small content that won't push us over the threshold const smallContent = [{ type: "text" as const, text: "Small content" }] const smallContentTokens = await estimateTokenCount(smallContent, mockApiHandler) - const messagesWithSmallContent: Anthropic.Messages.MessageParam[] = [ + const messagesWithSmallContent: ApiMessage[] = [ ...messages.slice(0, -1), { role: messages[messages.length - 1].role, content: smallContent }, ] @@ -353,7 +354,7 @@ describe("truncateConversationIfNeeded", () => { }, ] const largeContentTokens = await estimateTokenCount(largeContent, mockApiHandler) - const messagesWithLargeContent: Anthropic.Messages.MessageParam[] = [ + const messagesWithLargeContent: ApiMessage[] = [ ...messages.slice(0, -1), { role: messages[messages.length - 1].role, content: largeContent }, ] @@ -372,7 +373,7 @@ describe("truncateConversationIfNeeded", () => { // Test case 3: Very large content that will definitely exceed threshold const veryLargeContent = [{ type: "text" as const, text: "X".repeat(1000) }] const veryLargeContentTokens = await estimateTokenCount(veryLargeContent, mockApiHandler) - const messagesWithVeryLargeContent: Anthropic.Messages.MessageParam[] = [ + const messagesWithVeryLargeContent: ApiMessage[] = [ ...messages.slice(0, -1), { role: messages[messages.length - 1].role, content: veryLargeContent }, ] @@ -424,7 +425,7 @@ describe("getMaxTokens", () => { }) // Reuse across tests for consistency - const messages: Anthropic.Messages.MessageParam[] = [ + const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, { role: "user", content: "Third message" }, diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts index 75395ecd758..d17bf7fc57d 100644 --- a/src/core/sliding-window/index.ts +++ b/src/core/sliding-window/index.ts @@ -1,5 +1,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import { ApiHandler } from "../../api" +import { summarizeConversation } from "../condense" +import { ApiMessage } from "../task-persistence/apiMessages" /** * Default percentage of the context window to use as a buffer when deciding when to truncate @@ -27,14 +29,11 @@ export async function estimateTokenCount( * The first message is always retained, and a specified fraction (rounded to an even number) * of messages from the beginning (excluding the first) is removed. * - * @param {Anthropic.Messages.MessageParam[]} messages - The conversation messages. + * @param {ApiMessage[]} messages - The conversation messages. * @param {number} fracToRemove - The fraction (between 0 and 1) of messages (excluding the first) to remove. - * @returns {Anthropic.Messages.MessageParam[]} The truncated conversation messages. + * @returns {ApiMessage[]} The truncated conversation messages. */ -export function truncateConversation( - messages: Anthropic.Messages.MessageParam[], - fracToRemove: number, -): Anthropic.Messages.MessageParam[] { +export function truncateConversation(messages: ApiMessage[], fracToRemove: number): ApiMessage[] { const truncatedMessages = [messages[0]] const rawMessagesToRemove = Math.floor((messages.length - 1) * fracToRemove) const messagesToRemove = rawMessagesToRemove - (rawMessagesToRemove % 2) @@ -48,20 +47,22 @@ export function truncateConversation( * Conditionally truncates the conversation messages if the total token count * exceeds the model's limit, considering the size of incoming content. * - * @param {Anthropic.Messages.MessageParam[]} messages - The conversation messages. + * @param {ApiMessage[]} messages - The conversation messages. * @param {number} totalTokens - The total number of tokens in the conversation (excluding the last user message). * @param {number} contextWindow - The context window size. * @param {number} maxTokens - The maximum number of tokens allowed. * @param {ApiHandler} apiHandler - The API handler to use for token counting. - * @returns {Anthropic.Messages.MessageParam[]} The original or truncated conversation messages. + * @param {boolean} autoCondenseContext - Whether to use LLM summarization or sliding window implementation + * @returns {ApiMessage[]} The original or truncated conversation messages. */ type TruncateOptions = { - messages: Anthropic.Messages.MessageParam[] + messages: ApiMessage[] totalTokens: number contextWindow: number maxTokens?: number | null apiHandler: ApiHandler + autoCondenseContext?: boolean } /** @@ -69,7 +70,7 @@ type TruncateOptions = { * exceeds the model's limit, considering the size of incoming content. * * @param {TruncateOptions} options - The options for truncation - * @returns {Promise} The original or truncated conversation messages. + * @returns {Promise} The original or truncated conversation messages. */ export async function truncateConversationIfNeeded({ messages, @@ -77,7 +78,8 @@ export async function truncateConversationIfNeeded({ contextWindow, maxTokens, apiHandler, -}: TruncateOptions): Promise { + autoCondenseContext, +}: TruncateOptions): Promise { // Calculate the maximum tokens reserved for response const reservedTokens = maxTokens || contextWindow * 0.2 @@ -96,5 +98,13 @@ export async function truncateConversationIfNeeded({ const allowedTokens = contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokens // Determine if truncation is needed and apply if necessary - return effectiveTokens > allowedTokens ? truncateConversation(messages, 0.5) : messages + if (effectiveTokens <= allowedTokens) { + return messages + } else if (autoCondenseContext) { + const summarizedMessages = await summarizeConversation(messages, apiHandler) + if (messages !== summarizedMessages) { + return summarizedMessages + } + } + return truncateConversation(messages, 0.5) } diff --git a/src/core/task-persistence/apiMessages.ts b/src/core/task-persistence/apiMessages.ts index b361016345b..6ac36ed08fa 100644 --- a/src/core/task-persistence/apiMessages.ts +++ b/src/core/task-persistence/apiMessages.ts @@ -8,7 +8,7 @@ import { fileExistsAtPath } from "../../utils/fs" import { GlobalFileNames } from "../../shared/globalFileNames" import { getTaskDirectoryPath } from "../../shared/storagePathManager" -export type ApiMessage = Anthropic.MessageParam & { ts?: number } +export type ApiMessage = Anthropic.MessageParam & { ts?: number; isSummary?: boolean } export async function readApiMessages({ taskId, diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts new file mode 100644 index 00000000000..9b427c26c93 --- /dev/null +++ b/src/core/task/Task.ts @@ -0,0 +1,1666 @@ +import * as path from "path" +import os from "os" +import crypto from "crypto" +import EventEmitter from "events" + +import { Anthropic } from "@anthropic-ai/sdk" +import delay from "delay" +import pWaitFor from "p-wait-for" +import { serializeError } from "serialize-error" + +// schemas +import { TokenUsage, ToolUsage, ToolName, ModelInfo, CreatorModeConfig } from ../../schemas" + +// api +import { ApiHandler, buildApiHandler } from "../../api" +import { ApiStream } from "../../api/transform/stream" + +// shared +import { ProviderSettings } from "../../shared/api" +import { findLastIndex } from "../../shared/array" +import { combineApiRequests } from "../../shared/combineApiRequests" +import { combineCommandSequences } from "../../shared/combineCommandSequences" +import { + ClineApiReqCancelReason, + ClineApiReqInfo, + ClineAsk, + ClineMessage, + ClineSay, + ToolProgressStatus, +} from "../../shared/ExtensionMessage" +import { getApiMetrics } from "../../shared/getApiMetrics" +import { HistoryItem } from "../../shared/HistoryItem" +import { ClineAskResponse } from "../../shared/WebviewMessage" +import { defaultModeSlug } from "../../shared/modes" +import { DiffStrategy } from "../../shared/tools" + +// services +import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher" +import { BrowserSession } from "../../services/browser/BrowserSession" +import { McpHub } from "../../services/mcp/McpHub" +import { McpServerManager } from "../../services/mcp/McpServerManager" +import { telemetryService } from "../../services/telemetry/TelemetryService" +import { RepoPerTaskCheckpointService } from "../../services/checkpoints" + +// integrations +import { DiffViewProvider } from "../../integrations/editor/DiffViewProvider" +import { findToolName, formatContentBlockToMarkdown } from "../../integrations/misc/export-markdown" +import { RooTerminalProcess } from "../../integrations/terminal/types" +import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry" + +// utils +import { calculateApiCostAnthropic } from "../../utils/cost" +import { getWorkspacePath } from "../../utils/path" + +// prompts +import { formatResponse } from "../prompts/responses" +import { SYSTEM_PROMPT } from "../prompts/system" + +// core modules +import { ToolRepetitionDetector } from "../tools/ToolRepetitionDetector" +import { FileContextTracker } from "../context-tracking/FileContextTracker" +import { RooIgnoreController } from "../ignore/RooIgnoreController" +import { type AssistantMessageContent, parseAssistantMessage, presentAssistantMessage } from "../assistant-message" +import { truncateConversationIfNeeded } from "../sliding-window" +import { ClineProvider } from "../webview/ClineProvider" +import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-replace" +import { readApiMessages, saveApiMessages, readTaskMessages, saveTaskMessages, taskMetadata } from "../task-persistence" +import { getEnvironmentDetails } from "../environment/getEnvironmentDetails" +import { + type CheckpointDiffOptions, + type CheckpointRestoreOptions, + getCheckpointService, + checkpointSave, + checkpointRestore, + checkpointDiff, +} from "../checkpoints" +import { processUserContentMentions } from "../mentions/processUserContentMentions" +import { ApiMessage } from "../task-persistence/apiMessages" +import { getMessagesSinceLastSummary } from "../condense" +import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning" +import { readMemories } from "../../utils/memory" + +export type ClineEvents = { + message: [{ action: "created" | "updated"; message: ClineMessage }] + taskStarted: [] + taskModeSwitched: [taskId: string, mode: string] + taskPaused: [] + taskUnpaused: [] + taskAskResponded: [] + taskAborted: [] + taskSpawned: [taskId: string] + taskCompleted: [taskId: string, tokenUsage: TokenUsage, toolUsage: ToolUsage] + taskTokenUsageUpdated: [taskId: string, tokenUsage: TokenUsage] + taskToolFailed: [taskId: string, tool: ToolName, error: string] +} + +export type TaskOptions = { + provider: ClineProvider + apiConfiguration: ProviderSettings + enableDiff?: boolean + enableCheckpoints?: boolean + fuzzyMatchThreshold?: number + consecutiveMistakeLimit?: number + task?: string + images?: string[] + historyItem?: HistoryItem + experiments?: Record + startTask?: boolean + rootTask?: Task + parentTask?: Task + taskNumber?: number + onCreated?: (cline: Task) => void + pearaiModels?: Record + creatorModeConfig?: CreatorModeConfig +} + +export class Task extends EventEmitter { + readonly taskId: string + readonly instanceId: string + + readonly rootTask: Task | undefined = undefined + readonly parentTask: Task | undefined = undefined + readonly taskNumber: number + readonly workspacePath: string + + providerRef: WeakRef + private readonly globalStoragePath: string + abort: boolean = false + didFinishAbortingStream = false + abandoned = false + isInitialized = false + isPaused: boolean = false + pausedModeSlug: string = defaultModeSlug + private pauseInterval: NodeJS.Timeout | undefined + + // PearAI + public creatorModeConfig: CreatorModeConfig + + // API + readonly apiConfiguration: ProviderSettings + api: ApiHandler + private lastApiRequestTime?: number + + toolRepetitionDetector: ToolRepetitionDetector + rooIgnoreController?: RooIgnoreController + fileContextTracker: FileContextTracker + urlContentFetcher: UrlContentFetcher + terminalProcess?: RooTerminalProcess + + // Computer User + browserSession: BrowserSession + + // Editing + diffViewProvider: DiffViewProvider + diffStrategy?: DiffStrategy + diffEnabled: boolean = false + fuzzyMatchThreshold: number + didEditFile: boolean = false + + // LLM Messages & Chat Messages + apiConversationHistory: ApiMessage[] = [] + clineMessages: ClineMessage[] = [] + + // Ask + private askResponse?: ClineAskResponse + private askResponseText?: string + private askResponseImages?: string[] + public lastMessageTs?: number + + // Tool Use + consecutiveMistakeCount: number = 0 + consecutiveMistakeLimit: number + consecutiveMistakeCountForApplyDiff: Map = new Map() + toolUsage: ToolUsage = {} + + // Checkpoints + enableCheckpoints: boolean + checkpointService?: RepoPerTaskCheckpointService + checkpointServiceInitializing = false + + // Streaming + isWaitingForFirstChunk = false + isStreaming = false + currentStreamingContentIndex = 0 + assistantMessageContent: AssistantMessageContent[] = [] + presentAssistantMessageLocked = false + presentAssistantMessageHasPendingUpdates = false + userMessageContent: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] = [] + userMessageContentReady = false + didRejectTool = false + didAlreadyUseTool = false + didCompleteReadingStream = false + + constructor({ + provider, + apiConfiguration, + enableDiff = false, + enableCheckpoints = true, + fuzzyMatchThreshold = 1.0, + consecutiveMistakeLimit = 3, + task, + images, + historyItem, + startTask = true, + rootTask, + parentTask, + taskNumber = -1, + onCreated, + creatorModeConfig, + }: TaskOptions) { + super() + + if (startTask && !task && !images && !historyItem) { + throw new Error("Either historyItem or task/images must be provided") + } + + this.taskId = historyItem ? historyItem.id : crypto.randomUUID() + // normal use-case is usually retry similar history task with new workspace + this.workspacePath = parentTask + ? parentTask.workspacePath + : getWorkspacePath(path.join(os.homedir(), "Desktop")) + this.instanceId = crypto.randomUUID().slice(0, 8) + this.taskNumber = -1 + + this.rooIgnoreController = new RooIgnoreController(this.cwd) + this.fileContextTracker = new FileContextTracker(provider, this.taskId) + + this.rooIgnoreController.initialize().catch((error) => { + console.error("Failed to initialize RooIgnoreController:", error) + }) + + this.creatorModeConfig = creatorModeConfig ?? historyItem?.creatorModeConfig ?? { creatorMode: false } + + this.apiConfiguration = { + ...apiConfiguration, + creatorModeConfig: this.creatorModeConfig + } + this.api = buildApiHandler(this.apiConfiguration) + + this.urlContentFetcher = new UrlContentFetcher(provider.context) + this.browserSession = new BrowserSession(provider.context) + this.diffEnabled = enableDiff + this.fuzzyMatchThreshold = fuzzyMatchThreshold + this.consecutiveMistakeLimit = consecutiveMistakeLimit + this.providerRef = new WeakRef(provider) + this.globalStoragePath = provider.context.globalStorageUri.fsPath + this.diffViewProvider = new DiffViewProvider(this.cwd) + this.enableCheckpoints = enableCheckpoints + this.creatorModeConfig = creatorModeConfig ?? historyItem?.creatorModeConfig ?? { creatorMode: false } + + + this.rootTask = rootTask + this.parentTask = parentTask + this.taskNumber = taskNumber + + if (historyItem) { + telemetryService.captureTaskRestarted(this.taskId) + } else { + telemetryService.captureTaskCreated(this.taskId) + } + + this.diffStrategy = new MultiSearchReplaceDiffStrategy(this.fuzzyMatchThreshold) + this.toolRepetitionDetector = new ToolRepetitionDetector(this.consecutiveMistakeLimit) + + onCreated?.(this) + + if (startTask) { + if (task || images) { + this.startTask(task, images) + } else if (historyItem) { + this.resumeTaskFromHistory() + } else { + throw new Error("Either historyItem or task/images must be provided") + } + } + } + + static create(options: TaskOptions): [Task, Promise] { + const instance = new Task({ ...options, startTask: false }) + const { images, task, historyItem } = options + let promise + + if (images || task) { + promise = instance.startTask(task, images) + } else if (historyItem) { + promise = instance.resumeTaskFromHistory() + } else { + throw new Error("Either historyItem or task/images must be provided") + } + + return [instance, promise] + } + + // API Messages + + private async getSavedApiConversationHistory(): Promise { + return readApiMessages({ taskId: this.taskId, globalStoragePath: this.globalStoragePath }) + } + + private async addToApiConversationHistory(message: Anthropic.MessageParam) { + const messageWithTs = { ...message, ts: Date.now() } + this.apiConversationHistory.push(messageWithTs) + await this.saveApiConversationHistory() + } + + async overwriteApiConversationHistory(newHistory: ApiMessage[]) { + this.apiConversationHistory = newHistory + await this.saveApiConversationHistory() + } + + private async saveApiConversationHistory() { + try { + await saveApiMessages({ + messages: this.apiConversationHistory, + taskId: this.taskId, + globalStoragePath: this.globalStoragePath, + }) + } catch (error) { + // In the off chance this fails, we don't want to stop the task. + console.error("Failed to save API conversation history:", error) + } + } + + // Cline Messages + + private async getSavedClineMessages(): Promise { + return readTaskMessages({ taskId: this.taskId, globalStoragePath: this.globalStoragePath }) + } + + private async addToClineMessages(message: ClineMessage) { + this.clineMessages.push(message) + await this.providerRef.deref()?.postStateToWebview() + this.emit("message", { action: "created", message }) + await this.saveClineMessages() + } + + public async overwriteClineMessages(newMessages: ClineMessage[]) { + this.clineMessages = newMessages + await this.saveClineMessages() + } + + private async updateClineMessage(partialMessage: ClineMessage) { + await this.providerRef.deref()?.postMessageToWebview({ type: "partialMessage", partialMessage }) + this.emit("message", { action: "updated", message: partialMessage }) + } + + private async saveClineMessages() { + try { + await saveTaskMessages({ + messages: this.clineMessages, + taskId: this.taskId, + globalStoragePath: this.globalStoragePath, + }) + + const { historyItem, tokenUsage } = await taskMetadata({ + messages: this.clineMessages, + taskId: this.taskId, + taskNumber: this.taskNumber, + globalStoragePath: this.globalStoragePath, + workspace: this.cwd, + creatorModeConfig: this.creatorModeConfig, + }) + + this.emit("taskTokenUsageUpdated", this.taskId, tokenUsage) + + await this.providerRef.deref()?.updateTaskHistory(historyItem) + } catch (error) { + console.error("Failed to save cline messages:", error) + } + } + + // Note that `partial` has three valid states true (partial message), + // false (completion of partial message), undefined (individual complete + // message). + async ask( + type: ClineAsk, + text?: string, + partial?: boolean, + progressStatus?: ToolProgressStatus, + ): Promise<{ response: ClineAskResponse; text?: string; images?: string[] }> { + // If this Cline instance was aborted by the provider, then the only + // thing keeping us alive is a promise still running in the background, + // in which case we don't want to send its result to the webview as it + // is attached to a new instance of Cline now. So we can safely ignore + // the result of any active promises, and this class will be + // deallocated. (Although we set Cline = undefined in provider, that + // simply removes the reference to this instance, but the instance is + // still alive until this promise resolves or rejects.) + if (this.abort) { + throw new Error(`[Cline#ask] task ${this.taskId}.${this.instanceId} aborted`) + } + + let askTs: number + + if (partial !== undefined) { + const lastMessage = this.clineMessages.at(-1) + + const isUpdatingPreviousPartial = + lastMessage && lastMessage.partial && lastMessage.type === "ask" && lastMessage.ask === type + + if (partial) { + if (isUpdatingPreviousPartial) { + // Existing partial message, so update it. + lastMessage.text = text + lastMessage.partial = partial + lastMessage.progressStatus = progressStatus + // TODO: Be more efficient about saving and posting only new + // data or one whole message at a time so ignore partial for + // saves, and only post parts of partial message instead of + // whole array in new listener. + this.updateClineMessage(lastMessage) + throw new Error("Current ask promise was ignored (#1)") + } else { + // This is a new partial message, so add it with partial + // state. + askTs = Date.now() + this.lastMessageTs = askTs + await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text, partial }) + throw new Error("Current ask promise was ignored (#2)") + } + } else { + if (isUpdatingPreviousPartial) { + // This is the complete version of a previously partial + // message, so replace the partial with the complete version. + this.askResponse = undefined + this.askResponseText = undefined + this.askResponseImages = undefined + + // Bug for the history books: + // In the webview we use the ts as the chatrow key for the + // virtuoso list. Since we would update this ts right at the + // end of streaming, it would cause the view to flicker. The + // key prop has to be stable otherwise react has trouble + // reconciling items between renders, causing unmounting and + // remounting of components (flickering). + // The lesson here is if you see flickering when rendering + // lists, it's likely because the key prop is not stable. + // So in this case we must make sure that the message ts is + // never altered after first setting it. + askTs = lastMessage.ts + this.lastMessageTs = askTs + lastMessage.text = text + lastMessage.partial = false + lastMessage.progressStatus = progressStatus + await this.saveClineMessages() + this.updateClineMessage(lastMessage) + } else { + // This is a new and complete message, so add it like normal. + this.askResponse = undefined + this.askResponseText = undefined + this.askResponseImages = undefined + askTs = Date.now() + this.lastMessageTs = askTs + await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text }) + } + } + } else { + // This is a new non-partial message, so add it like normal. + this.askResponse = undefined + this.askResponseText = undefined + this.askResponseImages = undefined + askTs = Date.now() + this.lastMessageTs = askTs + await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text }) + } + + await pWaitFor(() => this.askResponse !== undefined || this.lastMessageTs !== askTs, { interval: 100 }) + + if (this.lastMessageTs !== askTs) { + // Could happen if we send multiple asks in a row i.e. with + // command_output. It's important that when we know an ask could + // fail, it is handled gracefully. + throw new Error("Current ask promise was ignored") + } + + const result = { response: this.askResponse!, text: this.askResponseText, images: this.askResponseImages } + this.askResponse = undefined + this.askResponseText = undefined + this.askResponseImages = undefined + this.emit("taskAskResponded") + return result + } + + async handleWebviewAskResponse(askResponse: ClineAskResponse, text?: string, images?: string[]) { + this.askResponse = askResponse + this.askResponseText = text + this.askResponseImages = images + } + + async handleTerminalOperation(terminalOperation: "continue" | "abort") { + if (terminalOperation === "continue") { + this.terminalProcess?.continue() + } else if (terminalOperation === "abort") { + this.terminalProcess?.abort() + } + } + + async say( + type: ClineSay, + text?: string, + images?: string[], + partial?: boolean, + checkpoint?: Record, + progressStatus?: ToolProgressStatus, + options: { + isNonInteractive?: boolean + } = {}, + ): Promise { + if (this.abort) { + throw new Error(`[Cline#say] task ${this.taskId}.${this.instanceId} aborted`) + } + + if (partial !== undefined) { + const lastMessage = this.clineMessages.at(-1) + + const isUpdatingPreviousPartial = + lastMessage && lastMessage.partial && lastMessage.type === "say" && lastMessage.say === type + + if (partial) { + if (isUpdatingPreviousPartial) { + // Existing partial message, so update it. + lastMessage.text = text + lastMessage.images = images + lastMessage.partial = partial + lastMessage.progressStatus = progressStatus + this.updateClineMessage(lastMessage) + } else { + // This is a new partial message, so add it with partial state. + const sayTs = Date.now() + + if (!options.isNonInteractive) { + this.lastMessageTs = sayTs + } + + await this.addToClineMessages({ ts: sayTs, type: "say", say: type, text, images, partial }) + } + } else { + // New now have a complete version of a previously partial message. + // This is the complete version of a previously partial + // message, so replace the partial with the complete version. + if (isUpdatingPreviousPartial) { + if (!options.isNonInteractive) { + this.lastMessageTs = lastMessage.ts + } + + lastMessage.text = text + lastMessage.images = images + lastMessage.partial = false + lastMessage.progressStatus = progressStatus + + // Instead of streaming partialMessage events, we do a save + // and post like normal to persist to disk. + await this.saveClineMessages() + + // More performant than an entire `postStateToWebview`. + this.updateClineMessage(lastMessage) + } else { + // This is a new and complete message, so add it like normal. + const sayTs = Date.now() + + if (!options.isNonInteractive) { + this.lastMessageTs = sayTs + } + + await this.addToClineMessages({ ts: sayTs, type: "say", say: type, text, images }) + } + } + } else { + // This is a new non-partial message, so add it like normal. + const sayTs = Date.now() + + // A "non-interactive" message is a message is one that the user + // does not need to respond to. We don't want these message types + // to trigger an update to `lastMessageTs` since they can be created + // asynchronously and could interrupt a pending ask. + if (!options.isNonInteractive) { + this.lastMessageTs = sayTs + } + + await this.addToClineMessages({ ts: sayTs, type: "say", say: type, text, images, checkpoint }) + } + } + + async sayAndCreateMissingParamError(toolName: ToolName, paramName: string, relPath?: string) { + await this.say( + "error", + `Roo tried to use ${toolName}${ + relPath ? ` for '${relPath.toPosix()}'` : "" + } without value for required parameter '${paramName}'. Retrying...`, + ) + return formatResponse.toolError(formatResponse.missingToolParameterError(paramName)) + } + + // Start / Abort / Resume + + private async startTask(task?: string, images?: string[]): Promise { + // `conversationHistory` (for API) and `clineMessages` (for webview) + // need to be in sync. + // If the extension process were killed, then on restart the + // `clineMessages` might not be empty, so we need to set it to [] when + // we create a new Cline client (otherwise webview would show stale + // messages from previous session). + this.clineMessages = [] + this.apiConversationHistory = [] + await this.providerRef.deref()?.postStateToWebview() + + await this.say("text", task, images) + this.isInitialized = true + + let imageBlocks: Anthropic.ImageBlockParam[] = formatResponse.imageBlocks(images) + + console.log(`[subtasks] task ${this.taskId}.${this.instanceId} starting`) + // PearAI memories + const memories = readMemories() + let memoryBlocks: Anthropic.TextBlockParam[] = [] + if (memories.length > 0) { + const memoryContext = memories.map((m) => m.memory).join("\n\n") + memoryBlocks.push({ + type: "text", + text: `\n\n${memoryContext}\n`, + }) + } + await this.initiateTaskLoop([ + { + type: "text", + text: `\n${task}\n`, + }, + ...imageBlocks, + ]) + } + + public async resumePausedTask(lastMessage: string) { + // Release this Cline instance from paused state. + this.isPaused = false + this.emit("taskUnpaused") + + // Fake an answer from the subtask that it has completed running and + // this is the result of what it has done add the message to the chat + // history and to the webview ui. + try { + await this.say("subtask_result", lastMessage) + + await this.addToApiConversationHistory({ + role: "user", + content: [{ type: "text", text: `[new_task completed] Result: ${lastMessage}` }], + }) + } catch (error) { + this.providerRef + .deref() + ?.log(`Error failed to add reply from subtast into conversation of parent task, error: ${error}`) + + throw error + } + } + + private async resumeTaskFromHistory() { + const modifiedClineMessages = await this.getSavedClineMessages() + + // Remove any resume messages that may have been added before + const lastRelevantMessageIndex = findLastIndex( + modifiedClineMessages, + (m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task"), + ) + + if (lastRelevantMessageIndex !== -1) { + modifiedClineMessages.splice(lastRelevantMessageIndex + 1) + } + + // since we don't use api_req_finished anymore, we need to check if the last api_req_started has a cost value, if it doesn't and no cancellation reason to present, then we remove it since it indicates an api request without any partial content streamed + const lastApiReqStartedIndex = findLastIndex( + modifiedClineMessages, + (m) => m.type === "say" && m.say === "api_req_started", + ) + + if (lastApiReqStartedIndex !== -1) { + const lastApiReqStarted = modifiedClineMessages[lastApiReqStartedIndex] + const { cost, cancelReason }: ClineApiReqInfo = JSON.parse(lastApiReqStarted.text || "{}") + if (cost === undefined && cancelReason === undefined) { + modifiedClineMessages.splice(lastApiReqStartedIndex, 1) + } + } + + await this.overwriteClineMessages(modifiedClineMessages) + this.clineMessages = await this.getSavedClineMessages() + + // Now present the cline messages to the user and ask if they want to + // resume (NOTE: we ran into a bug before where the + // apiConversationHistory wouldn't be initialized when opening a old + // task, and it was because we were waiting for resume). + // This is important in case the user deletes messages without resuming + // the task first. + this.apiConversationHistory = await this.getSavedApiConversationHistory() + + const lastClineMessage = this.clineMessages + .slice() + .reverse() + .find((m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task")) // could be multiple resume tasks + + let askType: ClineAsk + if (lastClineMessage?.ask === "completion_result") { + askType = "resume_completed_task" + } else { + askType = "resume_task" + } + + this.isInitialized = true + + const { response, text, images } = await this.ask(askType) // calls poststatetowebview + let responseText: string | undefined + let responseImages: string[] | undefined + if (response === "messageResponse") { + await this.say("user_feedback", text, images) + responseText = text + responseImages = images + } + + // Make sure that the api conversation history can be resumed by the API, + // even if it goes out of sync with cline messages. + let existingApiConversationHistory: ApiMessage[] = await this.getSavedApiConversationHistory() + + // v2.0 xml tags refactor caveat: since we don't use tools anymore, we need to replace all tool use blocks with a text block since the API disallows conversations with tool uses and no tool schema + const conversationWithoutToolBlocks = existingApiConversationHistory.map((message) => { + if (Array.isArray(message.content)) { + const newContent = message.content.map((block) => { + if (block.type === "tool_use") { + // it's important we convert to the new tool schema format so the model doesn't get confused about how to invoke tools + const inputAsXml = Object.entries(block.input as Record) + .map(([key, value]) => `<${key}>\n${value}\n`) + .join("\n") + return { + type: "text", + text: `<${block.name}>\n${inputAsXml}\n`, + } as Anthropic.Messages.TextBlockParam + } else if (block.type === "tool_result") { + // Convert block.content to text block array, removing images + const contentAsTextBlocks = Array.isArray(block.content) + ? block.content.filter((item) => item.type === "text") + : [{ type: "text", text: block.content }] + const textContent = contentAsTextBlocks.map((item) => item.text).join("\n\n") + const toolName = findToolName(block.tool_use_id, existingApiConversationHistory) + return { + type: "text", + text: `[${toolName} Result]\n\n${textContent}`, + } as Anthropic.Messages.TextBlockParam + } + return block + }) + return { ...message, content: newContent } + } + return message + }) + existingApiConversationHistory = conversationWithoutToolBlocks + + // FIXME: remove tool use blocks altogether + + // if the last message is an assistant message, we need to check if there's tool use since every tool use has to have a tool response + // if there's no tool use and only a text block, then we can just add a user message + // (note this isn't relevant anymore since we use custom tool prompts instead of tool use blocks, but this is here for legacy purposes in case users resume old tasks) + + // if the last message is a user message, we can need to get the assistant message before it to see if it made tool calls, and if so, fill in the remaining tool responses with 'interrupted' + + let modifiedOldUserContent: Anthropic.Messages.ContentBlockParam[] // either the last message if its user message, or the user message before the last (assistant) message + let modifiedApiConversationHistory: ApiMessage[] // need to remove the last user message to replace with new modified user message + if (existingApiConversationHistory.length > 0) { + const lastMessage = existingApiConversationHistory[existingApiConversationHistory.length - 1] + + if (lastMessage.role === "assistant") { + const content = Array.isArray(lastMessage.content) + ? lastMessage.content + : [{ type: "text", text: lastMessage.content }] + const hasToolUse = content.some((block) => block.type === "tool_use") + + if (hasToolUse) { + const toolUseBlocks = content.filter( + (block) => block.type === "tool_use", + ) as Anthropic.Messages.ToolUseBlock[] + const toolResponses: Anthropic.ToolResultBlockParam[] = toolUseBlocks.map((block) => ({ + type: "tool_result", + tool_use_id: block.id, + content: "Task was interrupted before this tool call could be completed.", + })) + modifiedApiConversationHistory = [...existingApiConversationHistory] // no changes + modifiedOldUserContent = [...toolResponses] + } else { + modifiedApiConversationHistory = [...existingApiConversationHistory] + modifiedOldUserContent = [] + } + } else if (lastMessage.role === "user") { + const previousAssistantMessage: ApiMessage | undefined = + existingApiConversationHistory[existingApiConversationHistory.length - 2] + + const existingUserContent: Anthropic.Messages.ContentBlockParam[] = Array.isArray(lastMessage.content) + ? lastMessage.content + : [{ type: "text", text: lastMessage.content }] + if (previousAssistantMessage && previousAssistantMessage.role === "assistant") { + const assistantContent = Array.isArray(previousAssistantMessage.content) + ? previousAssistantMessage.content + : [{ type: "text", text: previousAssistantMessage.content }] + + const toolUseBlocks = assistantContent.filter( + (block) => block.type === "tool_use", + ) as Anthropic.Messages.ToolUseBlock[] + + if (toolUseBlocks.length > 0) { + const existingToolResults = existingUserContent.filter( + (block) => block.type === "tool_result", + ) as Anthropic.ToolResultBlockParam[] + + const missingToolResponses: Anthropic.ToolResultBlockParam[] = toolUseBlocks + .filter( + (toolUse) => !existingToolResults.some((result) => result.tool_use_id === toolUse.id), + ) + .map((toolUse) => ({ + type: "tool_result", + tool_use_id: toolUse.id, + content: "Task was interrupted before this tool call could be completed.", + })) + + modifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1) // removes the last user message + modifiedOldUserContent = [...existingUserContent, ...missingToolResponses] + } else { + modifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1) + modifiedOldUserContent = [...existingUserContent] + } + } else { + modifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1) + modifiedOldUserContent = [...existingUserContent] + } + } else { + throw new Error("Unexpected: Last message is not a user or assistant message") + } + } else { + throw new Error("Unexpected: No existing API conversation history") + } + + let newUserContent: Anthropic.Messages.ContentBlockParam[] = [...modifiedOldUserContent] + + const agoText = ((): string => { + const timestamp = lastClineMessage?.ts ?? Date.now() + const now = Date.now() + const diff = now - timestamp + const minutes = Math.floor(diff / 60000) + const hours = Math.floor(minutes / 60) + const days = Math.floor(hours / 24) + + if (days > 0) { + return `${days} day${days > 1 ? "s" : ""} ago` + } + if (hours > 0) { + return `${hours} hour${hours > 1 ? "s" : ""} ago` + } + if (minutes > 0) { + return `${minutes} minute${minutes > 1 ? "s" : ""} ago` + } + return "just now" + })() + + const lastTaskResumptionIndex = newUserContent.findIndex( + (x) => x.type === "text" && x.text.startsWith("[TASK RESUMPTION]"), + ) + if (lastTaskResumptionIndex !== -1) { + newUserContent.splice(lastTaskResumptionIndex, newUserContent.length - lastTaskResumptionIndex) + } + + const wasRecent = lastClineMessage?.ts && Date.now() - lastClineMessage.ts < 30_000 + + newUserContent.push({ + type: "text", + text: + `[TASK RESUMPTION] This task was interrupted ${agoText}. It may or may not be complete, so please reassess the task context. Be aware that the project state may have changed since then. If the task has not been completed, retry the last step before interruption and proceed with completing the task.\n\nNote: If you previously attempted a tool use that the user did not provide a result for, you should assume the tool use was not successful and assess whether you should retry. If the last tool was a browser_action, the browser has been closed and you must launch a new browser if needed.${ + wasRecent + ? "\n\nIMPORTANT: If the last tool use was a write_to_file that was interrupted, the file was reverted back to its original state before the interrupted edit, and you do NOT need to re-read the file as you already have its up-to-date contents." + : "" + }` + + (responseText + ? `\n\nNew instructions for task continuation:\n\n${responseText}\n` + : ""), + }) + + if (responseImages && responseImages.length > 0) { + newUserContent.push(...formatResponse.imageBlocks(responseImages)) + } + + await this.overwriteApiConversationHistory(modifiedApiConversationHistory) + + console.log(`[subtasks] task ${this.taskId}.${this.instanceId} resuming from history item`) + + await this.initiateTaskLoop(newUserContent) + } + + public async abortTask(isAbandoned = false) { + console.log(`[subtasks] aborting task ${this.taskId}.${this.instanceId}`) + + // Will stop any autonomously running promises. + if (isAbandoned) { + this.abandoned = true + } + + this.abort = true + this.emit("taskAborted") + + // Stop waiting for child task completion. + if (this.pauseInterval) { + clearInterval(this.pauseInterval) + this.pauseInterval = undefined + } + + // Release any terminals associated with this task. + TerminalRegistry.releaseTerminalsForTask(this.taskId) + + this.urlContentFetcher.closeBrowser() + this.browserSession.closeBrowser() + this.rooIgnoreController?.dispose() + this.fileContextTracker.dispose() + + // If we're not streaming then `abortStream` (which reverts the diff + // view changes) won't be called, so we need to revert the changes here. + if (this.isStreaming && this.diffViewProvider.isEditing) { + await this.diffViewProvider.revertChanges() + } + + // Save the countdown message in the automatic retry or other content. + await this.saveClineMessages() + } + + // Used when a sub-task is launched and the parent task is waiting for it to + // finish. + // TBD: The 1s should be added to the settings, also should add a timeout to + // prevent infinite waiting. + public async waitForResume() { + await new Promise((resolve) => { + this.pauseInterval = setInterval(() => { + if (!this.isPaused) { + clearInterval(this.pauseInterval) + this.pauseInterval = undefined + resolve() + } + }, 1000) + }) + } + + // Task Loop + + private async initiateTaskLoop(userContent: Anthropic.Messages.ContentBlockParam[]): Promise { + // Kicks off the checkpoints initialization process in the background. + getCheckpointService(this) + + let nextUserContent = userContent + let includeFileDetails = true + + this.emit("taskStarted") + + while (!this.abort) { + const didEndLoop = await this.recursivelyMakeClineRequests(nextUserContent, includeFileDetails) + includeFileDetails = false // we only need file details the first time + + // The way this agentic loop works is that cline will be given a + // task that he then calls tools to complete. Unless there's an + // attempt_completion call, we keep responding back to him with his + // tool's responses until he either attempt_completion or does not + // use anymore tools. If he does not use anymore tools, we ask him + // to consider if he's completed the task and then call + // attempt_completion, otherwise proceed with completing the task. + // There is a MAX_REQUESTS_PER_TASK limit to prevent infinite + // requests, but Cline is prompted to finish the task as efficiently + // as he can. + + if (didEndLoop) { + // For now a task never 'completes'. This will only happen if + // the user hits max requests and denies resetting the count. + break + } else { + nextUserContent = [{ type: "text", text: formatResponse.noToolsUsed() }] + this.consecutiveMistakeCount++ + } + } + } + + public async recursivelyMakeClineRequests( + userContent: Anthropic.Messages.ContentBlockParam[], + includeFileDetails: boolean = false, + ): Promise { + if (this.abort) { + throw new Error(`[Cline#recursivelyMakeClineRequests] task ${this.taskId}.${this.instanceId} aborted`) + } + + if (this.consecutiveMistakeCount >= this.consecutiveMistakeLimit) { + const { response, text, images } = await this.ask( + "mistake_limit_reached", + this.api.getModel().id.includes("claude") + ? `This may indicate a failure in his thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. "Try breaking down the task into smaller steps").` + : "Roo Code uses complex prompts and iterative task execution that may be challenging for less capable models. For best results, it's recommended to use Claude 3.7 Sonnet for its advanced agentic coding capabilities.", + ) + + if (response === "messageResponse") { + userContent.push( + ...[ + { type: "text" as const, text: formatResponse.tooManyMistakes(text) }, + ...formatResponse.imageBlocks(images), + ], + ) + + await this.say("user_feedback", text, images) + + // Track consecutive mistake errors in telemetry. + telemetryService.captureConsecutiveMistakeError(this.taskId) + } + + this.consecutiveMistakeCount = 0 + } + + // Get previous api req's index to check token usage and determine if we + // need to truncate conversation history. + const previousApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === "api_req_started") + + // In this Cline request loop, we need to check if this task instance + // has been asked to wait for a subtask to finish before continuing. + const provider = this.providerRef.deref() + + if (this.isPaused && provider) { + provider.log(`[subtasks] paused ${this.taskId}.${this.instanceId}`) + await this.waitForResume() + provider.log(`[subtasks] resumed ${this.taskId}.${this.instanceId}`) + const currentMode = (await provider.getState())?.mode ?? defaultModeSlug + + if (currentMode !== this.pausedModeSlug) { + // The mode has changed, we need to switch back to the paused mode. + await provider.handleModeSwitch(this.pausedModeSlug) + + // Delay to allow mode change to take effect before next tool is executed. + await delay(500) + + provider.log( + `[subtasks] task ${this.taskId}.${this.instanceId} has switched back to '${this.pausedModeSlug}' from '${currentMode}'`, + ) + } + } + + // Getting verbose details is an expensive operation, it uses ripgrep to + // top-down build file structure of project which for large projects can + // take a few seconds. For the best UX we show a placeholder api_req_started + // message with a loading spinner as this happens. + await this.say( + "api_req_started", + JSON.stringify({ + request: + userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n") + "\n\nLoading...", + }), + ) + + const parsedUserContent = await processUserContentMentions({ + userContent, + cwd: this.cwd, + urlContentFetcher: this.urlContentFetcher, + fileContextTracker: this.fileContextTracker, + }) + + const environmentDetails = await getEnvironmentDetails(this, includeFileDetails) + + // Add environment details as its own text block, separate from tool + // results. + const finalUserContent = [...parsedUserContent, { type: "text" as const, text: environmentDetails }] + + await this.addToApiConversationHistory({ role: "user", content: finalUserContent }) + telemetryService.captureConversationMessage(this.taskId, "user") + + // Since we sent off a placeholder api_req_started message to update the + // webview while waiting to actually start the API request (to load + // potential details for example), we need to update the text of that + // message. + const lastApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === "api_req_started") + + this.clineMessages[lastApiReqIndex].text = JSON.stringify({ + request: finalUserContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n"), + } satisfies ClineApiReqInfo) + + await this.saveClineMessages() + await provider?.postStateToWebview() + + try { + let cacheWriteTokens = 0 + let cacheReadTokens = 0 + let inputTokens = 0 + let outputTokens = 0 + let totalCost: number | undefined + + // We can't use `api_req_finished` anymore since it's a unique case + // where it could come after a streaming message (i.e. in the middle + // of being updated or executed). + // Fortunately `api_req_finished` was always parsed out for the GUI + // anyways, so it remains solely for legacy purposes to keep track + // of prices in tasks from history (it's worth removing a few months + // from now). + const updateApiReqMsg = (cancelReason?: ClineApiReqCancelReason, streamingFailedMessage?: string) => { + this.clineMessages[lastApiReqIndex].text = JSON.stringify({ + ...JSON.parse(this.clineMessages[lastApiReqIndex].text || "{}"), + tokensIn: inputTokens, + tokensOut: outputTokens, + cacheWrites: cacheWriteTokens, + cacheReads: cacheReadTokens, + cost: + totalCost ?? + calculateApiCostAnthropic( + this.api.getModel().info, + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + ), + cancelReason, + streamingFailedMessage, + } satisfies ClineApiReqInfo) + } + + const abortStream = async (cancelReason: ClineApiReqCancelReason, streamingFailedMessage?: string) => { + if (this.diffViewProvider.isEditing) { + await this.diffViewProvider.revertChanges() // closes diff view + } + + // if last message is a partial we need to update and save it + const lastMessage = this.clineMessages.at(-1) + + if (lastMessage && lastMessage.partial) { + // lastMessage.ts = Date.now() DO NOT update ts since it is used as a key for virtuoso list + lastMessage.partial = false + // instead of streaming partialMessage events, we do a save and post like normal to persist to disk + console.log("updating partial message", lastMessage) + // await this.saveClineMessages() + } + + // Let assistant know their response was interrupted for when task is resumed + await this.addToApiConversationHistory({ + role: "assistant", + content: [ + { + type: "text", + text: + assistantMessage + + `\n\n[${ + cancelReason === "streaming_failed" + ? "Response interrupted by API Error" + : "Response interrupted by user" + }]`, + }, + ], + }) + + // Update `api_req_started` to have cancelled and cost, so that + // we can display the cost of the partial stream. + updateApiReqMsg(cancelReason, streamingFailedMessage) + await this.saveClineMessages() + + // Signals to provider that it can retrieve the saved messages + // from disk, as abortTask can not be awaited on in nature. + this.didFinishAbortingStream = true + } + + // Reset streaming state. + this.currentStreamingContentIndex = 0 + this.assistantMessageContent = [] + this.didCompleteReadingStream = false + this.userMessageContent = [] + this.userMessageContentReady = false + this.didRejectTool = false + this.didAlreadyUseTool = false + this.presentAssistantMessageLocked = false + this.presentAssistantMessageHasPendingUpdates = false + + await this.diffViewProvider.reset() + + // Yields only if the first chunk is successful, otherwise will + // allow the user to retry the request (most likely due to rate + // limit error, which gets thrown on the first chunk). + const stream = this.attemptApiRequest(previousApiReqIndex) + let assistantMessage = "" + let reasoningMessage = "" + this.isStreaming = true + + try { + for await (const chunk of stream) { + if (!chunk) { + // Sometimes chunk is undefined, no idea that can cause + // it, but this workaround seems to fix it. + continue + } + + switch (chunk.type) { + case "reasoning": + reasoningMessage += chunk.text + await this.say("reasoning", reasoningMessage, undefined, true) + break + case "usage": + inputTokens += chunk.inputTokens + outputTokens += chunk.outputTokens + cacheWriteTokens += chunk.cacheWriteTokens ?? 0 + cacheReadTokens += chunk.cacheReadTokens ?? 0 + totalCost = chunk.totalCost + break + case "text": + assistantMessage += chunk.text + + // Parse raw assistant message into content blocks. + const prevLength = this.assistantMessageContent.length + this.assistantMessageContent = parseAssistantMessage(assistantMessage) + + if (this.assistantMessageContent.length > prevLength) { + // New content we need to present, reset to + // false in case previous content set this to true. + this.userMessageContentReady = false + } + + // Present content to user. + presentAssistantMessage(this) + break + } + + if (this.abort) { + console.log(`aborting stream, this.abandoned = ${this.abandoned}`) + + if (!this.abandoned) { + // Only need to gracefully abort if this instance + // isn't abandoned (sometimes OpenRouter stream + // hangs, in which case this would affect future + // instances of Cline). + await abortStream("user_cancelled") + } + + break // Aborts the stream. + } + + if (this.didRejectTool) { + // `userContent` has a tool rejection, so interrupt the + // assistant's response to present the user's feedback. + assistantMessage += "\n\n[Response interrupted by user feedback]" + // Instead of setting this premptively, we allow the + // present iterator to finish and set + // userMessageContentReady when its ready. + // this.userMessageContentReady = true + break + } + + // PREV: We need to let the request finish for openrouter to + // get generation details. + // UPDATE: It's better UX to interrupt the request at the + // cost of the API cost not being retrieved. + if (this.didAlreadyUseTool) { + assistantMessage += + "\n\n[Response interrupted by a tool use result. Only one tool may be used at a time and should be placed at the end of the message.]" + break + } + } + } catch (error) { + // Abandoned happens when extension is no longer waiting for the + // Cline instance to finish aborting (error is thrown here when + // any function in the for loop throws due to this.abort). + if (!this.abandoned) { + // If the stream failed, there's various states the task + // could be in (i.e. could have streamed some tools the user + // may have executed), so we just resort to replicating a + // cancel task. + this.abortTask() + + await abortStream( + "streaming_failed", + error.message ?? JSON.stringify(serializeError(error), null, 2), + ) + + const history = await provider?.getTaskWithId(this.taskId) + + if (history) { + await provider?.initClineWithHistoryItem(history.historyItem) + } + } + } finally { + this.isStreaming = false + } + + // Need to call here in case the stream was aborted. + if (this.abort || this.abandoned) { + throw new Error(`[Cline#recursivelyMakeClineRequests] task ${this.taskId}.${this.instanceId} aborted`) + } + + this.didCompleteReadingStream = true + + // Set any blocks to be complete to allow `presentAssistantMessage` + // to finish and set `userMessageContentReady` to true. + // (Could be a text block that had no subsequent tool uses, or a + // text block at the very end, or an invalid tool use, etc. Whatever + // the case, `presentAssistantMessage` relies on these blocks either + // to be completed or the user to reject a block in order to proceed + // and eventually set userMessageContentReady to true.) + const partialBlocks = this.assistantMessageContent.filter((block) => block.partial) + partialBlocks.forEach((block) => (block.partial = false)) + + // Can't just do this b/c a tool could be in the middle of executing. + // this.assistantMessageContent.forEach((e) => (e.partial = false)) + + if (partialBlocks.length > 0) { + // If there is content to update then it will complete and + // update `this.userMessageContentReady` to true, which we + // `pWaitFor` before making the next request. All this is really + // doing is presenting the last partial message that we just set + // to complete. + presentAssistantMessage(this) + } + + updateApiReqMsg() + await this.saveClineMessages() + await this.providerRef.deref()?.postStateToWebview() + + // Now add to apiConversationHistory. + // Need to save assistant responses to file before proceeding to + // tool use since user can exit at any moment and we wouldn't be + // able to save the assistant's response. + let didEndLoop = false + + if (assistantMessage.length > 0) { + await this.addToApiConversationHistory({ + role: "assistant", + content: [{ type: "text", text: assistantMessage }], + }) + + telemetryService.captureConversationMessage(this.taskId, "assistant") + + // NOTE: This comment is here for future reference - this was a + // workaround for `userMessageContent` not getting set to true. + // It was due to it not recursively calling for partial blocks + // when `didRejectTool`, so it would get stuck waiting for a + // partial block to complete before it could continue. + // In case the content blocks finished it may be the api stream + // finished after the last parsed content block was executed, so + // we are able to detect out of bounds and set + // `userMessageContentReady` to true (note you should not call + // `presentAssistantMessage` since if the last block i + // completed it will be presented again). + // const completeBlocks = this.assistantMessageContent.filter((block) => !block.partial) // If there are any partial blocks after the stream ended we can consider them invalid. + // if (this.currentStreamingContentIndex >= completeBlocks.length) { + // this.userMessageContentReady = true + // } + + await pWaitFor(() => this.userMessageContentReady) + + // If the model did not tool use, then we need to tell it to + // either use a tool or attempt_completion. + const didToolUse = this.assistantMessageContent.some((block) => block.type === "tool_use") + + if (!didToolUse) { + this.userMessageContent.push({ type: "text", text: formatResponse.noToolsUsed() }) + this.consecutiveMistakeCount++ + } + + const recDidEndLoop = await this.recursivelyMakeClineRequests(this.userMessageContent) + didEndLoop = recDidEndLoop + } else { + // If there's no assistant_responses, that means we got no text + // or tool_use content blocks from API which we should assume is + // an error. + await this.say( + "error", + "Oops! Something went wrong. Please check the notifications on the bottom right of the window for more details, or contact PearAI Support on Discord.", + ) + + await this.addToApiConversationHistory({ + role: "assistant", + content: [{ type: "text", text: "Failure: I did not provide a response." }], + }) + } + + return didEndLoop // Will always be false for now. + } catch (error) { + // This should never happen since the only thing that can throw an + // error is the attemptApiRequest, which is wrapped in a try catch + // that sends an ask where if noButtonClicked, will clear current + // task and destroy this instance. However to avoid unhandled + // promise rejection, we will end this loop which will end execution + // of this instance (see `startTask`). + return true // Needs to be true so parent loop knows to end task. + } + } + + public async *attemptApiRequest(previousApiReqIndex: number, retryAttempt: number = 0): ApiStream { + let mcpHub: McpHub | undefined + + const { apiConfiguration, mcpEnabled, autoApprovalEnabled, alwaysApproveResubmit, requestDelaySeconds } = + (await this.providerRef.deref()?.getState()) ?? {} + + let rateLimitDelay = 0 + + // Only apply rate limiting if this isn't the first request + if (this.lastApiRequestTime) { + const now = Date.now() + const timeSinceLastRequest = now - this.lastApiRequestTime + const rateLimit = apiConfiguration?.rateLimitSeconds || 0 + rateLimitDelay = Math.ceil(Math.max(0, rateLimit * 1000 - timeSinceLastRequest) / 1000) + } + + // Only show rate limiting message if we're not retrying. If retrying, we'll include the delay there. + if (rateLimitDelay > 0 && retryAttempt === 0) { + // Show countdown timer + for (let i = rateLimitDelay; i > 0; i--) { + const delayMessage = `Rate limiting for ${i} seconds...` + await this.say("api_req_retry_delayed", delayMessage, undefined, true) + await delay(1000) + } + } + + // Update last request time before making the request + this.lastApiRequestTime = Date.now() + + if (mcpEnabled ?? true) { + const provider = this.providerRef.deref() + + if (!provider) { + throw new Error("Provider reference lost during view transition") + } + + // Wait for MCP hub initialization through McpServerManager + mcpHub = await McpServerManager.getInstance(provider.context, provider) + + if (!mcpHub) { + throw new Error("Failed to get MCP hub from server manager") + } + + // Wait for MCP servers to be connected before generating system prompt + await pWaitFor(() => !mcpHub!.isConnecting, { timeout: 10_000 }).catch(() => { + console.error("MCP servers failed to connect in time") + }) + } + + const rooIgnoreInstructions = this.rooIgnoreController?.getInstructions() + + const { + browserViewportSize, + mode, + customModePrompts, + customInstructions, + experiments, + enableMcpServerCreation, + browserToolEnabled, + language, + } = (await this.providerRef.deref()?.getState()) ?? {} + + const { customModes } = (await this.providerRef.deref()?.getState()) ?? {} + + const systemPrompt = await (async () => { + const provider = this.providerRef.deref() + + if (!provider) { + throw new Error("Provider not available") + } + + return SYSTEM_PROMPT( + provider.context, + this.cwd, + (this.api.getModel().info.supportsComputerUse ?? false) && (browserToolEnabled ?? true), + mcpHub, + this.diffStrategy, + browserViewportSize, + mode, + customModePrompts, + customModes, + customInstructions, + this.diffEnabled, + experiments, + enableMcpServerCreation, + language, + rooIgnoreInstructions, + ) + })() + + // If the previous API request's total token usage is close to the + // context window, truncate the conversation history to free up space + // for the new request. + if (previousApiReqIndex >= 0) { + const previousRequest = this.clineMessages[previousApiReqIndex]?.text + + if (!previousRequest) { + return + } + + const { + tokensIn = 0, + tokensOut = 0, + cacheWrites = 0, + cacheReads = 0, + }: ClineApiReqInfo = JSON.parse(previousRequest) + + const totalTokens = tokensIn + tokensOut + cacheWrites + cacheReads + + // Default max tokens value for thinking models when no specific + // value is set. + const DEFAULT_THINKING_MODEL_MAX_TOKENS = 16_384 + + const modelInfo = this.api.getModel().info + + const maxTokens = modelInfo.thinking + ? this.apiConfiguration.modelMaxTokens || DEFAULT_THINKING_MODEL_MAX_TOKENS + : modelInfo.maxTokens + + const contextWindow = modelInfo.contextWindow + + const autoCondenseContext = experiments?.autoCondenseContext ?? false + const trimmedMessages = await truncateConversationIfNeeded({ + messages: this.apiConversationHistory, + totalTokens, + maxTokens, + contextWindow, + apiHandler: this.api, + autoCondenseContext, + }) + if (trimmedMessages !== this.apiConversationHistory) { + await this.overwriteApiConversationHistory(trimmedMessages) + } + } + + const messagesSinceLastSummary = getMessagesSinceLastSummary(this.apiConversationHistory) + const cleanConversationHistory = maybeRemoveImageBlocks(messagesSinceLastSummary, this.api).map( + ({ role, content }) => ({ role, content }), + ) + + const stream = this.api.createMessage(systemPrompt, cleanConversationHistory) + const iterator = stream[Symbol.asyncIterator]() + + try { + // Awaiting first chunk to see if it will throw an error. + this.isWaitingForFirstChunk = true + const firstChunk = await iterator.next() + yield firstChunk.value + this.isWaitingForFirstChunk = false + } catch (error) { + this.isWaitingForFirstChunk = false + // note that this api_req_failed ask is unique in that we only present this option if the api hasn't streamed any content yet (ie it fails on the first chunk due), as it would allow them to hit a retry button. However if the api failed mid-stream, it could be in any arbitrary state where some tools may have executed, so that error is handled differently and requires cancelling the task entirely. + if (autoApprovalEnabled && alwaysApproveResubmit) { + let errorMsg + + if (error.error?.metadata?.raw) { + errorMsg = JSON.stringify(error.error.metadata.raw, null, 2) + } else if (error.message) { + errorMsg = error.message + } else { + errorMsg = "Unknown error" + } + + const baseDelay = requestDelaySeconds || 5 + let exponentialDelay = Math.ceil(baseDelay * Math.pow(2, retryAttempt)) + + // If the error is a 429, and the error details contain a retry delay, use that delay instead of exponential backoff + if (error.status === 429) { + const geminiRetryDetails = error.errorDetails?.find( + (detail: any) => detail["@type"] === "type.googleapis.com/google.rpc.RetryInfo", + ) + if (geminiRetryDetails) { + const match = geminiRetryDetails?.retryDelay?.match(/^(\d+)s$/) + if (match) { + exponentialDelay = Number(match[1]) + 1 + } + } + } + + // Wait for the greater of the exponential delay or the rate limit delay + const finalDelay = Math.max(exponentialDelay, rateLimitDelay) + + // Show countdown timer with exponential backoff + for (let i = finalDelay; i > 0; i--) { + await this.say( + "api_req_retry_delayed", + `${errorMsg}\n\nRetry attempt ${retryAttempt + 1}\nRetrying in ${i} seconds...`, + undefined, + true, + ) + await delay(1000) + } + + await this.say( + "api_req_retry_delayed", + `${errorMsg}\n\nRetry attempt ${retryAttempt + 1}\nRetrying now...`, + undefined, + false, + ) + + // Delegate generator output from the recursive call with + // incremented retry count. + yield* this.attemptApiRequest(previousApiReqIndex, retryAttempt + 1) + + return + } else { + const { response } = await this.ask( + "api_req_failed", + error.message ?? JSON.stringify(serializeError(error), null, 2), + ) + + if (response !== "yesButtonClicked") { + // This will never happen since if noButtonClicked, we will + // clear current task, aborting this instance. + throw new Error("API request failed") + } + + await this.say("api_req_retried") + + // Delegate generator output from the recursive call. + yield* this.attemptApiRequest(previousApiReqIndex) + return + } + } + + // No error, so we can continue to yield all remaining chunks. + // (Needs to be placed outside of try/catch since it we want caller to + // handle errors not with api_req_failed as that is reserved for first + // chunk failures only.) + // This delegates to another generator or iterable object. In this case, + // it's saying "yield all remaining values from this iterator". This + // effectively passes along all subsequent chunks from the original + // stream. + yield* iterator + } + + // Checkpoints + + public async checkpointSave() { + return checkpointSave(this) + } + + public async checkpointRestore(options: CheckpointRestoreOptions) { + return checkpointRestore(this, options) + } + + public async checkpointDiff(options: CheckpointDiffOptions) { + return checkpointDiff(this, options) + } + + // Metrics + + public combineMessages(messages: ClineMessage[]) { + return combineApiRequests(combineCommandSequences(messages)) + } + + public getTokenUsage() { + return getApiMetrics(this.combineMessages(this.clineMessages.slice(1))) + } + + public recordToolUsage(toolName: ToolName) { + if (!this.toolUsage[toolName]) { + this.toolUsage[toolName] = { attempts: 0, failures: 0 } + } + + this.toolUsage[toolName].attempts++ + } + + public recordToolError(toolName: ToolName, error?: string) { + if (!this.toolUsage[toolName]) { + this.toolUsage[toolName] = { attempts: 0, failures: 0 } + } + + this.toolUsage[toolName].failures++ + + if (error) { + this.emit("taskToolFailed", this.taskId, toolName, error) + } + } + + // Getters + + public get cwd() { + return this.workspacePath + } +} diff --git a/src/core/__tests__/Cline.test.ts b/src/core/task/__tests__/Task.test.ts similarity index 80% rename from src/core/__tests__/Cline.test.ts rename to src/core/task/__tests__/Task.test.ts index 00a9c4dc6bf..84756b8a2de 100644 --- a/src/core/__tests__/Cline.test.ts +++ b/src/core/task/__tests__/Task.test.ts @@ -1,4 +1,4 @@ -// npx jest src/core/__tests__/Cline.test.ts +// npx jest src/core/task/__tests__/Task.test.ts import * as os from "os" import * as path from "path" @@ -6,47 +6,18 @@ import * as path from "path" import * as vscode from "vscode" import { Anthropic } from "@anthropic-ai/sdk" -import { GlobalState } from "../../schemas" -import { Cline } from "../Cline" -import { ClineProvider } from "../webview/ClineProvider" -import { ApiConfiguration, ModelInfo } from "../../shared/api" -import { ApiStreamChunk } from "../../api/transform/stream" -import { ContextProxy } from "../config/ContextProxy" +import { GlobalState } from "../../../schemas" +import { Task } from "../Task" +import { ClineProvider } from "../../webview/ClineProvider" +import { ProviderSettings, ModelInfo } from "../../../shared/api" +import { ApiStreamChunk } from "../../../api/transform/stream" +import { ContextProxy } from "../../config/ContextProxy" +import { processUserContentMentions } from "../../mentions/processUserContentMentions" jest.mock("execa", () => ({ execa: jest.fn(), })) -// Mock RooIgnoreController -jest.mock("../ignore/RooIgnoreController") - -// Mock storagePathManager to prevent dynamic import issues -jest.mock("../../shared/storagePathManager", () => ({ - getTaskDirectoryPath: jest - .fn() - .mockImplementation((globalStoragePath, taskId) => Promise.resolve(`${globalStoragePath}/tasks/${taskId}`)), - getSettingsDirectoryPath: jest - .fn() - .mockImplementation((globalStoragePath) => Promise.resolve(`${globalStoragePath}/settings`)), -})) - -// Mock fileExistsAtPath -jest.mock("../../utils/fs", () => ({ - fileExistsAtPath: jest.fn().mockImplementation((filePath) => { - return filePath.includes("ui_messages.json") || filePath.includes("api_conversation_history.json") - }), -})) - -// Mock fs/promises -const mockMessages = [ - { - ts: Date.now(), - type: "say", - say: "text", - text: "historical task", - }, -] - jest.mock("fs/promises", () => ({ mkdir: jest.fn().mockResolvedValue(undefined), writeFile: jest.fn().mockResolvedValue(undefined), @@ -76,37 +47,21 @@ jest.mock("fs/promises", () => ({ rmdir: jest.fn().mockResolvedValue(undefined), })) -// Mock dependencies +jest.mock("p-wait-for", () => ({ + __esModule: true, + default: jest.fn().mockImplementation(async () => Promise.resolve()), +})) + jest.mock("vscode", () => { const mockDisposable = { dispose: jest.fn() } - const mockEventEmitter = { - event: jest.fn(), - fire: jest.fn(), - } - - const mockTextDocument = { - uri: { - fsPath: "/mock/workspace/path/file.ts", - }, - } - - const mockTextEditor = { - document: mockTextDocument, - } - - const mockTab = { - input: { - uri: { - fsPath: "/mock/workspace/path/file.ts", - }, - }, - } - - const mockTabGroup = { - tabs: [mockTab], - } + const mockEventEmitter = { event: jest.fn(), fire: jest.fn() } + const mockTextDocument = { uri: { fsPath: "/mock/workspace/path/file.ts" } } + const mockTextEditor = { document: mockTextDocument } + const mockTab = { input: { uri: { fsPath: "/mock/workspace/path/file.ts" } } } + const mockTabGroup = { tabs: [mockTab] } return { + TabInputTextDiff: jest.fn(), CodeActionKind: { QuickFix: { value: "quickfix" }, RefactorRewrite: { value: "refactor.rewrite" }, @@ -118,6 +73,7 @@ jest.mock("vscode", () => { visibleTextEditors: [mockTextEditor], tabGroups: { all: [mockTabGroup], + close: jest.fn(), onDidChangeTabs: jest.fn(() => ({ dispose: jest.fn() })), }, showErrorMessage: jest.fn(), @@ -125,9 +81,7 @@ jest.mock("vscode", () => { workspace: { workspaceFolders: [ { - uri: { - fsPath: "/mock/workspace/path", - }, + uri: { fsPath: "/mock/workspace/path" }, name: "mock-workspace", index: 0, }, @@ -156,15 +110,55 @@ jest.mock("vscode", () => { } }) -// Mock p-wait-for to resolve immediately -jest.mock("p-wait-for", () => ({ - __esModule: true, - default: jest.fn().mockImplementation(async () => Promise.resolve()), +jest.mock("../../mentions", () => ({ + parseMentions: jest.fn().mockImplementation((text) => { + return Promise.resolve(`processed: ${text}`) + }), + openMention: jest.fn(), + getLatestTerminalOutput: jest.fn(), +})) + +jest.mock("../../../integrations/misc/extract-text", () => ({ + extractTextFromFile: jest.fn().mockResolvedValue("Mock file content"), +})) + +jest.mock("../../environment/getEnvironmentDetails", () => ({ + getEnvironmentDetails: jest.fn().mockResolvedValue(""), +})) + +// Mock RooIgnoreController +jest.mock("../../ignore/RooIgnoreController") + +// Mock storagePathManager to prevent dynamic import issues +jest.mock("../../../shared/storagePathManager", () => ({ + getTaskDirectoryPath: jest + .fn() + .mockImplementation((globalStoragePath, taskId) => Promise.resolve(`${globalStoragePath}/tasks/${taskId}`)), + getSettingsDirectoryPath: jest + .fn() + .mockImplementation((globalStoragePath) => Promise.resolve(`${globalStoragePath}/settings`)), +})) + +// Mock fileExistsAtPath +jest.mock("../../../utils/fs", () => ({ + fileExistsAtPath: jest.fn().mockImplementation((filePath) => { + return filePath.includes("ui_messages.json") || filePath.includes("api_conversation_history.json") + }), })) +// Mock fs/promises +const mockMessages = [ + { + ts: Date.now(), + type: "say", + say: "text", + text: "historical task", + }, +] + describe("Cline", () => { let mockProvider: jest.Mocked - let mockApiConfig: ApiConfiguration + let mockApiConfig: ProviderSettings let mockOutputChannel: any let mockExtensionContext: vscode.ExtensionContext @@ -278,24 +272,21 @@ describe("Cline", () => { describe("constructor", () => { it("should respect provided settings", async () => { - const cline = new Cline({ + const cline = new Task({ provider: mockProvider, apiConfiguration: mockApiConfig, - customInstructions: "custom instructions", fuzzyMatchThreshold: 0.95, task: "test task", startTask: false, }) - expect(cline.customInstructions).toBe("custom instructions") expect(cline.diffEnabled).toBe(false) }) it("should use default fuzzy match threshold when not provided", async () => { - const cline = new Cline({ + const cline = new Task({ provider: mockProvider, apiConfiguration: mockApiConfig, - customInstructions: "custom instructions", enableDiff: true, fuzzyMatchThreshold: 0.95, task: "test task", @@ -310,99 +301,16 @@ describe("Cline", () => { it("should require either task or historyItem", () => { expect(() => { - new Cline({ provider: mockProvider, apiConfiguration: mockApiConfig }) + new Task({ provider: mockProvider, apiConfiguration: mockApiConfig }) }).toThrow("Either historyItem or task/images must be provided") }) }) describe("getEnvironmentDetails", () => { - let originalDate: DateConstructor - let mockDate: Date - - beforeEach(() => { - originalDate = global.Date - const fixedTime = new Date("2024-01-01T12:00:00Z") - mockDate = new Date(fixedTime) - mockDate.getTimezoneOffset = jest.fn().mockReturnValue(420) // UTC-7 - - class MockDate extends Date { - constructor() { - super() - return mockDate - } - static override now() { - return mockDate.getTime() - } - } - - global.Date = MockDate as DateConstructor - - // Create a proper mock of Intl.DateTimeFormat - const mockDateTimeFormat = { - resolvedOptions: () => ({ - timeZone: "America/Los_Angeles", - }), - format: () => "1/1/2024, 5:00:00 AM", - } - - const MockDateTimeFormat = function (this: any) { - return mockDateTimeFormat - } as any - - MockDateTimeFormat.prototype = mockDateTimeFormat - MockDateTimeFormat.supportedLocalesOf = jest.fn().mockReturnValue(["en-US"]) - - global.Intl.DateTimeFormat = MockDateTimeFormat - }) - - afterEach(() => { - global.Date = originalDate - }) - - it("should include timezone information in environment details", async () => { - const cline = new Cline({ - provider: mockProvider, - apiConfiguration: mockApiConfig, - task: "test task", - startTask: false, - }) - - const details = await cline["getEnvironmentDetails"](false) - - // Verify timezone information is present and formatted correctly. - expect(details).toContain("America/Los_Angeles") - expect(details).toMatch(/UTC-7:00/) // Fixed offset for America/Los_Angeles. - expect(details).toContain("# Current Time") - expect(details).toMatch(/1\/1\/2024.*5:00:00 AM.*\(America\/Los_Angeles, UTC-7:00\)/) // Full time string format. - }) - describe("API conversation handling", () => { - /** - * Mock environment details retrieval to avoid filesystem access in tests - * - * This setup: - * 1. Prevents file listing operations that might cause test instability - * 2. Preserves test-specific mocks when they exist (via _mockGetEnvironmentDetails) - * 3. Provides a stable, empty environment by default - */ - beforeEach(() => { - // Mock the method with a stable implementation - jest.spyOn(Cline.prototype, "getEnvironmentDetails").mockImplementation( - // Use 'any' type to allow for dynamic test properties - async function (this: any, _verbose: boolean = false): Promise { - // Use test-specific mock if available - if (this._mockGetEnvironmentDetails) { - return this._mockGetEnvironmentDetails() - } - // Default to empty environment details for stability - return "" - }, - ) - }) - it("should clean conversation history before sending to API", async () => { // Cline.create will now use our mocked getEnvironmentDetails - const [cline, task] = Cline.create({ + const [cline, task] = Task.create({ provider: mockProvider, apiConfiguration: mockApiConfig, task: "test task", @@ -420,12 +328,6 @@ describe("Cline", () => { const cleanMessageSpy = jest.fn().mockReturnValue(mockStreamForClean) jest.spyOn(cline.api, "createMessage").mockImplementation(cleanMessageSpy) - // Mock getEnvironmentDetails to return empty details. - jest.spyOn(cline as any, "getEnvironmentDetails").mockResolvedValue("") - - // Mock loadContext to return unmodified content. - jest.spyOn(cline as any, "loadContext").mockImplementation(async (content) => [content, ""]) - // Add test message to conversation history. cline.apiConversationHistory = [ { @@ -516,7 +418,7 @@ describe("Cline", () => { ] // Test with model that supports images - const [clineWithImages, taskWithImages] = Cline.create({ + const [clineWithImages, taskWithImages] = Task.create({ provider: mockProvider, apiConfiguration: configWithImages, task: "test task", @@ -539,7 +441,7 @@ describe("Cline", () => { clineWithImages.apiConversationHistory = conversationHistory // Test with model that doesn't support images - const [clineWithoutImages, taskWithoutImages] = Cline.create({ + const [clineWithoutImages, taskWithoutImages] = Task.create({ provider: mockProvider, apiConfiguration: configWithoutImages, task: "test task", @@ -574,15 +476,6 @@ describe("Cline", () => { configurable: true, }) - // Mock environment details and context loading - jest.spyOn(clineWithImages as any, "getEnvironmentDetails").mockResolvedValue("") - jest.spyOn(clineWithoutImages as any, "getEnvironmentDetails").mockResolvedValue("") - jest.spyOn(clineWithImages as any, "loadContext").mockImplementation(async (content) => [content, ""]) - jest.spyOn(clineWithoutImages as any, "loadContext").mockImplementation(async (content) => [ - content, - "", - ]) - // Set up mock streams const mockStreamWithImages = (async function* () { yield { type: "text", text: "test response" } @@ -639,7 +532,7 @@ describe("Cline", () => { }) it.skip("should handle API retry with countdown", async () => { - const [cline, task] = Cline.create({ + const [cline, task] = Task.create({ provider: mockProvider, apiConfiguration: mockApiConfig, task: "test task", @@ -763,7 +656,7 @@ describe("Cline", () => { }) it.skip("should not apply retry delay twice", async () => { - const [cline, task] = Cline.create({ + const [cline, task] = Task.create({ provider: mockProvider, apiConfiguration: mockApiConfig, task: "test task", @@ -885,18 +778,14 @@ describe("Cline", () => { await task.catch(() => {}) }) - describe("loadContext", () => { + describe("processUserContentMentions", () => { it("should process mentions in task and feedback tags", async () => { - const [cline, task] = Cline.create({ + const [cline, task] = Task.create({ provider: mockProvider, apiConfiguration: mockApiConfig, task: "test task", }) - // Mock parseMentions to track calls - const mockParseMentions = jest.fn().mockImplementation((text) => `processed: ${text}`) - jest.spyOn(require("../../core/mentions"), "parseMentions").mockImplementation(mockParseMentions) - const userContent = [ { type: "text", @@ -928,30 +817,28 @@ describe("Cline", () => { } as Anthropic.ToolResultBlockParam, ] - // Process the content - const [processedContent] = await cline["loadContext"](userContent) + const processedContent = await processUserContentMentions({ + userContent, + cwd: cline.cwd, + urlContentFetcher: cline.urlContentFetcher, + fileContextTracker: cline.fileContextTracker, + }) // Regular text should not be processed expect((processedContent[0] as Anthropic.TextBlockParam).text).toBe("Regular text with @/some/path") // Text within task tags should be processed expect((processedContent[1] as Anthropic.TextBlockParam).text).toContain("processed:") - expect(mockParseMentions).toHaveBeenCalledWith( + expect((processedContent[1] as Anthropic.TextBlockParam).text).toContain( "Text with @/some/path in task tags", - expect.any(String), - expect.any(Object), - expect.any(Object), ) // Feedback tag content should be processed const toolResult1 = processedContent[2] as Anthropic.ToolResultBlockParam const content1 = Array.isArray(toolResult1.content) ? toolResult1.content[0] : toolResult1.content expect((content1 as Anthropic.TextBlockParam).text).toContain("processed:") - expect(mockParseMentions).toHaveBeenCalledWith( + expect((content1 as Anthropic.TextBlockParam).text).toContain( "Check @/some/path", - expect.any(String), - expect.any(Object), - expect.any(Object), ) // Regular tool result should not be processed diff --git a/src/core/tools/ToolRepetitionDetector.ts b/src/core/tools/ToolRepetitionDetector.ts new file mode 100644 index 00000000000..a82574ba0e3 --- /dev/null +++ b/src/core/tools/ToolRepetitionDetector.ts @@ -0,0 +1,95 @@ +import { ToolUse } from "../../shared/tools" +import { t } from "../../i18n" + +/** + * Class for detecting consecutive identical tool calls + * to prevent the AI from getting stuck in a loop. + */ +export class ToolRepetitionDetector { + private previousToolCallJson: string | null = null + private consecutiveIdenticalToolCallCount: number = 0 + private readonly consecutiveIdenticalToolCallLimit: number + + /** + * Creates a new ToolRepetitionDetector + * @param limit The maximum number of identical consecutive tool calls allowed + */ + constructor(limit: number = 3) { + this.consecutiveIdenticalToolCallLimit = limit + } + + /** + * Checks if the current tool call is identical to the previous one + * and determines if execution should be allowed + * + * @param currentToolCallBlock ToolUse object representing the current tool call + * @returns Object indicating if execution is allowed and a message to show if not + */ + public check(currentToolCallBlock: ToolUse): { + allowExecution: boolean + askUser?: { + messageKey: string + messageDetail: string + } + } { + // Serialize the block to a canonical JSON string for comparison + const currentToolCallJson = this.serializeToolUse(currentToolCallBlock) + + // Compare with previous tool call + if (this.previousToolCallJson === currentToolCallJson) { + this.consecutiveIdenticalToolCallCount++ + } else { + this.consecutiveIdenticalToolCallCount = 1 // Start with 1 for the first occurrence + this.previousToolCallJson = currentToolCallJson + } + + // Check if limit is reached + if (this.consecutiveIdenticalToolCallCount >= this.consecutiveIdenticalToolCallLimit) { + // Reset counters to allow recovery if user guides the AI past this point + this.consecutiveIdenticalToolCallCount = 0 + this.previousToolCallJson = null + + // Return result indicating execution should not be allowed + return { + allowExecution: false, + askUser: { + messageKey: "mistake_limit_reached", + messageDetail: t("tools:toolRepetitionLimitReached", { toolName: currentToolCallBlock.name }), + }, + } + } + + // Execution is allowed + return { allowExecution: true } + } + + /** + * Serializes a ToolUse object into a canonical JSON string for comparison + * + * @param toolUse The ToolUse object to serialize + * @returns JSON string representation of the tool use with sorted parameter keys + */ + private serializeToolUse(toolUse: ToolUse): string { + // Create a new parameters object with alphabetically sorted keys + const sortedParams: Record = {} + + // Get parameter keys and sort them alphabetically + const sortedKeys = Object.keys(toolUse.params).sort() + + // Populate the sorted parameters object in a type-safe way + for (const key of sortedKeys) { + if (Object.prototype.hasOwnProperty.call(toolUse.params, key)) { + sortedParams[key] = toolUse.params[key as keyof typeof toolUse.params] + } + } + + // Create the object with the tool name and sorted parameters + const toolObject = { + name: toolUse.name, + parameters: sortedParams, + } + + // Convert to a canonical JSON string + return JSON.stringify(toolObject) + } +} diff --git a/src/core/tools/__tests__/ToolRepetitionDetector.test.ts b/src/core/tools/__tests__/ToolRepetitionDetector.test.ts new file mode 100644 index 00000000000..846011b5d82 --- /dev/null +++ b/src/core/tools/__tests__/ToolRepetitionDetector.test.ts @@ -0,0 +1,304 @@ +// npx jest src/core/tools/__tests__/ToolRepetitionDetector.test.ts + +import type { ToolName } from "../../../schemas" +import type { ToolUse } from "../../../shared/tools" + +import { ToolRepetitionDetector } from "../ToolRepetitionDetector" + +jest.mock("../../../i18n", () => ({ + t: jest.fn((key, options) => { + // For toolRepetitionLimitReached key, return a message with the tool name. + if (key === "tools:toolRepetitionLimitReached" && options?.toolName) { + return `Roo appears to be stuck in a loop, attempting the same action (${options.toolName}) repeatedly. This might indicate a problem with its current strategy.` + } + return key + }), +})) + +function createToolUse(name: string, displayName?: string, params: Record = {}): ToolUse { + return { + type: "tool_use", + name: (displayName || name) as ToolName, + params, + partial: false, + } +} + +describe("ToolRepetitionDetector", () => { + // ===== Initialization tests ===== + describe("initialization", () => { + it("should default to a limit of 3 if no argument provided", () => { + const detector = new ToolRepetitionDetector() + // We'll verify this through behavior in subsequent tests + + // First call (counter = 1) + const result1 = detector.check(createToolUse("test", "test-tool")) + expect(result1.allowExecution).toBe(true) + + // Second identical call (counter = 2) + const result2 = detector.check(createToolUse("test", "test-tool")) + expect(result2.allowExecution).toBe(true) + + // Third identical call (counter = 3) reaches the default limit + const result3 = detector.check(createToolUse("test", "test-tool")) + expect(result3.allowExecution).toBe(false) + }) + + it("should use the custom limit when provided", () => { + const customLimit = 2 + const detector = new ToolRepetitionDetector(customLimit) + + // First call (counter = 1) + const result1 = detector.check(createToolUse("test", "test-tool")) + expect(result1.allowExecution).toBe(true) + + // Second identical call (counter = 2) reaches the custom limit + const result2 = detector.check(createToolUse("test", "test-tool")) + expect(result2.allowExecution).toBe(false) + }) + }) + + // ===== No Repetition tests ===== + describe("no repetition", () => { + it("should allow execution for different tool calls", () => { + const detector = new ToolRepetitionDetector() + + const result1 = detector.check(createToolUse("first", "first-tool")) + expect(result1.allowExecution).toBe(true) + expect(result1.askUser).toBeUndefined() + + const result2 = detector.check(createToolUse("second", "second-tool")) + expect(result2.allowExecution).toBe(true) + expect(result2.askUser).toBeUndefined() + + const result3 = detector.check(createToolUse("third", "third-tool")) + expect(result3.allowExecution).toBe(true) + expect(result3.askUser).toBeUndefined() + }) + + it("should reset the counter when different tool calls are made", () => { + const detector = new ToolRepetitionDetector(2) + + // First call + detector.check(createToolUse("same", "same-tool")) + + // Second identical call would reach limit of 2, but we'll make a different call + detector.check(createToolUse("different", "different-tool")) + + // Back to the first tool - should be allowed since counter was reset + const result = detector.check(createToolUse("same", "same-tool")) + expect(result.allowExecution).toBe(true) + }) + }) + + // ===== Repetition Below Limit tests ===== + describe("repetition below limit", () => { + it("should allow execution when repetition is below limit and block when limit reached", () => { + const detector = new ToolRepetitionDetector(3) + + // First call (counter = 1) + const result1 = detector.check(createToolUse("repeat", "repeat-tool")) + expect(result1.allowExecution).toBe(true) + + // Second identical call (counter = 2) + const result2 = detector.check(createToolUse("repeat", "repeat-tool")) + expect(result2.allowExecution).toBe(true) + + // Third identical call (counter = 3) reaches limit + const result3 = detector.check(createToolUse("repeat", "repeat-tool")) + expect(result3.allowExecution).toBe(false) + }) + }) + + // ===== Repetition Reaches Limit tests ===== + describe("repetition reaches limit", () => { + it("should block execution when repetition reaches the limit", () => { + const detector = new ToolRepetitionDetector(3) + + // First call (counter = 1) + detector.check(createToolUse("repeat", "repeat-tool")) + + // Second identical call (counter = 2) + detector.check(createToolUse("repeat", "repeat-tool")) + + // Third identical call (counter = 3) - should reach limit + const result = detector.check(createToolUse("repeat", "repeat-tool")) + + expect(result.allowExecution).toBe(false) + expect(result.askUser).toBeDefined() + expect(result.askUser?.messageKey).toBe("mistake_limit_reached") + expect(result.askUser?.messageDetail).toContain("repeat-tool") + }) + + it("should reset internal state after limit is reached", () => { + const detector = new ToolRepetitionDetector(2) + + // Reach the limit + detector.check(createToolUse("repeat", "repeat-tool")) + const limitResult = detector.check(createToolUse("repeat", "repeat-tool")) // This reaches limit + expect(limitResult.allowExecution).toBe(false) + + // Use a new tool call - should be allowed since state was reset + const result = detector.check(createToolUse("new", "new-tool")) + expect(result.allowExecution).toBe(true) + }) + }) + + // ===== Repetition After Limit (Post-Reset) tests ===== + describe("repetition after limit", () => { + it("should allow execution of previously problematic tool after reset", () => { + const detector = new ToolRepetitionDetector(2) + + // Reach the limit with a specific tool + detector.check(createToolUse("problem", "problem-tool")) + const limitResult = detector.check(createToolUse("problem", "problem-tool")) // This reaches limit + expect(limitResult.allowExecution).toBe(false) + + // The same tool that previously caused problems should now be allowed + const result = detector.check(createToolUse("problem", "problem-tool")) + expect(result.allowExecution).toBe(true) + }) + + it("should require reaching the limit again after reset", () => { + const detector = new ToolRepetitionDetector(2) + + // Reach the limit + detector.check(createToolUse("repeat", "repeat-tool")) + const limitResult = detector.check(createToolUse("repeat", "repeat-tool")) // This reaches limit + expect(limitResult.allowExecution).toBe(false) + + // First call after reset + detector.check(createToolUse("repeat", "repeat-tool")) + + // Second identical call (counter = 2) should reach limit again + const result = detector.check(createToolUse("repeat", "repeat-tool")) + expect(result.allowExecution).toBe(false) + expect(result.askUser).toBeDefined() + }) + }) + + // ===== Tool Name Interpolation tests ===== + describe("tool name interpolation", () => { + it("should include tool name in the error message", () => { + const detector = new ToolRepetitionDetector(2) + const toolName = "special-tool-name" + + // Reach the limit + detector.check(createToolUse("test", toolName)) + const result = detector.check(createToolUse("test", toolName)) + + expect(result.allowExecution).toBe(false) + expect(result.askUser?.messageDetail).toContain(toolName) + }) + }) + + // ===== Edge Cases ===== + describe("edge cases", () => { + it("should handle empty tool call", () => { + const detector = new ToolRepetitionDetector(2) + + // Create an empty tool call - a tool with no parameters + // Use the empty tool directly in the check calls + detector.check(createToolUse("empty-tool", "empty-tool")) + const result = detector.check(createToolUse("empty-tool")) + + expect(result.allowExecution).toBe(false) + expect(result.askUser).toBeDefined() + }) + + it("should handle different tool names with identical serialized JSON", () => { + const detector = new ToolRepetitionDetector(2) + + // First, call with tool-name-1 twice to set up the counter + const toolUse1 = createToolUse("tool-name-1", "tool-name-1", { param: "value" }) + detector.check(toolUse1) + + // Create a tool that will serialize to the same JSON as toolUse1 + // We need to mock the serializeToolUse method to return the same value + const toolUse2 = createToolUse("tool-name-2", "tool-name-2", { param: "value" }) + + // Override the private method to force identical serialization + const originalSerialize = (detector as any).serializeToolUse + ;(detector as any).serializeToolUse = (tool: ToolUse) => { + // Use string comparison for the name since it's technically an enum + if (String(tool.name) === "tool-name-2") { + return (detector as any).serializeToolUse(toolUse1) // Return the same JSON as toolUse1 + } + return originalSerialize(tool) + } + + // This should detect as a repetition now + const result = detector.check(toolUse2) + + // Restore the original method + ;(detector as any).serializeToolUse = originalSerialize + + // Since we're directly manipulating the internal state for testing, + // we still expect it to consider this a repetition + expect(result.allowExecution).toBe(false) + expect(result.askUser).toBeDefined() + }) + + it("should treat tools with same parameters in different order as identical", () => { + const detector = new ToolRepetitionDetector(2) + + // First call with parameters in one order + const toolUse1 = createToolUse("same-tool", "same-tool", { a: "1", b: "2", c: "3" }) + detector.check(toolUse1) + + // Create tool with same parameters but in different order + const toolUse2 = createToolUse("same-tool", "same-tool", { c: "3", a: "1", b: "2" }) + + // This should still detect as a repetition due to canonical JSON with sorted keys + const result = detector.check(toolUse2) + + // Since parameters are sorted alphabetically in the serialized JSON, + // these should be considered identical + expect(result.allowExecution).toBe(false) + expect(result.askUser).toBeDefined() + }) + }) + + // ===== Explicit Nth Call Blocking tests ===== + describe("explicit Nth call blocking behavior", () => { + it("should block on the 1st call for limit 1", () => { + const detector = new ToolRepetitionDetector(1) + + // First call (counter = 1) should be blocked + const result = detector.check(createToolUse("tool", "tool-name")) + + expect(result.allowExecution).toBe(false) + expect(result.askUser).toBeDefined() + }) + + it("should block on the 2nd call for limit 2", () => { + const detector = new ToolRepetitionDetector(2) + + // First call (counter = 1) + const result1 = detector.check(createToolUse("tool", "tool-name")) + expect(result1.allowExecution).toBe(true) + + // Second call (counter = 2) should be blocked + const result2 = detector.check(createToolUse("tool", "tool-name")) + expect(result2.allowExecution).toBe(false) + expect(result2.askUser).toBeDefined() + }) + + it("should block on the 3rd call for limit 3 (default)", () => { + const detector = new ToolRepetitionDetector(3) + + // First call (counter = 1) + const result1 = detector.check(createToolUse("tool", "tool-name")) + expect(result1.allowExecution).toBe(true) + + // Second call (counter = 2) + const result2 = detector.check(createToolUse("tool", "tool-name")) + expect(result2.allowExecution).toBe(true) + + // Third call (counter = 3) should be blocked + const result3 = detector.check(createToolUse("tool", "tool-name")) + expect(result3.allowExecution).toBe(false) + expect(result3.askUser).toBeDefined() + }) + }) +}) diff --git a/src/core/tools/__tests__/executeCommandTool.test.ts b/src/core/tools/__tests__/executeCommandTool.test.ts index ee70ae5c097..615d72042df 100644 --- a/src/core/tools/__tests__/executeCommandTool.test.ts +++ b/src/core/tools/__tests__/executeCommandTool.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, jest, beforeEach } from "@jest/globals" -import { Cline } from "../../Cline" +import { Task } from "../../task/Task" import { formatResponse } from "../../prompts/responses" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../../shared/tools" import { ToolUsage } from "../../../schemas" @@ -13,7 +13,7 @@ jest.mock("execa", () => ({ execa: jest.fn(), })) -jest.mock("../../Cline") +jest.mock("../../task/Task") jest.mock("../../prompts/responses") // Create a mock for the executeCommand function @@ -74,7 +74,7 @@ beforeEach(() => { describe("executeCommandTool", () => { // Setup common test variables - let mockCline: jest.Mocked> & { consecutiveMistakeCount: number; didRejectTool: boolean } + let mockCline: jest.Mocked> & { consecutiveMistakeCount: number; didRejectTool: boolean } let mockAskApproval: jest.Mock let mockHandleError: jest.Mock let mockPushToolResult: jest.Mock @@ -160,7 +160,7 @@ describe("executeCommandTool", () => { // Execute await executeCommandTool( - mockCline as unknown as Cline, + mockCline as unknown as Task, mockToolUse, mockAskApproval as unknown as AskApproval, mockHandleError as unknown as HandleError, @@ -181,7 +181,7 @@ describe("executeCommandTool", () => { // Execute await executeCommandTool( - mockCline as unknown as Cline, + mockCline as unknown as Task, mockToolUse, mockAskApproval as unknown as AskApproval, mockHandleError as unknown as HandleError, @@ -204,7 +204,7 @@ describe("executeCommandTool", () => { // Execute await executeCommandTool( - mockCline as unknown as Cline, + mockCline as unknown as Task, mockToolUse, mockAskApproval as unknown as AskApproval, mockHandleError as unknown as HandleError, @@ -228,7 +228,7 @@ describe("executeCommandTool", () => { // Execute await executeCommandTool( - mockCline as unknown as Cline, + mockCline as unknown as Task, mockToolUse, mockAskApproval as unknown as AskApproval, mockHandleError as unknown as HandleError, @@ -258,7 +258,7 @@ describe("executeCommandTool", () => { // Execute await executeCommandTool( - mockCline as unknown as Cline, + mockCline as unknown as Task, mockToolUse, mockAskApproval as unknown as AskApproval, mockHandleError as unknown as HandleError, diff --git a/src/core/__tests__/read-file-xml.test.ts b/src/core/tools/__tests__/readFileTool.test.ts similarity index 57% rename from src/core/__tests__/read-file-xml.test.ts rename to src/core/tools/__tests__/readFileTool.test.ts index 1228750a7df..f0b3600a261 100644 --- a/src/core/__tests__/read-file-xml.test.ts +++ b/src/core/tools/__tests__/readFileTool.test.ts @@ -1,42 +1,58 @@ -// npx jest src/core/__tests__/read-file-xml.test.ts +// npx jest src/core/tools/__tests__/readFileTool.test.ts import * as path from "path" -import { countFileLines } from "../../integrations/misc/line-counter" -import { readLines } from "../../integrations/misc/read-lines" -import { extractTextFromFile } from "../../integrations/misc/extract-text" -import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter" +import { countFileLines } from "../../../integrations/misc/line-counter" +import { readLines } from "../../../integrations/misc/read-lines" +import { extractTextFromFile } from "../../../integrations/misc/extract-text" +import { parseSourceCodeDefinitionsForFile } from "../../../services/tree-sitter" import { isBinaryFile } from "isbinaryfile" -import { ReadFileToolUse } from "../../shared/tools" - -// Mock dependencies -jest.mock("../../integrations/misc/line-counter") -jest.mock("../../integrations/misc/read-lines") -jest.mock("../../integrations/misc/extract-text", () => { - const actual = jest.requireActual("../../integrations/misc/extract-text") - // Create a spy on the actual addLineNumbers function +import { ReadFileToolUse, ToolParamName, ToolResponse } from "../../../shared/tools" +import { readFileTool } from "../readFileTool" + +jest.mock("path", () => { + const originalPath = jest.requireActual("path") + return { + ...originalPath, + resolve: jest.fn().mockImplementation((...args) => args.join("/")), + } +}) + +jest.mock("fs/promises", () => ({ + mkdir: jest.fn().mockResolvedValue(undefined), + writeFile: jest.fn().mockResolvedValue(undefined), + readFile: jest.fn().mockResolvedValue("{}"), +})) + +jest.mock("isbinaryfile") + +jest.mock("../../../integrations/misc/line-counter") +jest.mock("../../../integrations/misc/read-lines") + +let mockInputContent = "" + +jest.mock("../../../integrations/misc/extract-text", () => { + const actual = jest.requireActual("../../../integrations/misc/extract-text") + // Create a spy on the actual addLineNumbers function. const addLineNumbersSpy = jest.spyOn(actual, "addLineNumbers") return { ...actual, - // Expose the spy so tests can access it + // Expose the spy so tests can access it. __addLineNumbersSpy: addLineNumbersSpy, extractTextFromFile: jest.fn().mockImplementation((_filePath) => { - // Use the actual addLineNumbers function + // Use the actual addLineNumbers function. const content = mockInputContent return Promise.resolve(actual.addLineNumbers(content)) }), } }) -// Get a reference to the spy -const addLineNumbersSpy = jest.requireMock("../../integrations/misc/extract-text").__addLineNumbersSpy +const addLineNumbersSpy = jest.requireMock("../../../integrations/misc/extract-text").__addLineNumbersSpy -// Variable to control what content is used by the mock -let mockInputContent = "" -jest.mock("../../services/tree-sitter") -jest.mock("isbinaryfile") -jest.mock("../ignore/RooIgnoreController", () => ({ +jest.mock("../../../services/tree-sitter") + +jest.mock("../../ignore/RooIgnoreController", () => ({ RooIgnoreController: class { initialize() { return Promise.resolve() @@ -46,22 +62,345 @@ jest.mock("../ignore/RooIgnoreController", () => ({ } }, })) -jest.mock("fs/promises", () => ({ - mkdir: jest.fn().mockResolvedValue(undefined), - writeFile: jest.fn().mockResolvedValue(undefined), - readFile: jest.fn().mockResolvedValue("{}"), -})) -jest.mock("../../utils/fs", () => ({ + +jest.mock("../../../utils/fs", () => ({ fileExistsAtPath: jest.fn().mockReturnValue(true), })) -// Mock path -jest.mock("path", () => { - const originalPath = jest.requireActual("path") - return { - ...originalPath, - resolve: jest.fn().mockImplementation((...args) => args.join("/")), +describe("read_file tool with maxReadFileLine setting", () => { + // Test data + const testFilePath = "test/file.txt" + const absoluteFilePath = "/test/file.txt" + const fileContent = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5" + const numberedFileContent = "1 | Line 1\n2 | Line 2\n3 | Line 3\n4 | Line 4\n5 | Line 5\n" + const sourceCodeDef = "\n\n# file.txt\n1--5 | Content" + const expectedFullFileXml = `${testFilePath}\n\n${numberedFileContent}\n` + + // Mocked functions with correct types + const mockedCountFileLines = countFileLines as jest.MockedFunction + const mockedReadLines = readLines as jest.MockedFunction + const mockedExtractTextFromFile = extractTextFromFile as jest.MockedFunction + const mockedParseSourceCodeDefinitionsForFile = parseSourceCodeDefinitionsForFile as jest.MockedFunction< + typeof parseSourceCodeDefinitionsForFile + > + + const mockedIsBinaryFile = isBinaryFile as jest.MockedFunction + const mockedPathResolve = path.resolve as jest.MockedFunction + + const mockCline: any = {} + let mockProvider: any + let toolResult: ToolResponse | undefined + + beforeEach(() => { + jest.clearAllMocks() + + mockedPathResolve.mockReturnValue(absoluteFilePath) + mockedIsBinaryFile.mockResolvedValue(false) + + mockInputContent = fileContent + + // Setup the extractTextFromFile mock implementation with the current + // mockInputContent. + mockedExtractTextFromFile.mockImplementation((_filePath) => { + const actual = jest.requireActual("../../../integrations/misc/extract-text") + return Promise.resolve(actual.addLineNumbers(mockInputContent)) + }) + + // No need to setup the extractTextFromFile mock implementation here + // as it's already defined at the module level. + + mockProvider = { + getState: jest.fn(), + deref: jest.fn().mockReturnThis(), + } + + mockCline.cwd = "/" + mockCline.task = "Test" + mockCline.providerRef = mockProvider + mockCline.rooIgnoreController = { + validateAccess: jest.fn().mockReturnValue(true), + } + mockCline.say = jest.fn().mockResolvedValue(undefined) + mockCline.ask = jest.fn().mockResolvedValue(true) + mockCline.presentAssistantMessage = jest.fn() + + mockCline.fileContextTracker = { + trackFileContext: jest.fn().mockResolvedValue(undefined), + } + + mockCline.recordToolUsage = jest.fn().mockReturnValue(undefined) + mockCline.recordToolError = jest.fn().mockReturnValue(undefined) + + toolResult = undefined + }) + + /** + * Helper function to execute the read file tool with different maxReadFileLine settings + */ + async function executeReadFileTool( + params: Partial = {}, + options: { + maxReadFileLine?: number + totalLines?: number + skipAddLineNumbersCheck?: boolean // Flag to skip addLineNumbers check + } = {}, + ): Promise { + // Configure mocks based on test scenario + const maxReadFileLine = options.maxReadFileLine ?? 500 + const totalLines = options.totalLines ?? 5 + + mockProvider.getState.mockResolvedValue({ maxReadFileLine }) + mockedCountFileLines.mockResolvedValue(totalLines) + + // Reset the spy before each test + addLineNumbersSpy.mockClear() + + // Create a tool use object + const toolUse: ReadFileToolUse = { + type: "tool_use", + name: "read_file", + params: { path: testFilePath, ...params }, + partial: false, + } + + await readFileTool( + mockCline, + toolUse, + mockCline.ask, + jest.fn(), + (result: ToolResponse) => { + toolResult = result + }, + (_: ToolParamName, content?: string) => content ?? "", + ) + + // Verify addLineNumbers was called appropriately + if (!options.skipAddLineNumbersCheck) { + expect(addLineNumbersSpy).toHaveBeenCalled() + } else { + expect(addLineNumbersSpy).not.toHaveBeenCalled() + } + + return toolResult } + + describe("when maxReadFileLine is negative", () => { + it("should read the entire file using extractTextFromFile", async () => { + // Setup - use default mockInputContent + mockInputContent = fileContent + + // Execute + const result = await executeReadFileTool({}, { maxReadFileLine: -1 }) + + // Verify + expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) + expect(mockedReadLines).not.toHaveBeenCalled() + expect(mockedParseSourceCodeDefinitionsForFile).not.toHaveBeenCalled() + expect(result).toBe(expectedFullFileXml) + }) + + it("should ignore range parameters and read entire file when maxReadFileLine is -1", async () => { + // Setup - use default mockInputContent + mockInputContent = fileContent + + // Execute with range parameters + const result = await executeReadFileTool( + { + start_line: "2", + end_line: "4", + }, + { maxReadFileLine: -1 }, + ) + + // Verify that extractTextFromFile is still used (not readLines) + expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) + expect(mockedReadLines).not.toHaveBeenCalled() + expect(mockedParseSourceCodeDefinitionsForFile).not.toHaveBeenCalled() + expect(result).toBe(expectedFullFileXml) + }) + + it("should not show line snippet in approval message when maxReadFileLine is -1", async () => { + // This test verifies the line snippet behavior for the approval message + // Setup - use default mockInputContent + mockInputContent = fileContent + + // Execute - we'll reuse executeReadFileTool to run the tool + await executeReadFileTool({}, { maxReadFileLine: -1 }) + + // Verify the empty line snippet for full read was passed to the approval message + // Look at the parameters passed to the 'ask' method in the approval message + const askCall = mockCline.ask.mock.calls[0] + const completeMessage = JSON.parse(askCall[1]) + + // Verify the reason (lineSnippet) is empty or undefined for full read + expect(completeMessage.reason).toBeFalsy() + }) + }) + + describe("when maxReadFileLine is 0", () => { + it("should return an empty content with source code definitions", async () => { + // Setup - for maxReadFileLine = 0, the implementation won't call readLines + mockedParseSourceCodeDefinitionsForFile.mockResolvedValue(sourceCodeDef) + + // Execute - skip addLineNumbers check as it's not called for maxReadFileLine=0 + const result = await executeReadFileTool( + {}, + { + maxReadFileLine: 0, + totalLines: 5, + skipAddLineNumbersCheck: true, + }, + ) + + // Verify + expect(mockedExtractTextFromFile).not.toHaveBeenCalled() + expect(mockedReadLines).not.toHaveBeenCalled() // Per implementation line 141 + expect(mockedParseSourceCodeDefinitionsForFile).toHaveBeenCalledWith( + absoluteFilePath, + mockCline.rooIgnoreController, + ) + + // Verify XML structure + expect(result).toContain(`${testFilePath}`) + expect(result).toContain("Showing only 0 of 5 total lines") + expect(result).toContain("") + expect(result).toContain("") + expect(result).toContain(sourceCodeDef.trim()) + expect(result).toContain("") + expect(result).not.toContain(" { + it("should read only maxReadFileLine lines and add source code definitions", async () => { + // Setup + const content = "Line 1\nLine 2\nLine 3" + mockedReadLines.mockResolvedValue(content) + mockedParseSourceCodeDefinitionsForFile.mockResolvedValue(sourceCodeDef) + + // Execute + const result = await executeReadFileTool({}, { maxReadFileLine: 3 }) + + // Verify - check behavior but not specific implementation details + expect(mockedExtractTextFromFile).not.toHaveBeenCalled() + expect(mockedReadLines).toHaveBeenCalled() + expect(mockedParseSourceCodeDefinitionsForFile).toHaveBeenCalledWith( + absoluteFilePath, + mockCline.rooIgnoreController, + ) + + // Verify XML structure + expect(result).toContain(`${testFilePath}`) + expect(result).toContain('') + expect(result).toContain("1 | Line 1") + expect(result).toContain("2 | Line 2") + expect(result).toContain("3 | Line 3") + expect(result).toContain("") + expect(result).toContain("Showing only 3 of 5 total lines") + expect(result).toContain("") + expect(result).toContain("") + expect(result).toContain(sourceCodeDef.trim()) + expect(result).toContain("") + expect(result).toContain("") + expect(result).toContain(sourceCodeDef.trim()) + }) + }) + + describe("when maxReadFileLine equals or exceeds file length", () => { + it("should use extractTextFromFile when maxReadFileLine > totalLines", async () => { + // Setup + mockedCountFileLines.mockResolvedValue(5) // File shorter than maxReadFileLine + mockInputContent = fileContent + + // Execute + const result = await executeReadFileTool({}, { maxReadFileLine: 10, totalLines: 5 }) + + // Verify + expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) + expect(result).toBe(expectedFullFileXml) + }) + + it("should read with extractTextFromFile when file has few lines", async () => { + // Setup + mockedCountFileLines.mockResolvedValue(3) // File shorter than maxReadFileLine + mockInputContent = fileContent + + // Execute + const result = await executeReadFileTool({}, { maxReadFileLine: 5, totalLines: 3 }) + + // Verify + expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) + expect(mockedReadLines).not.toHaveBeenCalled() + // Create a custom expected XML with lines="1-3" since totalLines is 3 + const expectedXml = `${testFilePath}\n\n${numberedFileContent}\n` + expect(result).toBe(expectedXml) + }) + }) + + describe("when file is binary", () => { + it("should always use extractTextFromFile regardless of maxReadFileLine", async () => { + // Setup + mockedIsBinaryFile.mockResolvedValue(true) + // For binary files, we're using a maxReadFileLine of 3 and totalLines is assumed to be 3 + mockedCountFileLines.mockResolvedValue(3) + + // For binary files, we need a special mock implementation that doesn't use addLineNumbers + // Save the original mock implementation + const originalMockImplementation = mockedExtractTextFromFile.getMockImplementation() + // Create a special mock implementation that doesn't call addLineNumbers + mockedExtractTextFromFile.mockImplementation(() => { + return Promise.resolve(numberedFileContent) + }) + + // Reset the spy to clear any previous calls + addLineNumbersSpy.mockClear() + + // Execute - skip addLineNumbers check as we're directly providing the numbered content + const result = await executeReadFileTool( + {}, + { + maxReadFileLine: 3, + totalLines: 3, + skipAddLineNumbersCheck: true, + }, + ) + + // Restore the original mock implementation after the test + mockedExtractTextFromFile.mockImplementation(originalMockImplementation) + + // Verify + expect(mockedExtractTextFromFile).toHaveBeenCalledWith(absoluteFilePath) + expect(mockedReadLines).not.toHaveBeenCalled() + // Create a custom expected XML with lines="1-3" for binary files + const expectedXml = `${testFilePath}\n\n${numberedFileContent}\n` + expect(result).toBe(expectedXml) + }) + }) + + describe("with range parameters", () => { + it("should honor start_line and end_line when provided", async () => { + // Setup + mockedReadLines.mockResolvedValue("Line 2\nLine 3\nLine 4") + + // Execute using executeReadFileTool with range parameters + const rangeResult = await executeReadFileTool({ + start_line: "2", + end_line: "4", + }) + + // Verify + expect(mockedReadLines).toHaveBeenCalledWith(absoluteFilePath, 3, 1) // end_line - 1, start_line - 1 + expect(addLineNumbersSpy).toHaveBeenCalledWith(expect.any(String), 2) // start with proper line numbers + + // Verify XML structure with lines attribute + expect(rangeResult).toContain(`${testFilePath}`) + expect(rangeResult).toContain(``) + expect(rangeResult).toContain("2 | Line 2") + expect(rangeResult).toContain("3 | Line 3") + expect(rangeResult).toContain("4 | Line 4") + expect(rangeResult).toContain("") + }) + }) }) describe("read_file tool XML output structure", () => { @@ -85,27 +424,21 @@ describe("read_file tool XML output structure", () => { // Mock instances const mockCline: any = {} let mockProvider: any - let toolResult: string | undefined + let toolResult: ToolResponse | undefined beforeEach(() => { jest.clearAllMocks() - // Setup path resolution mockedPathResolve.mockReturnValue(absoluteFilePath) - - // Setup mocks for file operations mockedIsBinaryFile.mockResolvedValue(false) - // Set the default content for the mock mockInputContent = fileContent - // Setup mock provider mockProvider = { getState: jest.fn().mockResolvedValue({ maxReadFileLine: 500 }), deref: jest.fn().mockReturnThis(), } - // Setup Cline instance with mock methods mockCline.cwd = "/" mockCline.task = "Test" mockCline.providerRef = mockProvider @@ -116,14 +449,14 @@ describe("read_file tool XML output structure", () => { mockCline.ask = jest.fn().mockResolvedValue(true) mockCline.presentAssistantMessage = jest.fn() mockCline.sayAndCreateMissingParamError = jest.fn().mockResolvedValue("Missing required parameter") - // Add mock for getFileContextTracker method - mockCline.getFileContextTracker = jest.fn().mockReturnValue({ + + mockCline.fileContextTracker = { trackFileContext: jest.fn().mockResolvedValue(undefined), - }) + } + mockCline.recordToolUsage = jest.fn().mockReturnValue(undefined) mockCline.recordToolError = jest.fn().mockReturnValue(undefined) - // Reset tool result toolResult = undefined }) @@ -139,7 +472,7 @@ describe("read_file tool XML output structure", () => { validateAccess?: boolean skipAddLineNumbersCheck?: boolean // Flag to skip addLineNumbers check } = {}, - ): Promise { + ): Promise { // Configure mocks based on test scenario const totalLines = options.totalLines ?? 5 const maxReadFileLine = options.maxReadFileLine ?? 500 @@ -162,9 +495,6 @@ describe("read_file tool XML output structure", () => { partial: false, } - // Import the tool implementation dynamically to avoid hoisting issues - const { readFileTool } = require("../tools/readFileTool") - // Reset the spy's call history before each test addLineNumbersSpy.mockClear() @@ -174,10 +504,10 @@ describe("read_file tool XML output structure", () => { toolUse, mockCline.ask, jest.fn(), - (result: string) => { + (result: ToolResponse) => { toolResult = result }, - (param: string, value: string) => value, + (param: ToolParamName, content?: string) => content ?? "", ) // Verify addLineNumbers was called (unless explicitly skipped) if (!options.skipAddLineNumbersCheck) { @@ -410,7 +740,9 @@ describe("read_file tool XML output structure", () => { // Should contain all the requested lines, not just maxReadFileLine lines expect(result).toBeDefined() - if (result) { + expect(typeof result).toBe("string") + + if (typeof result === "string") { expect(result.split("\n").length).toBeGreaterThan(maxReadFileLine) } }) @@ -507,19 +839,16 @@ describe("read_file tool XML output structure", () => { partial: false, } - // Import the tool implementation dynamically - const { readFileTool } = require("../tools/readFileTool") - // Execute the tool await readFileTool( mockCline, toolUse, mockCline.ask, jest.fn(), - (result: string) => { + (result: ToolResponse) => { toolResult = result }, - (param: string, value: string) => value, + (param: ToolParamName, content?: string) => content ?? "", ) // Verify @@ -565,6 +894,7 @@ describe("read_file tool XML output structure", () => { // Execute const result = await executeReadFileTool({}, { maxReadFileLine, totalLines }) + console.log(result) // Verify // Empty files should include a content tag and notice diff --git a/src/core/__tests__/mode-validator.test.ts b/src/core/tools/__tests__/validateToolUse.test.ts similarity index 95% rename from src/core/__tests__/mode-validator.test.ts rename to src/core/tools/__tests__/validateToolUse.test.ts index 1111f24b9f2..da2550b4d6c 100644 --- a/src/core/__tests__/mode-validator.test.ts +++ b/src/core/tools/__tests__/validateToolUse.test.ts @@ -1,8 +1,8 @@ -// npx jest src/core/__tests__/mode-validator.test.ts +// npx jest src/core/tools/__tests__/validateToolUse.test.ts -import { isToolAllowedForMode, modes, ModeConfig } from "../../shared/modes" -import { TOOL_GROUPS } from "../../shared/tools" -import { validateToolUse } from "../mode-validator" +import { isToolAllowedForMode, modes, ModeConfig } from "../../../shared/modes" +import { TOOL_GROUPS } from "../../../shared/tools" +import { validateToolUse } from "../validateToolUse" const [codeMode, architectMode, askMode] = modes.map((mode) => mode.slug) diff --git a/src/core/tools/accessMcpResourceTool.ts b/src/core/tools/accessMcpResourceTool.ts index 3161a3f8d5c..22b1aba9095 100644 --- a/src/core/tools/accessMcpResourceTool.ts +++ b/src/core/tools/accessMcpResourceTool.ts @@ -1,10 +1,10 @@ import { ClineAskUseMcpServer } from "../../shared/ExtensionMessage" import { ToolUse, RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { formatResponse } from "../prompts/responses" export async function accessMcpResourceTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/tools/applyDiffTool.ts b/src/core/tools/applyDiffTool.ts index 590040f2bab..9fbe1e1de1a 100644 --- a/src/core/tools/applyDiffTool.ts +++ b/src/core/tools/applyDiffTool.ts @@ -3,7 +3,7 @@ import fs from "fs/promises" import { ClineSayTool } from "../../shared/ExtensionMessage" import { getReadablePath } from "../../utils/path" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ToolUse, RemoveClosingTag } from "../../shared/tools" import { formatResponse } from "../prompts/responses" import { AskApproval, HandleError, PushToolResult } from "../../shared/tools" @@ -14,7 +14,7 @@ import { telemetryService } from "../../services/telemetry/TelemetryService" import { unescapeHtmlEntities } from "../../utils/text-normalization" export async function applyDiffTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, @@ -31,6 +31,7 @@ export async function applyDiffTool( const sharedMessageProps: ClineSayTool = { tool: "appliedDiff", path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), + diff: diffContent, } try { @@ -46,8 +47,10 @@ export async function applyDiffTool( return } - const partialMessage = JSON.stringify(sharedMessageProps) - await cline.ask("tool", partialMessage, block.partial, toolProgressStatus).catch(() => {}) + await cline + .ask("tool", JSON.stringify(sharedMessageProps), block.partial, toolProgressStatus) + .catch(() => {}) + return } else { if (!relPath) { @@ -164,7 +167,7 @@ export async function applyDiffTool( // Track file edit operation if (relPath) { - await cline.getFileContextTracker().trackFileContext(relPath, "roo_edited" as RecordSource) + await cline.fileContextTracker.trackFileContext(relPath, "roo_edited" as RecordSource) } // Used to determine if we should wait for busy terminal to update before sending api request diff --git a/src/core/tools/askFollowupQuestionTool.ts b/src/core/tools/askFollowupQuestionTool.ts index 46ce2e4e072..d5adf6d4cd7 100644 --- a/src/core/tools/askFollowupQuestionTool.ts +++ b/src/core/tools/askFollowupQuestionTool.ts @@ -1,10 +1,10 @@ -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" import { formatResponse } from "../prompts/responses" import { parseXml } from "../../utils/xml" export async function askFollowupQuestionTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/tools/attemptCompletionTool.ts b/src/core/tools/attemptCompletionTool.ts index b65ce34b19a..a5e469c77fe 100644 --- a/src/core/tools/attemptCompletionTool.ts +++ b/src/core/tools/attemptCompletionTool.ts @@ -1,6 +1,6 @@ import Anthropic from "@anthropic-ai/sdk" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ToolResponse, ToolUse, @@ -16,7 +16,7 @@ import { telemetryService } from "../../services/telemetry/TelemetryService" import { type ExecuteCommandOptions, executeCommand } from "./executeCommandTool" export async function attemptCompletionTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, @@ -46,7 +46,7 @@ export async function attemptCompletionTool( await cline.say("completion_result", removeClosingTag("result", result), undefined, false) telemetryService.captureTaskCompleted(cline.taskId) - cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.getToolUsage()) + cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.toolUsage) await cline.ask("command", removeClosingTag("command", command), block.partial).catch(() => {}) } @@ -72,17 +72,17 @@ export async function attemptCompletionTool( // Haven't sent a command message yet so first send completion_result then command. await cline.say("completion_result", result, undefined, false) telemetryService.captureTaskCompleted(cline.taskId) - cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.getToolUsage()) + cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.toolUsage) } // Complete command message. - const executionId = Date.now().toString() - const didApprove = await askApproval("command", command, { id: executionId }) + const didApprove = await askApproval("command", command) if (!didApprove) { return } + const executionId = cline.lastMessageTs?.toString() ?? Date.now().toString() const options: ExecuteCommandOptions = { executionId, command } const [userRejected, execCommandResult] = await executeCommand(cline, options) @@ -97,7 +97,7 @@ export async function attemptCompletionTool( } else { await cline.say("completion_result", result, undefined, false) telemetryService.captureTaskCompleted(cline.taskId) - cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.getToolUsage()) + cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.toolUsage) } if (cline.parentTask) { @@ -108,7 +108,7 @@ export async function attemptCompletionTool( } // tell the provider to remove the current subtask and resume the previous task in the stack - await cline.providerRef.deref()?.finishSubTask(lastMessage?.text ?? "") + await cline.providerRef.deref()?.finishSubTask(result) return } diff --git a/src/core/tools/browserActionTool.ts b/src/core/tools/browserActionTool.ts index 093a89a7d5d..13cb9b0ec26 100644 --- a/src/core/tools/browserActionTool.ts +++ b/src/core/tools/browserActionTool.ts @@ -1,4 +1,4 @@ -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" import { BrowserAction, @@ -9,7 +9,7 @@ import { import { formatResponse } from "../prompts/responses" export async function browserActionTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/tools/executeCommandTool.ts b/src/core/tools/executeCommandTool.ts index 4bd303904f7..6f5fc714a88 100644 --- a/src/core/tools/executeCommandTool.ts +++ b/src/core/tools/executeCommandTool.ts @@ -3,7 +3,7 @@ import * as path from "path" import delay from "delay" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { CommandExecutionStatus } from "../../schemas" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag, ToolResponse } from "../../shared/tools" import { formatResponse } from "../prompts/responses" @@ -16,7 +16,7 @@ import { Terminal } from "../../integrations/terminal/Terminal" class ShellIntegrationError extends Error {} export async function executeCommandTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, @@ -48,14 +48,14 @@ export async function executeCommandTool( cline.consecutiveMistakeCount = 0 - const executionId = Date.now().toString() command = unescapeHtmlEntities(command) // Unescape HTML entities. - const didApprove = await askApproval("command", command, { id: executionId }) + const didApprove = await askApproval("command", command) if (!didApprove) { return } + const executionId = cline.lastMessageTs?.toString() ?? Date.now().toString() const clineProvider = await cline.providerRef.deref() const clineProviderState = await clineProvider?.getState() const { terminalOutputLineLimit = 500, terminalShellIntegrationDisabled = false } = clineProviderState ?? {} @@ -114,7 +114,7 @@ export type ExecuteCommandOptions = { } export async function executeCommand( - cline: Cline, + cline: Task, { executionId, command, @@ -149,9 +149,12 @@ export async function executeCommand( const terminalProvider = terminalShellIntegrationDisabled ? "execa" : "vscode" const clineProvider = await cline.providerRef.deref() + let accumulatedOutput = "" const callbacks: RooTerminalCallbacks = { - onLine: async (output: string, process: RooTerminalProcess) => { - const status: CommandExecutionStatus = { executionId, status: "output", output } + onLine: async (lines: string, process: RooTerminalProcess) => { + accumulatedOutput += lines + const compressedOutput = Terminal.compressTerminalOutput(accumulatedOutput, terminalOutputLineLimit) + const status: CommandExecutionStatus = { executionId, status: "output", output: compressedOutput } clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) }) if (runInBackground) { @@ -195,7 +198,7 @@ export async function executeCommand( const terminal = await TerminalRegistry.getOrCreateTerminal(workingDir, !!customCwd, cline.taskId, terminalProvider) if (terminal instanceof Terminal) { - terminal.terminal.show() + terminal.terminal.show(true) // Update the working directory in case the terminal we asked for has // a different working directory so that the model will know where the diff --git a/src/core/tools/fetchInstructionsTool.ts b/src/core/tools/fetchInstructionsTool.ts index d72c19ce907..5325f98fbf4 100644 --- a/src/core/tools/fetchInstructionsTool.ts +++ b/src/core/tools/fetchInstructionsTool.ts @@ -1,11 +1,11 @@ -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { fetchInstructions } from "../prompts/instructions/instructions" import { ClineSayTool } from "../../shared/ExtensionMessage" import { formatResponse } from "../prompts/responses" import { ToolUse, AskApproval, HandleError, PushToolResult } from "../../shared/tools" export async function fetchInstructionsTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/tools/insertContentTool.ts b/src/core/tools/insertContentTool.ts index 8e6c5fc89ea..bcb217326a2 100644 --- a/src/core/tools/insertContentTool.ts +++ b/src/core/tools/insertContentTool.ts @@ -3,7 +3,7 @@ import fs from "fs/promises" import path from "path" import { getReadablePath } from "../../utils/path" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" import { formatResponse } from "../prompts/responses" import { ClineSayTool } from "../../shared/ExtensionMessage" @@ -12,7 +12,7 @@ import { fileExistsAtPath } from "../../utils/fs" import { insertGroups } from "../diff/insert-groups" export async function insertContentTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, @@ -26,13 +26,13 @@ export async function insertContentTool( const sharedMessageProps: ClineSayTool = { tool: "insertContent", path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), + diff: content, lineNumber: line ? parseInt(line, 10) : undefined, } try { if (block.partial) { - const partialMessage = JSON.stringify(sharedMessageProps) - await cline.ask("tool", partialMessage, block.partial).catch(() => {}) + await cline.ask("tool", JSON.stringify(sharedMessageProps), block.partial).catch(() => {}) return } @@ -132,7 +132,7 @@ export async function insertContentTool( // Track file edit operation if (relPath) { - await cline.getFileContextTracker().trackFileContext(relPath, "roo_edited" as RecordSource) + await cline.fileContextTracker.trackFileContext(relPath, "roo_edited" as RecordSource) } cline.didEditFile = true @@ -145,14 +145,15 @@ export async function insertContentTool( return } - const userFeedbackDiff = JSON.stringify({ - tool: "insertContent", - path: getReadablePath(cline.cwd, relPath), - lineNumber: lineNumber, - diff: userEdits, - } satisfies ClineSayTool) - - await cline.say("user_feedback_diff", userFeedbackDiff) + await cline.say( + "user_feedback_diff", + JSON.stringify({ + tool: "insertContent", + path: getReadablePath(cline.cwd, relPath), + diff: userEdits, + lineNumber: lineNumber, + } satisfies ClineSayTool), + ) pushToolResult( `The user made the following updates to your content:\n\n${userEdits}\n\n` + diff --git a/src/core/tools/listCodeDefinitionNamesTool.ts b/src/core/tools/listCodeDefinitionNamesTool.ts index 5f1e5ad8837..f0cdde01ebc 100644 --- a/src/core/tools/listCodeDefinitionNamesTool.ts +++ b/src/core/tools/listCodeDefinitionNamesTool.ts @@ -2,14 +2,14 @@ import path from "path" import fs from "fs/promises" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ClineSayTool } from "../../shared/ExtensionMessage" import { getReadablePath } from "../../utils/path" import { parseSourceCodeForDefinitionsTopLevel, parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter" import { RecordSource } from "../context-tracking/FileContextTrackerTypes" export async function listCodeDefinitionNamesTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, @@ -64,7 +64,7 @@ export async function listCodeDefinitionNamesTool( } if (relPath) { - await cline.getFileContextTracker().trackFileContext(relPath, "read_tool" as RecordSource) + await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource) } pushToolResult(result) diff --git a/src/core/tools/listFilesTool.ts b/src/core/tools/listFilesTool.ts index 7c785526e8b..6d40de711c4 100644 --- a/src/core/tools/listFilesTool.ts +++ b/src/core/tools/listFilesTool.ts @@ -1,6 +1,6 @@ import * as path from "path" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ClineSayTool } from "../../shared/ExtensionMessage" import { formatResponse } from "../prompts/responses" import { listFiles } from "../../services/glob/list-files" @@ -23,7 +23,7 @@ import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } f */ export async function listFilesTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/tools/newTaskTool.ts b/src/core/tools/newTaskTool.ts index 7b6030eee35..e589e01e564 100644 --- a/src/core/tools/newTaskTool.ts +++ b/src/core/tools/newTaskTool.ts @@ -1,12 +1,12 @@ import delay from "delay" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { defaultModeSlug, getModeBySlug } from "../../shared/modes" import { formatResponse } from "../prompts/responses" export async function newTaskTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/tools/readFileTool.ts b/src/core/tools/readFileTool.ts index e982420bf15..fade9b2d11c 100644 --- a/src/core/tools/readFileTool.ts +++ b/src/core/tools/readFileTool.ts @@ -1,7 +1,7 @@ import path from "path" import { isBinaryFile } from "isbinaryfile" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ClineSayTool } from "../../shared/ExtensionMessage" import { formatResponse } from "../prompts/responses" import { t } from "../../i18n" @@ -15,7 +15,7 @@ import { extractTextFromFile, addLineNumbers } from "../../integrations/misc/ext import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter" export async function readFileTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, @@ -164,7 +164,21 @@ export async function readFileTool( const res = await Promise.all([ maxReadFileLine > 0 ? readLines(absolutePath, maxReadFileLine - 1, 0) : "", - parseSourceCodeDefinitionsForFile(absolutePath, cline.rooIgnoreController), + (async () => { + try { + return await parseSourceCodeDefinitionsForFile(absolutePath, cline.rooIgnoreController) + } catch (error) { + if (error instanceof Error && error.message.startsWith("Unsupported language:")) { + console.warn(`[read_file] Warning: ${error.message}`) + return undefined + } else { + console.error( + `[read_file] Unhandled error: ${error instanceof Error ? error.message : String(error)}`, + ) + return undefined + } + } + })(), ]) content = res[0].length > 0 ? addLineNumbers(res[0]) : "" @@ -231,7 +245,7 @@ export async function readFileTool( // Track file read operation if (relPath) { - await cline.getFileContextTracker().trackFileContext(relPath, "read_tool" as RecordSource) + await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource) } // Format the result into the required XML structure diff --git a/src/core/tools/searchAndReplaceTool.ts b/src/core/tools/searchAndReplaceTool.ts index 7a503b5f1ee..417e2046df8 100644 --- a/src/core/tools/searchAndReplaceTool.ts +++ b/src/core/tools/searchAndReplaceTool.ts @@ -4,7 +4,7 @@ import fs from "fs/promises" import delay from "delay" // Internal imports -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { AskApproval, HandleError, PushToolResult, RemoveClosingTag, ToolUse } from "../../shared/tools" import { formatResponse } from "../prompts/responses" import { ClineSayTool } from "../../shared/ExtensionMessage" @@ -21,7 +21,7 @@ import { RecordSource } from "../context-tracking/FileContextTrackerTypes" * Validates required parameters for search and replace operation */ async function validateParams( - cline: Cline, + cline: Task, relPath: string | undefined, search: string | undefined, replace: string | undefined, @@ -61,7 +61,7 @@ async function validateParams( * @param removeClosingTag - Function to remove closing tags */ export async function searchAndReplaceTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, @@ -215,7 +215,7 @@ export async function searchAndReplaceTool( // Track file edit operation if (relPath) { - await cline.getFileContextTracker().trackFileContext(relPath, "roo_edited" as RecordSource) + await cline.fileContextTracker.trackFileContext(relPath, "roo_edited" as RecordSource) } cline.didEditFile = true @@ -226,13 +226,14 @@ export async function searchAndReplaceTool( return } - const userFeedbackDiff = JSON.stringify({ - tool: "appliedDiff", - path: getReadablePath(cline.cwd, relPath), - diff: userEdits, - } satisfies ClineSayTool) - - await cline.say("user_feedback_diff", userFeedbackDiff) + await cline.say( + "user_feedback_diff", + JSON.stringify({ + tool: "appliedDiff", + path: getReadablePath(cline.cwd, relPath), + diff: userEdits, + } satisfies ClineSayTool), + ) // Format and send response with user's updates const resultMessage = [ diff --git a/src/core/tools/searchFilesTool.ts b/src/core/tools/searchFilesTool.ts index 33a8b8b3cc3..6528f20d541 100644 --- a/src/core/tools/searchFilesTool.ts +++ b/src/core/tools/searchFilesTool.ts @@ -1,13 +1,13 @@ import path from "path" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" import { ClineSayTool } from "../../shared/ExtensionMessage" import { getReadablePath } from "../../utils/path" import { regexSearchFiles } from "../../services/ripgrep" export async function searchFilesTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/tools/switchModeTool.ts b/src/core/tools/switchModeTool.ts index 28f719ff2d6..8ce906b41fc 100644 --- a/src/core/tools/switchModeTool.ts +++ b/src/core/tools/switchModeTool.ts @@ -1,12 +1,12 @@ import delay from "delay" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" import { formatResponse } from "../prompts/responses" import { defaultModeSlug, getModeBySlug } from "../../shared/modes" export async function switchModeTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/tools/useMcpToolTool.ts b/src/core/tools/useMcpToolTool.ts index 882f214a6f9..b8339bc8d0e 100644 --- a/src/core/tools/useMcpToolTool.ts +++ b/src/core/tools/useMcpToolTool.ts @@ -1,10 +1,10 @@ -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" import { formatResponse } from "../prompts/responses" import { ClineAskUseMcpServer } from "../../shared/ExtensionMessage" export async function useMcpToolTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, diff --git a/src/core/mode-validator.ts b/src/core/tools/validateToolUse.ts similarity index 75% rename from src/core/mode-validator.ts rename to src/core/tools/validateToolUse.ts index 4c5e8fbf7fb..0b1623f0575 100644 --- a/src/core/mode-validator.ts +++ b/src/core/tools/validateToolUse.ts @@ -1,5 +1,5 @@ -import { ToolName } from "../schemas" -import { Mode, isToolAllowedForMode, ModeConfig } from "../shared/modes" +import { ToolName } from "../../schemas" +import { Mode, isToolAllowedForMode, ModeConfig } from "../../shared/modes" export function validateToolUse( toolName: ToolName, diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index a23aea97145..2c37f95b74e 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -2,7 +2,7 @@ import path from "path" import delay from "delay" import * as vscode from "vscode" -import { Cline } from "../Cline" +import { Task } from "../task/Task" import { ClineSayTool } from "../../shared/ExtensionMessage" import { formatResponse } from "../prompts/responses" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" @@ -15,7 +15,7 @@ import { detectCodeOmission } from "../../integrations/editor/detect-omission" import { unescapeHtmlEntities } from "../../utils/text-normalization" export async function writeToFileTool( - cline: Cline, + cline: Task, block: ToolUse, askApproval: AskApproval, handleError: HandleError, @@ -72,6 +72,7 @@ export async function writeToFileTool( const sharedMessageProps: ClineSayTool = { tool: fileExists ? "editedExistingFile" : "newFileCreated", path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), + content: newContent, isOutsideWorkspace, } @@ -211,7 +212,7 @@ export async function writeToFileTool( // Track file edit operation if (relPath) { - await cline.getFileContextTracker().trackFileContext(relPath, "roo_edited" as RecordSource) + await cline.fileContextTracker.trackFileContext(relPath, "roo_edited" as RecordSource) } cline.didEditFile = true // used to determine if we should wait for busy terminal to update before sending api request diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 4bd50398fdc..557c1cab814 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -9,16 +9,10 @@ import axios from "axios" import pWaitFor from "p-wait-for" import * as vscode from "vscode" -import { CreatorModeConfig, GlobalState, ProviderSettings, RooCodeSettings } from "../../schemas" +import type { GlobalState, ProviderName, ProviderSettings, RooCodeSettings, ProviderSettingsEntry, CreatorModeConfig } from "../../schemas" import { t } from "../../i18n" import { setPanel } from "../../activate/registerCommands" -import { - ApiConfiguration, - ApiProvider, - requestyDefaultModelId, - openRouterDefaultModelId, - glamaDefaultModelId, -} from "../../shared/api" +import { requestyDefaultModelId, openRouterDefaultModelId, glamaDefaultModelId } from "../../shared/api" import { findLast } from "../../shared/array" import { supportPrompt } from "../../shared/support-prompt" import { GlobalFileNames } from "../../shared/globalFileNames" @@ -41,8 +35,8 @@ import { ContextProxy } from "../config/ContextProxy" import { ProviderSettingsManager } from "../config/ProviderSettingsManager" import { CustomModesManager } from "../config/CustomModesManager" import { buildApiHandler } from "../../api" -import { CodeActionName } from "../CodeActionProvider" -import { Cline, ClineOptions } from "../Cline" +import { CodeActionName } from "../../activate/CodeActionProvider" +import { Task, TaskOptions } from "../task/Task" import { getNonce } from "./getNonce" import { getUri } from "./getUri" import { getSystemPromptFilePath } from "../prompts/sections/custom-system-prompt" @@ -59,7 +53,7 @@ import { PearAIAgentModelsConfig } from "../../api/providers/pearai/pearai" */ export type ClineProviderEvents = { - clineCreated: [cline: Cline] + clineCreated: [cline: Task] } export class ClineProvider extends EventEmitter implements vscode.WebviewViewProvider { @@ -68,7 +62,7 @@ export class ClineProvider extends EventEmitter implements private static activeInstances: Set = new Set() private disposables: vscode.Disposable[] = [] private view?: vscode.WebviewView | vscode.WebviewPanel - private clineStack: Cline[] = [] + private clineStack: Task[] = [] private _workspaceTracker?: WorkspaceTracker // workSpaceTracker read-only for access outside this class public get workspaceTracker(): WorkspaceTracker | undefined { return this._workspaceTracker @@ -77,7 +71,7 @@ export class ClineProvider extends EventEmitter implements public isViewLaunched = false public settingsImportedAt?: number - public readonly latestAnnouncementId = "apr-30-2025-3-15" // Update for v3.15.0 announcement + public readonly latestAnnouncementId = "may-14-2025-3-17" // Update for v3.17.0 announcement public readonly providerSettingsManager: ProviderSettingsManager public readonly customModesManager: CustomModesManager @@ -129,7 +123,7 @@ export class ClineProvider extends EventEmitter implements // Adds a new Cline instance to clineStack, marking the start of a new task. // The instance is pushed to the top of the stack (LIFO order). // When the task is completed, the top instance is removed, reactivating the previous task. - async addClineToStack(cline: Cline) { + async addClineToStack(cline: Task) { console.log(`[subtasks] adding task ${cline.taskId}.${cline.instanceId} to stack`) // Add this cline instance into the stack that represents the order of all the called tasks. @@ -174,7 +168,7 @@ export class ClineProvider extends EventEmitter implements // returns the current cline object in the stack (the top one) // if the stack is empty, returns undefined - getCurrentCline(): Cline | undefined { + getCurrentCline(): Task | undefined { if (this.clineStack.length === 0) { return undefined } @@ -190,15 +184,15 @@ export class ClineProvider extends EventEmitter implements return this.clineStack.map((cline) => cline.taskId) } - // remove the current task/cline instance (at the top of the stack), ao this task is finished + // remove the current task/cline instance (at the top of the stack), so this task is finished // and resume the previous task/cline instance (if it exists) // this is used when a sub task is finished and the parent task needs to be resumed async finishSubTask(lastMessage: string) { console.log(`[subtasks] finishing subtask ${lastMessage}`) // remove the last cline instance from the stack (this is the finished sub task) await this.removeClineFromStack() - // resume the last cline instance in the stack (if it exists - this is the 'parnt' calling task) - this.getCurrentCline()?.resumePausedTask(lastMessage) + // resume the last cline instance in the stack (if it exists - this is the 'parent' calling task) + await this.getCurrentCline()?.resumePausedTask(lastMessage) } /* @@ -265,7 +259,7 @@ export class ClineProvider extends EventEmitter implements return false } - // check if there is a cline instance in the stack (if this provider has an active task) + // Check if there is a cline instance in the stack (if this provider has an active task) if (visibleProvider.getCurrentCline()) { return true } @@ -460,7 +454,7 @@ export class ClineProvider extends EventEmitter implements return data } - public async initClineWithSubTask(parent: Cline, task?: string, images?: string[]) { + public async initClineWithSubTask(parent: Task, task?: string, images?: string[]) { return this.initClineWithTask(task, images, parent) } @@ -470,28 +464,20 @@ export class ClineProvider extends EventEmitter implements public async initClineWithTask( task?: string, images?: string[], - parentTask?: Cline, + parentTask?: Task, options: Partial< Pick< - ClineOptions, - | "customInstructions" - | "enableDiff" - | "enableCheckpoints" - | "fuzzyMatchThreshold" - | "consecutiveMistakeLimit" - | "experiments" + TaskOptions, + "enableDiff" | "enableCheckpoints" | "fuzzyMatchThreshold" | "consecutiveMistakeLimit" | "experiments" > > = {}, creatorModeConfig?: CreatorModeConfig, ) { const { apiConfiguration, - customModePrompts, diffEnabled: enableDiff, enableCheckpoints, fuzzyMatchThreshold, - mode, - customInstructions: globalInstructions, experiments, } = await this.getState() @@ -509,7 +495,7 @@ export class ClineProvider extends EventEmitter implements const pearaiAgentModels = await this.getPearAIAgentModels() - const cline = new Cline({ + const cline = new Task({ provider: this, apiConfiguration: { ...apiConfiguration, @@ -518,6 +504,8 @@ export class ClineProvider extends EventEmitter implements }, creatorModeConfig, customInstructions: effectiveInstructions, + provider: this, + apiConfiguration, enableDiff, enableCheckpoints, fuzzyMatchThreshold, @@ -541,19 +529,16 @@ export class ClineProvider extends EventEmitter implements } public async initClineWithHistoryItem( - historyItem: HistoryItem & { rootTask?: Cline; parentTask?: Cline }, + historyItem: HistoryItem & { rootTask?: Task; parentTask?: Task }, options?: { creatorModeConfig?: CreatorModeConfig } ) { await this.removeClineFromStack() const { apiConfiguration, - customModePrompts, diffEnabled: enableDiff, enableCheckpoints, fuzzyMatchThreshold, - mode, - customInstructions: globalInstructions, experiments, } = await this.getState() @@ -561,7 +546,7 @@ export class ClineProvider extends EventEmitter implements const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n") const pearaiAgentModels = await this.getPearAIAgentModels() - const cline = new Cline({ + const cline = new Task({ provider: this, apiConfiguration: { ...apiConfiguration, pearaiAgentModels: pearaiAgentModels }, customInstructions: effectiveInstructions, @@ -803,7 +788,6 @@ export class ClineProvider extends EventEmitter implements * @param newMode The mode to switch to */ public async handleModeSwitch(newMode: Mode) { - // Capture mode switch telemetry event const cline = this.getCurrentCline() if (cline) { @@ -820,33 +804,37 @@ export class ClineProvider extends EventEmitter implements // Update listApiConfigMeta first to ensure UI has latest data await this.updateGlobalState("listApiConfigMeta", listApiConfig) - // If this mode has a saved config, use it + // If this mode has a saved config, use it. if (savedConfigId) { - const config = listApiConfig?.find((c) => c.id === savedConfigId) - - if (config?.name) { - let apiConfig = await this.providerSettingsManager.loadConfig(config.name) - - // Switch to pearai-model-creator model if we are in Creator Mode - if (newMode == PEARAI_CREATOR_MODE_WEBAPP_MANAGER_SLUG || newMode.includes('creator')) { - apiConfig = { - ...apiConfig, - apiProvider: "pearai", - apiModelId: "pearai-model-creator", - } - } - - await Promise.all([ - this.updateGlobalState("currentApiConfigName", config.name), - this.updateApiConfiguration(apiConfig), - ]) + const profile = listApiConfig.find(({ id }) => id === savedConfigId) + + // TODO: Nang to look at + // if (config?.name) { + // let apiConfig = await this.providerSettingsManager.loadConfig(config.name) + + // // Switch to pearai-model-creator model if we are in Creator Mode + // if (newMode == PEARAI_CREATOR_MODE_WEBAPP_MANAGER_SLUG || newMode.includes('creator')) { + // apiConfig = { + // ...apiConfig, + // apiProvider: "pearai", + // apiModelId: "pearai-model-creator", + // } + // } + + // await Promise.all([ + // this.updateGlobalState("currentApiConfigName", config.name), + // this.updateApiConfiguration(apiConfig), + // ]) + // } + if (profile?.name) { + await this.activateProviderProfile({ name: profile.name }) } } else { - // If no saved config for this mode, save current config as default + // If no saved config for this mode, save current config as default. const currentApiConfigName = this.getGlobalState("currentApiConfigName") if (currentApiConfigName) { - const config = listApiConfig?.find((c) => c.name === currentApiConfigName) + const config = listApiConfig.find((c) => c.name === currentApiConfigName) if (config?.id) { await this.providerSettingsManager.setModeConfig(newMode, config.id) @@ -867,25 +855,133 @@ export class ClineProvider extends EventEmitter implements ...providerSettings, creatorModeConfig: currentCline?.creatorModeConfig, } + } + // Provider Profile Management - if (mode) { - const currentApiConfigName = this.getGlobalState("currentApiConfigName") - const listApiConfig = await this.providerSettingsManager.listConfig() - const config = listApiConfig?.find((c) => c.name === currentApiConfigName) + getProviderProfileEntries(): ProviderSettingsEntry[] { + return this.contextProxy.getValues().listApiConfigMeta || [] + } + + getProviderProfileEntry(name: string): ProviderSettingsEntry | undefined { + return this.getProviderProfileEntries().find((profile) => profile.name === name) + } + + public hasProviderProfileEntry(name: string): boolean { + return !!this.getProviderProfileEntry(name) + } + + async upsertProviderProfile( + name: string, + providerSettings: ProviderSettings, + activate: boolean = true, + ): Promise { + try { + // TODO: Do we need to be calling `activateProfile`? It's not + // clear to me what the source of truth should be; in some cases + // we rely on the `ContextProxy`'s data store and in other cases + // we rely on the `ProviderSettingsManager`'s data store. It might + // be simpler to unify these two. + const id = await this.providerSettingsManager.saveConfig(name, providerSettings) + + if (activate) { + const { mode } = await this.getState() + + // These promises do the following: + // 1. Adds or updates the list of provider profiles. + // 2. Sets the current provider profile. + // 3. Sets the current mode's provider profile. + // 4. Copies the provider settings to the context. + // + // Note: 1, 2, and 4 can be done in one `ContextProxy` call: + // this.contextProxy.setValues({ ...providerSettings, listApiConfigMeta: ..., currentApiConfigName: ... }) + // We should probably switch to that and verify that it works. + // I left the original implementation in just to be safe. + await Promise.all([ + this.updateGlobalState("listApiConfigMeta", await this.providerSettingsManager.listConfig()), + this.updateGlobalState("currentApiConfigName", name), + this.providerSettingsManager.setModeConfig(mode, id), + this.contextProxy.setProviderSettings(providerSettings), + ]) - if (config?.id) { - await this.providerSettingsManager.setModeConfig(mode, config.id) + // Change the provider for the current task. + // TODO: We should rename `buildApiHandler` for clarity (e.g. `getProviderClient`). + const task = this.getCurrentCline() + + if (task) { + task.api = buildApiHandler(providerSettings) + } + } else { + await this.updateGlobalState("listApiConfigMeta", await this.providerSettingsManager.listConfig()) } + + // await this.contextProxy.setProviderSettings(updatedConfig) + + // if (this.getCurrentCline()) { + // this.getCurrentCline()!.api = buildApiHandler(updatedConfig) + await this.postStateToWebview() + return id + } catch (error) { + this.log( + `Error create new api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, + ) + + vscode.window.showErrorMessage(t("common:errors.create_api_config")) + return undefined } + } - await this.contextProxy.setProviderSettings(updatedConfig) + async deleteProviderProfile(profileToDelete: ProviderSettingsEntry) { + const globalSettings = this.contextProxy.getValues() + let profileToActivate: string | undefined = globalSettings.currentApiConfigName - if (this.getCurrentCline()) { - this.getCurrentCline()!.api = buildApiHandler(updatedConfig) + if (profileToDelete.name === profileToActivate) { + profileToActivate = this.getProviderProfileEntries().find(({ name }) => name !== profileToDelete.name)?.name } + + if (!profileToActivate) { + throw new Error("You cannot delete the last profile") + } + + const entries = this.getProviderProfileEntries().filter(({ name }) => name !== profileToDelete.name) + + await this.contextProxy.setValues({ + ...globalSettings, + currentApiConfigName: profileToActivate, + listApiConfigMeta: entries, + }) + + await this.postStateToWebview() + } + + async activateProviderProfile(args: { name: string } | { id: string }) { + const { name, id, ...providerSettings } = await this.providerSettingsManager.activateProfile(args) + + // See `upsertProviderProfile` for a description of what this is doing. + await Promise.all([ + this.contextProxy.setValue("listApiConfigMeta", await this.providerSettingsManager.listConfig()), + this.contextProxy.setValue("currentApiConfigName", name), + this.contextProxy.setProviderSettings(providerSettings), + ]) + + const { mode } = await this.getState() + + if (id) { + await this.providerSettingsManager.setModeConfig(mode, id) + } + + // Change the provider for the current task. + const task = this.getCurrentCline() + + if (task) { + task.api = buildApiHandler(providerSettings) + } + + await this.postStateToWebview() } + // Task Management + async cancelTask() { const cline = this.getCurrentCline() @@ -933,11 +1029,6 @@ export class ClineProvider extends EventEmitter implements async updateCustomInstructions(instructions?: string) { // User may be clearing the field. await this.updateGlobalState("customInstructions", instructions || undefined) - - if (this.getCurrentCline()) { - this.getCurrentCline()!.customInstructions = instructions || undefined - } - await this.postStateToWebview() } @@ -995,14 +1086,14 @@ export class ClineProvider extends EventEmitter implements throw error } - const newConfiguration: ApiConfiguration = { + const newConfiguration: ProviderSettings = { ...apiConfiguration, apiProvider: "openrouter", openRouterApiKey: apiKey, openRouterModelId: apiConfiguration?.openRouterModelId || openRouterDefaultModelId, } - await this.upsertApiConfiguration(currentApiConfigName, newConfiguration) + await this.upsertProviderProfile(currentApiConfigName, newConfiguration) } // Glama @@ -1025,14 +1116,14 @@ export class ClineProvider extends EventEmitter implements const { apiConfiguration, currentApiConfigName } = await this.getState() - const newConfiguration: ApiConfiguration = { + const newConfiguration: ProviderSettings = { ...apiConfiguration, apiProvider: "glama", glamaApiKey: apiKey, glamaModelId: apiConfiguration?.glamaModelId || glamaDefaultModelId, } - await this.upsertApiConfiguration(currentApiConfigName, newConfiguration) + await this.upsertProviderProfile(currentApiConfigName, newConfiguration) } // Requesty @@ -1040,36 +1131,14 @@ export class ClineProvider extends EventEmitter implements async handleRequestyCallback(code: string) { let { apiConfiguration, currentApiConfigName } = await this.getState() - const newConfiguration: ApiConfiguration = { + const newConfiguration: ProviderSettings = { ...apiConfiguration, apiProvider: "requesty", requestyApiKey: code, requestyModelId: apiConfiguration?.requestyModelId || requestyDefaultModelId, } - await this.upsertApiConfiguration(currentApiConfigName, newConfiguration) - } - - // Save configuration - - async upsertApiConfiguration(configName: string, apiConfiguration: ApiConfiguration) { - try { - await this.providerSettingsManager.saveConfig(configName, apiConfiguration) - const listApiConfig = await this.providerSettingsManager.listConfig() - - await Promise.all([ - this.updateGlobalState("listApiConfigMeta", listApiConfig), - this.updateApiConfiguration(apiConfiguration), - this.updateGlobalState("currentApiConfigName", configName), - ]) - - await this.postStateToWebview() - } catch (error) { - this.log( - `Error create new api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, - ) - vscode.window.showErrorMessage(t("common:errors.create_api_config")) - } + await this.upsertProviderProfile(currentApiConfigName, newConfiguration) } // Task history @@ -1365,7 +1434,7 @@ export class ClineProvider extends EventEmitter implements const customModes = await this.customModesManager.getCustomModes() // Determine apiProvider with the same logic as before. - const apiProvider: ApiProvider = stateValues.apiProvider ? stateValues.apiProvider : "anthropic" + const apiProvider: ProviderName = stateValues.apiProvider ? stateValues.apiProvider : "anthropic" // Build the apiConfiguration object combining state values and secrets. const providerSettings = this.contextProxy.getProviderSettings() @@ -1544,10 +1613,12 @@ export class ClineProvider extends EventEmitter implements const appVersion = this.context.extension?.packageJSON?.version const vscodeVersion = vscode.version const platform = process.platform + const editorName = vscode.env.appName // Get the editor name (VS Code, Cursor, etc.) const properties: Record = { vscodeVersion, platform, + editorName, } // Add extension version @@ -1589,6 +1660,10 @@ export class ClineProvider extends EventEmitter implements if (currentCline?.creatorModeConfig?.creatorMode) { properties.isCreatorMode = true } + // Add isSubtask property that indicates whether this task is a subtask + if (currentCline) { + properties.isSubtask = !!currentCline.parentTask + } return properties } diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 344c9672186..d3b7f80e166 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -1,53 +1,42 @@ // npx jest src/core/webview/__tests__/ClineProvider.test.ts +import Anthropic from "@anthropic-ai/sdk" import * as vscode from "vscode" import axios from "axios" import { ClineProvider } from "../ClineProvider" -import { ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage" +import { ProviderSettingsEntry, ClineMessage, ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage" import { setSoundEnabled } from "../../../utils/sound" import { setTtsEnabled } from "../../../utils/tts" import { defaultModeSlug } from "../../../shared/modes" import { experimentDefault } from "../../../shared/experiments" import { ContextProxy } from "../../config/ContextProxy" import { AGENT_RULES_DIR } from "../../../shared/constants" +import { Task, TaskOptions } from "../../task/Task" // Mock setup must come before imports jest.mock("../../prompts/sections/custom-instructions") -// Mock dependencies jest.mock("vscode") + jest.mock("delay") -// Mock BrowserSession -jest.mock("../../../services/browser/BrowserSession", () => ({ - BrowserSession: jest.fn().mockImplementation(() => ({ - testConnection: jest.fn().mockImplementation(async (url) => { - if (url === "http://localhost:9222") { - return { - success: true, - message: "Successfully connected to Chrome", - endpoint: "ws://localhost:9222/devtools/browser/123", - } - } else { - return { - success: false, - message: "Failed to connect to Chrome", - endpoint: undefined, - } - } - }), - })), +jest.mock("p-wait-for", () => ({ + __esModule: true, + default: jest.fn().mockResolvedValue(undefined), })) -// Mock browserDiscovery -jest.mock("../../../services/browser/browserDiscovery", () => ({ - discoverChromeHostUrl: jest.fn().mockImplementation(async () => { - return "http://localhost:9222" - }), - tryChromeHostUrl: jest.fn().mockImplementation(async (url) => { - return url === "http://localhost:9222" - }), +jest.mock("fs/promises", () => ({ + mkdir: jest.fn(), + writeFile: jest.fn(), + readFile: jest.fn(), + unlink: jest.fn(), + rmdir: jest.fn(), +})) + +jest.mock("axios", () => ({ + get: jest.fn().mockResolvedValue({ data: { data: [] } }), + post: jest.fn(), })) jest.mock( @@ -75,6 +64,35 @@ jest.mock( { virtual: true }, ) +jest.mock("../../../services/browser/BrowserSession", () => ({ + BrowserSession: jest.fn().mockImplementation(() => ({ + testConnection: jest.fn().mockImplementation(async (url) => { + if (url === "http://localhost:9222") { + return { + success: true, + message: "Successfully connected to Chrome", + endpoint: "ws://localhost:9222/devtools/browser/123", + } + } else { + return { + success: false, + message: "Failed to connect to Chrome", + endpoint: undefined, + } + } + }), + })), +})) + +jest.mock("../../../services/browser/browserDiscovery", () => ({ + discoverChromeHostUrl: jest.fn().mockImplementation(async () => { + return "http://localhost:9222" + }), + tryChromeHostUrl: jest.fn().mockImplementation(async (url) => { + return url === "http://localhost:9222" + }), +})) + // Initialize mocks const mockAddCustomInstructions = jest.fn().mockResolvedValue("Combined instructions") @@ -116,7 +134,6 @@ jest.mock( { virtual: true }, ) -// Mock dependencies jest.mock("vscode", () => ({ ExtensionContext: jest.fn(), OutputChannel: jest.fn(), @@ -157,50 +174,24 @@ jest.mock("vscode", () => ({ }, })) -// Mock sound utility jest.mock("../../../utils/sound", () => ({ setSoundEnabled: jest.fn(), })) -// Mock tts utility jest.mock("../../../utils/tts", () => ({ setTtsEnabled: jest.fn(), setTtsSpeed: jest.fn(), })) -// Mock ESM modules -jest.mock("p-wait-for", () => ({ - __esModule: true, - default: jest.fn().mockResolvedValue(undefined), -})) - -// Mock fs/promises -jest.mock("fs/promises", () => ({ - mkdir: jest.fn(), - writeFile: jest.fn(), - readFile: jest.fn(), - unlink: jest.fn(), - rmdir: jest.fn(), -})) - -// Mock axios -jest.mock("axios", () => ({ - get: jest.fn().mockResolvedValue({ data: { data: [] } }), - post: jest.fn(), -})) - -// Mock buildApiHandler jest.mock("../../../api", () => ({ buildApiHandler: jest.fn(), })) -// Mock system prompt jest.mock("../../prompts/system", () => ({ SYSTEM_PROMPT: jest.fn().mockImplementation(async () => "mocked system prompt"), codeMode: "code", })) -// Mock WorkspaceTracker jest.mock("../../../integrations/workspace/WorkspaceTracker", () => { return jest.fn().mockImplementation(() => ({ initializeFilePaths: jest.fn(), @@ -208,9 +199,8 @@ jest.mock("../../../integrations/workspace/WorkspaceTracker", () => { })) }) -// Mock Cline -jest.mock("../../Cline", () => ({ - Cline: jest +jest.mock("../../task/Task", () => ({ + Task: jest .fn() .mockImplementation( (_provider, _apiConfiguration, _customInstructions, _diffEnabled, _fuzzyMatchThreshold, _task, taskId) => ({ @@ -230,7 +220,6 @@ jest.mock("../../Cline", () => ({ ), })) -// Mock extract-text jest.mock("../../../integrations/misc/extract-text", () => ({ extractTextFromFile: jest.fn().mockImplementation(async (_filePath: string) => { const content = "const x = 1;\nconst y = 2;\nconst z = 3;" @@ -239,17 +228,13 @@ jest.mock("../../../integrations/misc/extract-text", () => ({ }), })) -// Spy on console.error and console.log to suppress expected messages -beforeAll(() => { - jest.spyOn(console, "error").mockImplementation(() => {}) - jest.spyOn(console, "log").mockImplementation(() => {}) -}) - afterAll(() => { jest.restoreAllMocks() }) describe("ClineProvider", () => { + let defaultTaskOptions: TaskOptions + let provider: ClineProvider let mockContext: vscode.ExtensionContext let mockOutputChannel: vscode.OutputChannel @@ -328,6 +313,13 @@ describe("ClineProvider", () => { provider = new ClineProvider(mockContext, mockOutputChannel, "sidebar", new ContextProxy(mockContext)) + defaultTaskOptions = { + provider, + apiConfiguration: { + apiProvider: "openrouter", + }, + } + // @ts-ignore - Access private property for testing updateGlobalStateSpy = jest.spyOn(provider.contextProxy, "setValue") @@ -452,8 +444,7 @@ describe("ClineProvider", () => { test("clearTask aborts current task", async () => { // Setup Cline instance with auto-mock from the top of the file - const { Cline } = require("../../Cline") // Get the mocked class - const mockCline = new Cline() // Create a new mocked instance + const mockCline = new Task(defaultTaskOptions) // Create a new mocked instance // add the mock object to the stack await provider.addClineToStack(mockCline) @@ -476,9 +467,8 @@ describe("ClineProvider", () => { test("addClineToStack adds multiple Cline instances to the stack", async () => { // Setup Cline instance with auto-mock from the top of the file - const { Cline } = require("../../Cline") // Get the mocked class - const mockCline1 = new Cline() // Create a new mocked instance - const mockCline2 = new Cline() // Create a new mocked instance + const mockCline1 = new Task(defaultTaskOptions) // Create a new mocked instance + const mockCline2 = new Task(defaultTaskOptions) // Create a new mocked instance Object.defineProperty(mockCline1, "taskId", { value: "test-task-id-1", writable: true }) Object.defineProperty(mockCline2, "taskId", { value: "test-task-id-2", writable: true }) @@ -601,14 +591,16 @@ describe("ClineProvider", () => { expect(state.alwaysApproveResubmit).toBe(false) }) - test("loads saved API config when switching modes", async () => { + it("loads saved API config when switching modes", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] + const profile: ProviderSettingsEntry = { name: "test-config", id: "test-id", apiProvider: "anthropic" } + ;(provider as any).providerSettingsManager = { getModeConfigId: jest.fn().mockResolvedValue("test-id"), - listConfig: jest.fn().mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]), - loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic" }), + listConfig: jest.fn().mockResolvedValue([profile]), + activateProfile: jest.fn().mockResolvedValue(profile), setModeConfig: jest.fn(), } as any @@ -617,11 +609,11 @@ describe("ClineProvider", () => { // Should load the saved config for architect mode expect(provider.providerSettingsManager.getModeConfigId).toHaveBeenCalledWith("architect") - expect(provider.providerSettingsManager.loadConfig).toHaveBeenCalledWith("test-config") + expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ name: "test-config" }) expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "test-config") }) - test("saves current config when switching to mode without config", async () => { + it("saves current config when switching to mode without config", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] @@ -642,16 +634,15 @@ describe("ClineProvider", () => { expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "current-id") }) - test("saves config as default for current mode when loading config", async () => { + it("saves config as default for current mode when loading config", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] + const profile: ProviderSettingsEntry = { apiProvider: "anthropic", id: "new-id", name: "new-config" } + ;(provider as any).providerSettingsManager = { - loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic", id: "new-id" }), - loadConfigById: jest - .fn() - .mockResolvedValue({ config: { apiProvider: "anthropic", id: "new-id" }, name: "new-config" }), - listConfig: jest.fn().mockResolvedValue([{ name: "new-config", id: "new-id", apiProvider: "anthropic" }]), + activateProfile: jest.fn().mockResolvedValue(profile), + listConfig: jest.fn().mockResolvedValue([profile]), setModeConfig: jest.fn(), getModeConfigId: jest.fn().mockResolvedValue(undefined), } as any @@ -666,18 +657,19 @@ describe("ClineProvider", () => { expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "new-id") }) - test("load API configuration by ID works and updates mode config", async () => { + it("load API configuration by ID works and updates mode config", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] + const profile: ProviderSettingsEntry = { + name: "config-by-id", + id: "config-id-123", + apiProvider: "anthropic", + } + ;(provider as any).providerSettingsManager = { - loadConfigById: jest.fn().mockResolvedValue({ - config: { apiProvider: "anthropic", id: "config-id-123" }, - name: "config-by-id", - }), - listConfig: jest - .fn() - .mockResolvedValue([{ name: "config-by-id", id: "config-id-123", apiProvider: "anthropic" }]), + activateProfile: jest.fn().mockResolvedValue(profile), + listConfig: jest.fn().mockResolvedValue([profile]), setModeConfig: jest.fn(), getModeConfigId: jest.fn().mockResolvedValue(undefined), } as any @@ -691,8 +683,8 @@ describe("ClineProvider", () => { // Should save new config as default for architect mode expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "config-id-123") - // Ensure the loadConfigById method was called with the correct ID - expect(provider.providerSettingsManager.loadConfigById).toHaveBeenCalledWith("config-id-123") + // Ensure the `activateProfile` method was called with the correct ID + expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ id: "config-id-123" }) }) test("handles browserToolEnabled setting", async () => { @@ -815,49 +807,6 @@ describe("ClineProvider", () => { expect(mockPostMessage).toHaveBeenCalled() }) - test("uses mode-specific custom instructions in Cline initialization", async () => { - // Setup mock state - const modeCustomInstructions = "Code mode instructions" - const mockApiConfig = { - apiProvider: "openrouter", - } - - jest.spyOn(provider, "getState").mockResolvedValue({ - apiConfiguration: mockApiConfig, - customModePrompts: { - code: { customInstructions: modeCustomInstructions }, - }, - mode: "code", - diffEnabled: true, - enableCheckpoints: false, - fuzzyMatchThreshold: 1.0, - experiments: experimentDefault, - } as any) - - // Reset Cline mock - const { Cline } = require("../../Cline") - ;(Cline as jest.Mock).mockClear() - - // Initialize Cline with a task - await provider.initClineWithTask("Test task") - - // Verify Cline was initialized with mode-specific instructions - expect(Cline).toHaveBeenCalledWith({ - provider, - apiConfiguration: mockApiConfig, - customInstructions: modeCustomInstructions, - enableDiff: true, - enableCheckpoints: false, - fuzzyMatchThreshold: 1.0, - task: "Test task", - experiments: experimentDefault, - rootTask: undefined, - parentTask: undefined, - taskNumber: 1, - onCreated: expect.any(Function), - }) - }) - test("handles mode-specific custom instructions updates", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] @@ -895,7 +844,7 @@ describe("ClineProvider", () => { }) }) - test("saves mode config when updating API configuration", async () => { + it("saves mode config when updating API configuration", async () => { // Setup mock context with mode and config name mockContext = { ...mockContext, @@ -921,12 +870,14 @@ describe("ClineProvider", () => { ;(provider as any).providerSettingsManager = { listConfig: jest.fn().mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]), + saveConfig: jest.fn().mockResolvedValue("test-id"), setModeConfig: jest.fn(), } as any // Update API configuration await messageHandler({ - type: "apiConfiguration", + type: "upsertApiConfiguration", + text: "test-config", apiConfiguration: { apiProvider: "anthropic" }, }) @@ -959,13 +910,19 @@ describe("ClineProvider", () => { { ts: 4000, type: "say", say: "browser_action" }, // Response to delete { ts: 5000, type: "say", say: "user_feedback" }, // Next user message { ts: 6000, type: "say", say: "user_feedback" }, // Final message - ] - - const mockApiHistory = [{ ts: 1000 }, { ts: 2000 }, { ts: 3000 }, { ts: 4000 }, { ts: 5000 }, { ts: 6000 }] - - // Setup Cline instance with auto-mock from the top of the file - const { Cline } = require("../../Cline") // Get the mocked class - const mockCline = new Cline() // Create a new mocked instance + ] as ClineMessage[] + + const mockApiHistory = [ + { ts: 1000 }, + { ts: 2000 }, + { ts: 3000 }, + { ts: 4000 }, + { ts: 5000 }, + { ts: 6000 }, + ] as (Anthropic.MessageParam & { ts?: number })[] + + // Setup Task instance with auto-mock from the top of the file + const mockCline = new Task(defaultTaskOptions) // Create a new mocked instance mockCline.clineMessages = mockMessages // Set test-specific messages mockCline.apiConversationHistory = mockApiHistory // Set API history await provider.addClineToStack(mockCline) // Add the mocked instance to the stack @@ -1006,13 +963,19 @@ describe("ClineProvider", () => { { ts: 2000, type: "say", say: "text", value: 3000 }, // Message to delete { ts: 3000, type: "say", say: "user_feedback" }, { ts: 4000, type: "say", say: "user_feedback" }, - ] + ] as ClineMessage[] - const mockApiHistory = [{ ts: 1000 }, { ts: 2000 }, { ts: 3000 }, { ts: 4000 }] + const mockApiHistory = [ + { ts: 1000 }, + { ts: 2000 }, + { ts: 3000 }, + { ts: 4000 }, + ] as (Anthropic.MessageParam & { + ts?: number + })[] // Setup Cline instance with auto-mock from the top of the file - const { Cline } = require("../../Cline") // Get the mocked class - const mockCline = new Cline() // Create a new mocked instance + const mockCline = new Task(defaultTaskOptions) // Create a new mocked instance mockCline.clineMessages = mockMessages mockCline.apiConversationHistory = mockApiHistory await provider.addClineToStack(mockCline) @@ -1038,10 +1001,11 @@ describe("ClineProvider", () => { ;(vscode.window.showInformationMessage as jest.Mock).mockResolvedValue("Cancel") // Setup Cline instance with auto-mock from the top of the file - const { Cline } = require("../../Cline") // Get the mocked class - const mockCline = new Cline() // Create a new mocked instance - mockCline.clineMessages = [{ ts: 1000 }, { ts: 2000 }] - mockCline.apiConversationHistory = [{ ts: 1000 }, { ts: 2000 }] + const mockCline = new Task(defaultTaskOptions) // Create a new mocked instance + mockCline.clineMessages = [{ ts: 1000 }, { ts: 2000 }] as ClineMessage[] + mockCline.apiConversationHistory = [{ ts: 1000 }, { ts: 2000 }] as (Anthropic.MessageParam & { + ts?: number + })[] await provider.addClineToStack(mockCline) // Trigger message deletion @@ -1213,15 +1177,16 @@ describe("ClineProvider", () => { }) test("passes diffEnabled: false to SYSTEM_PROMPT when diff is disabled", async () => { - // Setup Cline instance with mocked api.getModel() - const { Cline } = require("../../Cline") - const mockCline = new Cline() + // Setup Task instance with mocked api.getModel() + const mockCline = new Task(defaultTaskOptions) + mockCline.api = { getModel: jest.fn().mockReturnValue({ id: "claude-3-sonnet", info: { supportsComputerUse: true }, }), - } + } as any + await provider.addClineToStack(mockCline) // Mock getState to return diffEnabled: false @@ -1537,13 +1502,17 @@ describe("ClineProvider", () => { await provider.resolveWebviewView(mockWebviewView) }) - test("loads saved API config when switching modes", async () => { + it("loads saved API config when switching modes", async () => { + const profile: ProviderSettingsEntry = { + name: "saved-config", + id: "saved-config-id", + apiProvider: "anthropic", + } + ;(provider as any).providerSettingsManager = { getModeConfigId: jest.fn().mockResolvedValue("saved-config-id"), - listConfig: jest - .fn() - .mockResolvedValue([{ name: "saved-config", id: "saved-config-id", apiProvider: "anthropic" }]), - loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic" }), + listConfig: jest.fn().mockResolvedValue([profile]), + activateProfile: jest.fn().mockResolvedValue(profile), setModeConfig: jest.fn(), } as any @@ -1555,7 +1524,7 @@ describe("ClineProvider", () => { // Verify saved config was loaded expect(provider.providerSettingsManager.getModeConfigId).toHaveBeenCalledWith("architect") - expect(provider.providerSettingsManager.loadConfig).toHaveBeenCalledWith("saved-config") + expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ name: "saved-config" }) expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "saved-config") // Verify state was posted to webview @@ -1671,14 +1640,11 @@ describe("ClineProvider", () => { currentApiConfigName: "test-config", } as any) - // Trigger updateApiConfiguration + // Trigger upsertApiConfiguration await messageHandler({ type: "upsertApiConfiguration", text: "test-config", - apiConfiguration: { - apiProvider: "anthropic", - apiKey: "test-key", - }, + apiConfiguration: { apiProvider: "anthropic", apiKey: "test-key" }, }) // Verify error was logged and user was notified @@ -1743,9 +1709,8 @@ describe("ClineProvider", () => { .mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]), } as any - // Setup Cline instance with auto-mock from the top of the file - const { Cline } = require("../../Cline") // Get the mocked class - const mockCline = new Cline() // Create a new mocked instance + // Setup Task instance with auto-mock from the top of the file + const mockCline = new Task(defaultTaskOptions) // Create a new mocked instance await provider.addClineToStack(mockCline) const testApiConfig = { @@ -2085,6 +2050,7 @@ describe.skip("ContextProxy integration", () => { }) describe("getTelemetryProperties", () => { + let defaultTaskOptions: TaskOptions let provider: ClineProvider let mockContext: vscode.ExtensionContext let mockOutputChannel: vscode.OutputChannel @@ -2114,9 +2080,15 @@ describe("getTelemetryProperties", () => { mockOutputChannel = { appendLine: jest.fn() } as unknown as vscode.OutputChannel provider = new ClineProvider(mockContext, mockOutputChannel, "sidebar", new ContextProxy(mockContext)) - // Setup Cline instance with mocked getModel method - const { Cline } = require("../../Cline") - mockCline = new Cline() + defaultTaskOptions = { + provider, + apiConfiguration: { + apiProvider: "openrouter", + }, + } + + // Setup Task instance with mocked getModel method + mockCline = new Task(defaultTaskOptions) mockCline.api = { getModel: jest.fn().mockReturnValue({ id: "claude-3-7-sonnet-20250219", diff --git a/src/core/webview/generateSystemPrompt.ts b/src/core/webview/generateSystemPrompt.ts new file mode 100644 index 00000000000..f676fa18f62 --- /dev/null +++ b/src/core/webview/generateSystemPrompt.ts @@ -0,0 +1,73 @@ +import { WebviewMessage } from "../../shared/WebviewMessage" +import { defaultModeSlug, getModeBySlug, getGroupName } from "../../shared/modes" +import { buildApiHandler } from "../../api" + +import { SYSTEM_PROMPT } from "../prompts/system" +import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-replace" + +import { ClineProvider } from "./ClineProvider" + +export const generateSystemPrompt = async (provider: ClineProvider, message: WebviewMessage) => { + const { + apiConfiguration, + customModePrompts, + customInstructions, + browserViewportSize, + diffEnabled, + mcpEnabled, + fuzzyMatchThreshold, + experiments, + enableMcpServerCreation, + browserToolEnabled, + language, + } = await provider.getState() + + const diffStrategy = new MultiSearchReplaceDiffStrategy(fuzzyMatchThreshold) + + const cwd = provider.cwd + + const mode = message.mode ?? defaultModeSlug + const customModes = await provider.customModesManager.getCustomModes() + + const rooIgnoreInstructions = provider.getCurrentCline()?.rooIgnoreController?.getInstructions() + + // Determine if browser tools can be used based on model support, mode, and user settings + let modelSupportsComputerUse = false + + // Create a temporary API handler to check if the model supports computer use + // This avoids relying on an active Cline instance which might not exist during preview + try { + const tempApiHandler = buildApiHandler(apiConfiguration) + modelSupportsComputerUse = tempApiHandler.getModel().info.supportsComputerUse ?? false + } catch (error) { + console.error("Error checking if model supports computer use:", error) + } + + // Check if the current mode includes the browser tool group + const modeConfig = getModeBySlug(mode, customModes) + const modeSupportsBrowser = modeConfig?.groups.some((group) => getGroupName(group) === "browser") ?? false + + // Only enable browser tools if the model supports it, the mode includes browser tools, + // and browser tools are enabled in settings + const canUseBrowserTool = modelSupportsComputerUse && modeSupportsBrowser && (browserToolEnabled ?? true) + + const systemPrompt = await SYSTEM_PROMPT( + provider.context, + cwd, + canUseBrowserTool, + mcpEnabled ? provider.getMcpHub() : undefined, + diffStrategy, + browserViewportSize ?? "900x600", + mode, + customModePrompts, + customModes, + customInstructions, + diffEnabled, + experiments, + enableMcpServerCreation, + language, + rooIgnoreInstructions, + ) + + return systemPrompt +} diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 003e2523a0c..88a1fb42eb9 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -4,9 +4,9 @@ import pWaitFor from "p-wait-for" import * as vscode from "vscode" import { ClineProvider } from "./ClineProvider" -import { Language, ApiConfigMeta } from "../../schemas" +import { Language, ProviderSettings } from "../../schemas" import { changeLanguage, t } from "../../i18n" -import { ApiConfiguration } from "../../shared/api" +import { RouterName, toRouterName } from "../../shared/api" import { supportPrompt } from "../../shared/support-prompt" import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage" @@ -32,14 +32,15 @@ import { openMention } from "../mentions" import { telemetryService } from "../../services/telemetry/TelemetryService" import { TelemetrySetting } from "../../shared/TelemetrySetting" import { getWorkspacePath } from "../../utils/path" -import { Mode, defaultModeSlug, getModeBySlug, getGroupName } from "../../shared/modes" -import { SYSTEM_PROMPT } from "../prompts/system" -import { buildApiHandler } from "../../api" +import { Mode, defaultModeSlug } from "../../shared/modes" import { GlobalState } from "../../schemas" import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-replace" -import { getModels } from "../../api/providers/fetchers/cache" +import { getModels, flushModels} from "../../api/providers/fetchers/cache" import { getpearAIExports } from "../../activate/registerPearListener" import { AGENT_RULES_DIR } from "../../shared/constants" +import { generateSystemPrompt } from "./generateSystemPrompt" + +const ALLOWED_VSCODE_SETTINGS = new Set(["terminal.integrated.inheritEnv"]) export const webviewMessageHandler = async (provider: ClineProvider, message: WebviewMessage) => { // Utility functions provided for concise get/update of global state via contextProxy API. @@ -91,19 +92,12 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We if (currentConfigName) { if (!(await provider.providerSettingsManager.hasConfig(currentConfigName))) { - // current config name not valid, get first config in list - await updateGlobalState("currentApiConfigName", listApiConfig?.[0]?.name) - if (listApiConfig?.[0]?.name) { - const apiConfig = await provider.providerSettingsManager.loadConfig( - listApiConfig?.[0]?.name, - ) + // Current config name not valid, get first config in list. + const name = listApiConfig[0]?.name + await updateGlobalState("currentApiConfigName", name) - await Promise.all([ - updateGlobalState("listApiConfigMeta", listApiConfig), - provider.postMessageToWebview({ type: "listApiConfig", listApiConfig }), - provider.updateApiConfiguration(apiConfig), - ]) - await provider.postStateToWebview() + if (name) { + await provider.activateProviderProfile({ name }) return } } @@ -140,14 +134,12 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We // initializing new instance of Cline will make sure that any agentically running promises in old instance don't affect our new task. this essentially creates a fresh slate for the new task const existingCline = provider.getCurrentCline() const creatorModeConfig = existingCline?.creatorModeConfig - + await provider.initClineWithTask(message.text, message.images, undefined, {}, creatorModeConfig) - break - case "apiConfiguration": - if (message.apiConfiguration) { - await provider.updateApiConfiguration(message.apiConfiguration) - } - await provider.postStateToWebview() + // Initializing new instance of Cline will make sure that any + // agentically running promises in old instance don't affect our new + // task. This essentially creates a fresh slate for the new task. + await provider.initClineWithTask(message.text, message.images) break case "customInstructions": await provider.updateCustomInstructions(message.text) @@ -292,12 +284,19 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We case "resetState": await provider.resetState() break + case "flushRouterModels": + const routerName: RouterName = toRouterName(message.text) + await flushModels(routerName) + break case "requestRouterModels": - const [openRouterModels, requestyModels, glamaModels, unboundModels] = await Promise.all([ - getModels("openrouter"), - getModels("requesty"), - getModels("glama"), - getModels("unbound"), + const { apiConfiguration } = await provider.getState() + + const [openRouterModels, requestyModels, glamaModels, unboundModels, litellmModels] = await Promise.all([ + getModels("openrouter", apiConfiguration.openRouterApiKey), + getModels("requesty", apiConfiguration.requestyApiKey), + getModels("glama", apiConfiguration.glamaApiKey), + getModels("unbound", apiConfiguration.unboundApiKey), + getModels("litellm", apiConfiguration.litellmApiKey, apiConfiguration.litellmBaseUrl), ]) provider.postMessageToWebview({ @@ -307,6 +306,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We requesty: requestyModels, glama: glamaModels, unbound: unboundModels, + litellm: litellmModels, }, }) break @@ -341,7 +341,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We openImage(message.text!) break case "openFile": - openFile(message.text!, message.values as { create?: boolean; content?: string }) + openFile(message.text!, message.values as { create?: boolean; content?: string; line?: number }) break case "openMention": openMention(message.text) @@ -380,16 +380,29 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We break case "allowedCommands": await provider.context.globalState.update("allowedCommands", message.commands) - // Also update workspace settings + + // Also update workspace settings. await vscode.workspace .getConfiguration("roo-cline") .update("allowedCommands", message.commands, vscode.ConfigurationTarget.Global) + break + case "openCustomModesSettings": { + const customModesFilePath = await provider.customModesManager.getCustomModesFilePath() + + if (customModesFilePath) { + openFile(customModesFilePath) + } + + break + } case "openMcpSettings": { const mcpSettingsFilePath = await provider.getMcpHub()?.getMcpSettingsFilePath() + if (mcpSettingsFilePath) { openFile(mcpSettingsFilePath) } + break } case "openProjectMcpSettings": { @@ -405,20 +418,16 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We try { await fs.mkdir(rooDir, { recursive: true }) const exists = await fileExistsAtPath(mcpPath) + if (!exists) { await fs.writeFile(mcpPath, JSON.stringify({ mcpServers: {} }, null, 2)) } + await openFile(mcpPath) } catch (error) { vscode.window.showErrorMessage(t("common:errors.create_mcp_json", { error: `${error}` })) } - break - } - case "openCustomModesSettings": { - const customModesFilePath = await provider.customModesManager.getCustomModesFilePath() - if (customModesFilePath) { - openFile(customModesFilePath) - } + break } case "deleteMcpServer": { @@ -564,6 +573,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We if (!message.text) { // Use testBrowserConnection for auto-discovery const chromeHostUrl = await discoverChromeHostUrl() + if (chromeHostUrl) { // Send the result back to the webview await provider.postMessageToWebview({ @@ -583,6 +593,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We // Test the provided URL const customHostUrl = message.text const hostIsValid = await tryChromeHostUrl(message.text) + // Send the result back to the webview await provider.postMessageToWebview({ type: "browserConnectionResult", @@ -596,6 +607,42 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We case "fuzzyMatchThreshold": await updateGlobalState("fuzzyMatchThreshold", message.value) await provider.postStateToWebview() + break + case "updateVSCodeSetting": { + const { setting, value } = message + + if (setting !== undefined && value !== undefined) { + if (ALLOWED_VSCODE_SETTINGS.has(setting)) { + await vscode.workspace.getConfiguration().update(setting, value, true) + } else { + vscode.window.showErrorMessage(`Cannot update restricted VSCode setting: ${setting}`) + } + } + + break + } + case "getVSCodeSetting": + const { setting } = message + + if (setting) { + try { + await provider.postMessageToWebview({ + type: "vsCodeSetting", + setting, + value: vscode.workspace.getConfiguration().get(setting), + }) + } catch (error) { + console.error(`Failed to get VSCode setting ${message.setting}:`, error) + + await provider.postMessageToWebview({ + type: "vsCodeSetting", + setting, + error: `Failed to get setting: ${error.message}`, + value: undefined, + }) + } + } + break case "alwaysApproveResubmit": await updateGlobalState("alwaysApproveResubmit", message.bool ?? false) @@ -894,45 +941,36 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We const { apiConfiguration, customSupportPrompts, listApiConfigMeta, enhancementApiConfigId } = await provider.getState() - // Try to get enhancement config first, fall back to current config - let configToUse: ApiConfiguration = apiConfiguration - if (enhancementApiConfigId) { - const config = listApiConfigMeta?.find((c: ApiConfigMeta) => c.id === enhancementApiConfigId) - if (config?.name) { - const loadedConfig = await provider.providerSettingsManager.loadConfig(config.name) - if (loadedConfig.apiProvider) { - configToUse = loadedConfig - } + // Try to get enhancement config first, fall back to current config. + let configToUse: ProviderSettings = apiConfiguration + + if (enhancementApiConfigId && !!listApiConfigMeta.find(({ id }) => id === enhancementApiConfigId)) { + const { name: _, ...providerSettings } = await provider.providerSettingsManager.getProfile({ + id: enhancementApiConfigId, + }) + + if (providerSettings.apiProvider) { + configToUse = providerSettings } } const enhancedPrompt = await singleCompletionHandler( configToUse, - supportPrompt.create( - "ENHANCE", - { - userInput: message.text, - }, - customSupportPrompts, - ), + supportPrompt.create("ENHANCE", { userInput: message.text }, customSupportPrompts), ) - // Capture telemetry for prompt enhancement + // Capture telemetry for prompt enhancement. const currentCline = provider.getCurrentCline() telemetryService.capturePromptEnhanced(currentCline?.taskId) - await provider.postMessageToWebview({ - type: "enhancedPrompt", - text: enhancedPrompt, - }) + await provider.postMessageToWebview({ type: "enhancedPrompt", text: enhancedPrompt }) } catch (error) { provider.log( `Error enhancing prompt: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, ) + vscode.window.showErrorMessage(t("common:errors.enhance_prompt")) - await provider.postMessageToWebview({ - type: "enhancedPrompt", - }) + await provider.postMessageToWebview({ type: "enhancedPrompt" }) } } break @@ -1039,7 +1077,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We break case "upsertApiConfiguration": if (message.text && message.apiConfiguration) { - await provider.upsertApiConfiguration(message.text, message.apiConfiguration) + await provider.upsertProviderProfile(message.text, message.apiConfiguration) } break case "renameApiConfiguration": @@ -1051,30 +1089,23 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We break } - // Load the old configuration to get its ID - const oldConfig = await provider.providerSettingsManager.loadConfig(oldName) + // Load the old configuration to get its ID. + const { id } = await provider.providerSettingsManager.getProfile({ name: oldName }) - // Create a new configuration with the same ID - const newConfig = { - ...message.apiConfiguration, - id: oldConfig.id, // Preserve the ID - } + // Create a new configuration with the new name and old ID. + await provider.providerSettingsManager.saveConfig(newName, { ...message.apiConfiguration, id }) - // Save with the new name but same ID - await provider.providerSettingsManager.saveConfig(newName, newConfig) + // Delete the old configuration. await provider.providerSettingsManager.deleteConfig(oldName) - const listApiConfig = await provider.providerSettingsManager.listConfig() - - // Update listApiConfigMeta first to ensure UI has latest data - await updateGlobalState("listApiConfigMeta", listApiConfig) - await updateGlobalState("currentApiConfigName", newName) - - await provider.postStateToWebview() + // Re-activate to update the global settings related to the + // currently activated provider profile. + await provider.activateProviderProfile({ name: newName }) } catch (error) { provider.log( `Error rename api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, ) + vscode.window.showErrorMessage(t("common:errors.rename_api_config")) } } @@ -1082,16 +1113,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We case "loadApiConfiguration": if (message.text) { try { - const apiConfig = await provider.providerSettingsManager.loadConfig(message.text) - const listApiConfig = await provider.providerSettingsManager.listConfig() - - await Promise.all([ - updateGlobalState("listApiConfigMeta", listApiConfig), - updateGlobalState("currentApiConfigName", message.text), - provider.updateApiConfiguration(apiConfig), - ]) - - await provider.postStateToWebview() + await provider.activateProviderProfile({ name: message.text }) } catch (error) { provider.log( `Error load api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, @@ -1103,18 +1125,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We case "loadApiConfigurationById": if (message.text) { try { - const { config: apiConfig, name } = await provider.providerSettingsManager.loadConfigById( - message.text, - ) - const listApiConfig = await provider.providerSettingsManager.listConfig() - - await Promise.all([ - updateGlobalState("listApiConfigMeta", listApiConfig), - updateGlobalState("currentApiConfigName", name), - provider.updateApiConfiguration(apiConfig), - ]) - - await provider.postStateToWebview() + await provider.activateProviderProfile({ id: message.text }) } catch (error) { provider.log( `Error load api configuration by ID: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, @@ -1135,29 +1146,25 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We break } - try { - await provider.providerSettingsManager.deleteConfig(message.text) - const listApiConfig = await provider.providerSettingsManager.listConfig() - - // Update listApiConfigMeta first to ensure UI has latest data - await updateGlobalState("listApiConfigMeta", listApiConfig) + const oldName = message.text - // If this was the current config, switch to first available - const currentApiConfigName = getGlobalState("currentApiConfigName") + const newName = (await provider.providerSettingsManager.listConfig()).filter( + (c) => c.name !== oldName, + )[0]?.name - if (message.text === currentApiConfigName && listApiConfig?.[0]?.name) { - const apiConfig = await provider.providerSettingsManager.loadConfig(listApiConfig[0].name) - await Promise.all([ - updateGlobalState("currentApiConfigName", listApiConfig[0].name), - provider.updateApiConfiguration(apiConfig), - ]) - } + if (!newName) { + vscode.window.showErrorMessage(t("common:errors.delete_api_config")) + return + } - await provider.postStateToWebview() + try { + await provider.providerSettingsManager.deleteConfig(oldName) + await provider.activateProviderProfile({ name: newName }) } catch (error) { provider.log( `Error delete api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, ) + vscode.window.showErrorMessage(t("common:errors.delete_api_config")) } } @@ -1277,75 +1284,10 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We case "openPearAICreatorFeedbackOverlay": const pearAIExports = await getpearAIExports(); const currentCline = provider.getCurrentCline(); - - + + // Open the feedback form with the chat history pearAIExports.pearAPI.creatorMode.openFeedbackForm(currentCline?.clineMessages || []); break } } - -const generateSystemPrompt = async (provider: ClineProvider, message: WebviewMessage) => { - const { - apiConfiguration, - customModePrompts, - customInstructions, - browserViewportSize, - diffEnabled, - mcpEnabled, - fuzzyMatchThreshold, - experiments, - enableMcpServerCreation, - browserToolEnabled, - language, - } = await provider.getState() - - const diffStrategy = new MultiSearchReplaceDiffStrategy(fuzzyMatchThreshold) - - const cwd = provider.cwd - - const mode = message.mode ?? defaultModeSlug - const customModes = await provider.customModesManager.getCustomModes() - - const rooIgnoreInstructions = provider.getCurrentCline()?.rooIgnoreController?.getInstructions() - - // Determine if browser tools can be used based on model support, mode, and user settings - let modelSupportsComputerUse = false - - // Create a temporary API handler to check if the model supports computer use - // This avoids relying on an active Cline instance which might not exist during preview - try { - const tempApiHandler = buildApiHandler(apiConfiguration) - modelSupportsComputerUse = tempApiHandler.getModel().info.supportsComputerUse ?? false - } catch (error) { - console.error("Error checking if model supports computer use:", error) - } - - // Check if the current mode includes the browser tool group - const modeConfig = getModeBySlug(mode, customModes) - const modeSupportsBrowser = modeConfig?.groups.some((group) => getGroupName(group) === "browser") ?? false - - // Only enable browser tools if the model supports it, the mode includes browser tools, - // and browser tools are enabled in settings - const canUseBrowserTool = modelSupportsComputerUse && modeSupportsBrowser && (browserToolEnabled ?? true) - - const systemPrompt = await SYSTEM_PROMPT( - provider.context, - cwd, - canUseBrowserTool, - mcpEnabled ? provider.getMcpHub() : undefined, - diffStrategy, - browserViewportSize ?? "900x600", - mode, - customModePrompts, - customModes, - customInstructions, - diffEnabled, - experiments, - enableMcpServerCreation, - language, - rooIgnoreInstructions, - ) - - return systemPrompt -} diff --git a/src/exports/api.ts b/src/exports/api.ts index 8498fa763f9..5cd9c073219 100644 --- a/src/exports/api.ts +++ b/src/exports/api.ts @@ -6,8 +6,15 @@ import * as path from "path" import { getWorkspacePath } from "../utils/path" import { ClineProvider } from "../core/webview/ClineProvider" import { openClineInNewTab } from "../activate/registerCommands" -import { RooCodeSettings, RooCodeEvents, RooCodeEventName } from "../schemas" -import { IpcOrigin, IpcMessageType, TaskCommandName, TaskEvent } from "../schemas/ipc" +import { + RooCodeSettings, + RooCodeEvents, + RooCodeEventName, + ProviderSettings, + ProviderSettingsEntry, + isSecretStateKey, +} from "../schemas" +import { IpcOrigin, IpcMessageType, TaskCommandName, TaskEvent } from "../schemas" import { RooCodeAPI } from "./interface" import { IpcServer } from "./ipc" @@ -178,91 +185,6 @@ export class API extends EventEmitter implements RooCodeAPI { await this.sidebarProvider.postMessageToWebview({ type: "invoke", invoke: "secondaryButtonClick" }) } - public getConfiguration() { - return this.sidebarProvider.getValues() - } - - public async setConfiguration(values: RooCodeSettings) { - await this.sidebarProvider.setValues(values) - await this.sidebarProvider.providerSettingsManager.saveConfig(values.currentApiConfigName || "default", values) - await this.sidebarProvider.postStateToWebview() - } - - public async createProfile(name: string) { - if (!name || !name.trim()) { - throw new Error("Profile name cannot be empty") - } - - const currentSettings = this.getConfiguration() - const profiles = currentSettings.listApiConfigMeta || [] - - if (profiles.some((profile) => profile.name === name)) { - throw new Error(`A profile with the name "${name}" already exists`) - } - - const id = this.sidebarProvider.providerSettingsManager.generateId() - - await this.setConfiguration({ - ...currentSettings, - listApiConfigMeta: [ - ...profiles, - { - id, - name: name.trim(), - apiProvider: "openai" as const, - }, - ], - }) - - return id - } - - public getProfiles() { - return (this.getConfiguration().listApiConfigMeta || []).map((profile) => profile.name) - } - - public async setActiveProfile(name: string) { - const currentSettings = this.getConfiguration() - const profiles = currentSettings.listApiConfigMeta || [] - - const profile = profiles.find((p) => p.name === name) - - if (!profile) { - throw new Error(`Profile with name "${name}" does not exist`) - } - - await this.setConfiguration({ ...currentSettings, currentApiConfigName: profile.name }) - } - - public getActiveProfile() { - return this.getConfiguration().currentApiConfigName - } - - public async deleteProfile(name: string) { - const currentSettings = this.getConfiguration() - const profiles = currentSettings.listApiConfigMeta || [] - const targetIndex = profiles.findIndex((p) => p.name === name) - - if (targetIndex === -1) { - throw new Error(`Profile with name "${name}" does not exist`) - } - - const profileToDelete = profiles[targetIndex] - profiles.splice(targetIndex, 1) - - // If we're deleting the active profile, clear the currentApiConfigName. - const newSettings: RooCodeSettings = { - ...currentSettings, - listApiConfigMeta: profiles, - currentApiConfigName: - currentSettings.currentApiConfigName === profileToDelete.name - ? undefined - : currentSettings.currentApiConfigName, - } - - await this.setConfiguration(newSettings) - } - public isReady() { return this.sidebarProvider.viewLaunched } @@ -328,4 +250,103 @@ export class API extends EventEmitter implements RooCodeAPI { this.logfile = undefined } } + + // Global Settings Management + + public getConfiguration(): RooCodeSettings { + return Object.fromEntries( + Object.entries(this.sidebarProvider.getValues()).filter(([key]) => !isSecretStateKey(key)), + ) + } + + public async setConfiguration(values: RooCodeSettings) { + await this.sidebarProvider.contextProxy.setValues(values) + await this.sidebarProvider.providerSettingsManager.saveConfig(values.currentApiConfigName || "default", values) + await this.sidebarProvider.postStateToWebview() + } + + // Provider Profile Management + + public getProfiles(): string[] { + return this.sidebarProvider.getProviderProfileEntries().map(({ name }) => name) + } + + public getProfileEntry(name: string): ProviderSettingsEntry | undefined { + return this.sidebarProvider.getProviderProfileEntry(name) + } + + public async createProfile(name: string, profile?: ProviderSettings, activate: boolean = true) { + const entry = this.getProfileEntry(name) + + if (entry) { + throw new Error(`Profile with name "${name}" already exists`) + } + + const id = await this.sidebarProvider.upsertProviderProfile(name, profile ?? {}, activate) + + if (!id) { + throw new Error(`Failed to create profile with name "${name}"`) + } + + return id + } + + public async updateProfile( + name: string, + profile: ProviderSettings, + activate: boolean = true, + ): Promise { + const entry = this.getProfileEntry(name) + + if (!entry) { + throw new Error(`Profile with name "${name}" does not exist`) + } + + const id = await this.sidebarProvider.upsertProviderProfile(name, profile, activate) + + if (!id) { + throw new Error(`Failed to update profile with name "${name}"`) + } + + return id + } + + public async upsertProfile( + name: string, + profile: ProviderSettings, + activate: boolean = true, + ): Promise { + const id = await this.sidebarProvider.upsertProviderProfile(name, profile, activate) + + if (!id) { + throw new Error(`Failed to upsert profile with name "${name}"`) + } + + return id + } + + public async deleteProfile(name: string): Promise { + const entry = this.getProfileEntry(name) + + if (!entry) { + throw new Error(`Profile with name "${name}" does not exist`) + } + + await this.sidebarProvider.deleteProviderProfile(entry) + } + + public getActiveProfile(): string | undefined { + return this.getConfiguration().currentApiConfigName + } + + public async setActiveProfile(name: string): Promise { + const entry = this.getProfileEntry(name) + + if (!entry) { + throw new Error(`Profile with name "${name}" does not exist`) + } + + await this.sidebarProvider.activateProviderProfile({ name }) + return this.getActiveProfile() + } } diff --git a/src/exports/interface.ts b/src/exports/interface.ts index a0ef46a2260..ababc64557a 100644 --- a/src/exports/interface.ts +++ b/src/exports/interface.ts @@ -1,12 +1,47 @@ import { EventEmitter } from "events" +import { Socket } from "node:net" + +/** + * Types + */ + +import type { + GlobalSettings, + ProviderSettings, + ProviderSettingsEntry, + ClineMessage, + TokenUsage, + RooCodeEvents, + IpcMessage, + TaskCommand, + TaskEvent, +} from "./types" + +export type { + GlobalSettings, + ProviderSettings, + ProviderSettingsEntry, + ClineMessage, + TokenUsage, + RooCodeEvents, + IpcMessage, + TaskCommand, + TaskEvent, +} + +/** + * Enums + */ -import type { ProviderSettings, GlobalSettings, ClineMessage, TokenUsage, RooCodeEvents } from "./types" -export type { RooCodeSettings, ProviderSettings, GlobalSettings, ClineMessage, TokenUsage, RooCodeEvents } +import { RooCodeEventName, IpcOrigin, IpcMessageType } from "../schemas" -import { RooCodeEventName } from "../schemas" -export type { RooCodeEventName } +export { RooCodeEventName, IpcOrigin, IpcMessageType } -type RooCodeSettings = GlobalSettings & ProviderSettings +/** + * RooCodeAPI + */ + +export type RooCodeSettings = GlobalSettings & ProviderSettings export interface RooCodeAPI extends EventEmitter { /** @@ -74,6 +109,11 @@ export interface RooCodeAPI extends EventEmitter { */ pressSecondaryButton(): Promise + /** + * Returns true if the API is ready to use. + */ + isReady(): boolean + /** * Returns the current configuration. * @returns The current configuration. @@ -87,30 +127,46 @@ export interface RooCodeAPI extends EventEmitter { setConfiguration(values: RooCodeSettings): Promise /** - * Creates a new API configuration profile + * Returns a list of all configured profile names + * @returns Array of profile names + */ + getProfiles(): string[] + + /** + * Returns the profile entry for a given name * @param name The name of the profile - * @returns The ID of the created profile + * @returns The profile entry, or undefined if the profile does not exist */ - createProfile(name: string): Promise + getProfileEntry(name: string): ProviderSettingsEntry | undefined /** - * Returns a list of all configured profile names - * @returns Array of profile names + * Creates a new API configuration profile + * @param name The name of the profile + * @param profile The profile to create; defaults to an empty object + * @param activate Whether to activate the profile after creation; defaults to true + * @returns The ID of the created profile + * @throws Error if the profile already exists */ - getProfiles(): string[] + createProfile(name: string, profile?: ProviderSettings, activate?: boolean): Promise /** - * Changes the active API configuration profile - * @param name The name of the profile to activate + * Updates an existing API configuration profile + * @param name The name of the profile + * @param profile The profile to update + * @param activate Whether to activate the profile after update; defaults to true + * @returns The ID of the updated profile * @throws Error if the profile does not exist */ - setActiveProfile(name: string): Promise + updateProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise /** - * Returns the name of the currently active profile - * @returns The profile name, or undefined if no profile is active + * Creates a new API configuration profile or updates an existing one + * @param name The name of the profile + * @param profile The profile to create or update; defaults to an empty object + * @param activate Whether to activate the profile after upsert; defaults to true + * @returns The ID of the upserted profile */ - getActiveProfile(): string | undefined + upsertProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise /** * Deletes a profile by name @@ -120,7 +176,38 @@ export interface RooCodeAPI extends EventEmitter { deleteProfile(name: string): Promise /** - * Returns true if the API is ready to use. + * Returns the name of the currently active profile + * @returns The profile name, or undefined if no profile is active */ - isReady(): boolean + getActiveProfile(): string | undefined + + /** + * Changes the active API configuration profile + * @param name The name of the profile to activate + * @throws Error if the profile does not exist + */ + setActiveProfile(name: string): Promise +} + +/** + * RooCodeIpcServer + */ + +export type IpcServerEvents = { + [IpcMessageType.Connect]: [clientId: string] + [IpcMessageType.Disconnect]: [clientId: string] + [IpcMessageType.TaskCommand]: [clientId: string, data: TaskCommand] + [IpcMessageType.TaskEvent]: [relayClientId: string | undefined, data: TaskEvent] +} + +export interface RooCodeIpcServer extends EventEmitter { + listen(): void + + broadcast(message: IpcMessage): void + + send(client: string | Socket, message: IpcMessage): void + + get socketPath(): string + + get isListening(): boolean } diff --git a/src/exports/ipc.ts b/src/exports/ipc.ts index 33a5b1ff668..85950c5ee61 100644 --- a/src/exports/ipc.ts +++ b/src/exports/ipc.ts @@ -4,20 +4,14 @@ import * as crypto from "node:crypto" import ipc from "node-ipc" -import { IpcOrigin, IpcMessageType, IpcMessage, ipcMessageSchema, TaskCommand, TaskEvent } from "../schemas/ipc" +import { IpcOrigin, IpcMessageType, type IpcMessage, ipcMessageSchema } from "../schemas" +import type { IpcServerEvents, RooCodeIpcServer } from "./interface" /** * IpcServer */ -type IpcServerEvents = { - [IpcMessageType.Connect]: [clientId: string] - [IpcMessageType.Disconnect]: [clientId: string] - [IpcMessageType.TaskCommand]: [clientId: string, data: TaskCommand] - [IpcMessageType.TaskEvent]: [relayClientId: string | undefined, data: TaskEvent] -} - -export class IpcServer extends EventEmitter { +export class IpcServer extends EventEmitter implements RooCodeIpcServer { private readonly _socketPath: string private readonly _log: (...args: unknown[]) => void private readonly _clients: Map diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts index d3a05f43ced..4e2567429b7 100644 --- a/src/exports/roo-code.d.ts +++ b/src/exports/roo-code.d.ts @@ -1,4 +1,187 @@ import { EventEmitter } from "events" +import { Socket } from "node:net" + +type GlobalSettings = { + currentApiConfigName?: string | undefined + listApiConfigMeta?: + | { + id: string + name: string + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + }[] + | undefined + pinnedApiConfigs?: + | { + [x: string]: boolean + } + | undefined + lastShownAnnouncementId?: string | undefined + customInstructions?: string | undefined + taskHistory?: + | { + id: string + number: number + ts: number + task: string + tokensIn: number + tokensOut: number + cacheWrites?: number | undefined + cacheReads?: number | undefined + totalCost: number + size?: number | undefined + workspace?: string | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + }[] + | undefined + autoApprovalEnabled?: boolean | undefined + alwaysAllowReadOnly?: boolean | undefined + alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined + alwaysAllowWrite?: boolean | undefined + alwaysAllowWriteOutsideWorkspace?: boolean | undefined + writeDelayMs?: number | undefined + alwaysAllowBrowser?: boolean | undefined + alwaysApproveResubmit?: boolean | undefined + requestDelaySeconds?: number | undefined + alwaysAllowMcp?: boolean | undefined + alwaysAllowModeSwitch?: boolean | undefined + alwaysAllowSubtasks?: boolean | undefined + alwaysAllowExecute?: boolean | undefined + allowedCommands?: string[] | undefined + browserToolEnabled?: boolean | undefined + browserViewportSize?: string | undefined + screenshotQuality?: number | undefined + remoteBrowserEnabled?: boolean | undefined + remoteBrowserHost?: string | undefined + cachedChromeHostUrl?: string | undefined + enableCheckpoints?: boolean | undefined + ttsEnabled?: boolean | undefined + ttsSpeed?: number | undefined + soundEnabled?: boolean | undefined + soundVolume?: number | undefined + maxOpenTabsContext?: number | undefined + maxWorkspaceFiles?: number | undefined + showRooIgnoredFiles?: boolean | undefined + maxReadFileLine?: number | undefined + terminalOutputLineLimit?: number | undefined + terminalShellIntegrationTimeout?: number | undefined + terminalShellIntegrationDisabled?: boolean | undefined + terminalCommandDelay?: number | undefined + terminalPowershellCounter?: boolean | undefined + terminalZshClearEolMark?: boolean | undefined + terminalZshOhMy?: boolean | undefined + terminalZshP10k?: boolean | undefined + terminalZdotdir?: boolean | undefined + terminalCompressProgressBar?: boolean | undefined + rateLimitSeconds?: number | undefined + diffEnabled?: boolean | undefined + fuzzyMatchThreshold?: number | undefined + experiments?: + | { + autoCondenseContext: boolean + powerSteering: boolean + } + | undefined + language?: + | ( + | "ca" + | "de" + | "en" + | "es" + | "fr" + | "hi" + | "it" + | "ja" + | "ko" + | "nl" + | "pl" + | "pt-BR" + | "ru" + | "tr" + | "vi" + | "zh-CN" + | "zh-TW" + ) + | undefined + telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined + mcpEnabled?: boolean | undefined + enableMcpServerCreation?: boolean | undefined + mode?: string | undefined + modeApiConfigs?: + | { + [x: string]: string + } + | undefined + customModes?: + | { + slug: string + name: string + roleDefinition: string + whenToUse?: string | undefined + customInstructions?: string | undefined + groups: ( + | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") + | [ + "read" | "edit" | "browser" | "command" | "mcp" | "modes", + { + fileRegex?: string | undefined + description?: string | undefined + }, + ] + )[] + source?: ("global" | "project") | undefined + backendOnly?: boolean | undefined + }[] + | undefined + customModePrompts?: + | { + [x: string]: + | { + roleDefinition?: string | undefined + whenToUse?: string | undefined + customInstructions?: string | undefined + } + | undefined + } + | undefined + customSupportPrompts?: + | { + [x: string]: string | undefined + } + | undefined + enhancementApiConfigId?: string | undefined + historyPreviewCollapsed?: boolean | undefined +} type ProviderSettings = { apiProvider?: @@ -22,8 +205,19 @@ type ProviderSettings = { | "fake-ai" | "pearai" | "xai" + | "groq" + | "chutes" + | "litellm" ) | undefined + includeMaxTokens?: boolean | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + diffEnabled?: boolean | undefined + fuzzyMatchThreshold?: number | undefined + modelTemperature?: (number | null) | undefined + rateLimitSeconds?: number | undefined + modelMaxTokens?: number | undefined + modelMaxThinkingTokens?: number | undefined apiModelId?: string | undefined apiKey?: string | undefined anthropicBaseUrl?: string | undefined @@ -41,7 +235,6 @@ type ProviderSettings = { awsRegion?: string | undefined awsUseCrossRegionInference?: boolean | undefined awsUsePromptCache?: boolean | undefined - awspromptCacheId?: string | undefined awsProfile?: string | undefined awsUseProfile?: boolean | undefined awsCustomArn?: string | undefined @@ -62,7 +255,6 @@ type ProviderSettings = { supportsImages?: boolean | undefined supportsComputerUse?: boolean | undefined supportsPromptCache: boolean - isPromptCacheOptional?: boolean | undefined inputPrice?: number | undefined outputPrice?: number | undefined cacheWritesPrice?: number | undefined @@ -121,16 +313,6 @@ type ProviderSettings = { unboundModelId?: string | undefined requestyApiKey?: string | undefined requestyModelId?: string | undefined - xaiApiKey?: string | undefined - modelMaxTokens?: number | undefined - modelMaxThinkingTokens?: number | undefined - includeMaxTokens?: boolean | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - promptCachingEnabled?: boolean | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - modelTemperature?: (number | null) | undefined - rateLimitSeconds?: number | undefined fakeAi?: unknown | undefined pearaiBaseUrl?: string | undefined pearaiModelId?: string | undefined @@ -143,7 +325,6 @@ type ProviderSettings = { supportsImages?: boolean | undefined supportsComputerUse?: boolean | undefined supportsPromptCache: boolean - isPromptCacheOptional?: boolean | undefined inputPrice?: number | undefined outputPrice?: number | undefined cacheWritesPrice?: number | undefined @@ -176,7 +357,6 @@ type ProviderSettings = { supportsImages?: boolean | undefined supportsComputerUse?: boolean | undefined supportsPromptCache: boolean - isPromptCacheOptional?: boolean | undefined inputPrice?: number | undefined outputPrice?: number | undefined cacheWritesPrice?: number | undefined @@ -209,181 +389,43 @@ type ProviderSettings = { newProjectPath?: string | undefined } | undefined + xaiApiKey?: string | undefined + groqApiKey?: string | undefined + chutesApiKey?: string | undefined + litellmBaseUrl?: string | undefined + litellmApiKey?: string | undefined + litellmModelId?: string | undefined } -type GlobalSettings = { - currentApiConfigName?: string | undefined - listApiConfigMeta?: - | { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "pearai" - | "xai" - ) - | undefined - }[] - | undefined - pinnedApiConfigs?: - | { - [x: string]: boolean - } - | undefined - lastShownAnnouncementId?: string | undefined - customInstructions?: string | undefined - taskHistory?: - | { - id: string - number: number - ts: number - task: string - tokensIn: number - tokensOut: number - cacheWrites?: number | undefined - cacheReads?: number | undefined - totalCost: number - size?: number | undefined - workspace?: string | undefined - creatorModeConfig?: - | { - creatorMode?: boolean | undefined - newProjectType?: string | undefined - newProjectPath?: string | undefined - } - | undefined - }[] - | undefined - autoApprovalEnabled?: boolean | undefined - alwaysAllowReadOnly?: boolean | undefined - alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined - alwaysAllowWrite?: boolean | undefined - alwaysAllowWriteOutsideWorkspace?: boolean | undefined - writeDelayMs?: number | undefined - alwaysAllowBrowser?: boolean | undefined - alwaysApproveResubmit?: boolean | undefined - requestDelaySeconds?: number | undefined - alwaysAllowMcp?: boolean | undefined - alwaysAllowModeSwitch?: boolean | undefined - alwaysAllowSubtasks?: boolean | undefined - alwaysAllowExecute?: boolean | undefined - allowedCommands?: string[] | undefined - browserToolEnabled?: boolean | undefined - browserViewportSize?: string | undefined - screenshotQuality?: number | undefined - remoteBrowserEnabled?: boolean | undefined - remoteBrowserHost?: string | undefined - cachedChromeHostUrl?: string | undefined - enableCheckpoints?: boolean | undefined - ttsEnabled?: boolean | undefined - ttsSpeed?: number | undefined - soundEnabled?: boolean | undefined - soundVolume?: number | undefined - maxOpenTabsContext?: number | undefined - maxWorkspaceFiles?: number | undefined - showRooIgnoredFiles?: boolean | undefined - maxReadFileLine?: number | undefined - terminalOutputLineLimit?: number | undefined - terminalShellIntegrationTimeout?: number | undefined - terminalShellIntegrationDisabled?: boolean | undefined - terminalCommandDelay?: number | undefined - terminalPowershellCounter?: boolean | undefined - terminalZshClearEolMark?: boolean | undefined - terminalZshOhMy?: boolean | undefined - terminalZshP10k?: boolean | undefined - terminalZdotdir?: boolean | undefined - terminalCompressProgressBar?: boolean | undefined - rateLimitSeconds?: number | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - experiments?: - | { - powerSteering: boolean - } - | undefined - language?: +type ProviderSettingsEntry = { + id: string + name: string + apiProvider?: | ( - | "ca" - | "de" - | "en" - | "es" - | "fr" - | "hi" - | "it" - | "ja" - | "ko" - | "pl" - | "pt-BR" - | "ru" - | "tr" - | "vi" - | "zh-CN" - | "zh-TW" + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" ) | undefined - telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined - mcpEnabled?: boolean | undefined - enableMcpServerCreation?: boolean | undefined - mode?: string | undefined - modeApiConfigs?: - | { - [x: string]: string - } - | undefined - customModes?: - | { - slug: string - name: string - roleDefinition: string - customInstructions?: string | undefined - groups: ( - | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") - | [ - "read" | "edit" | "browser" | "command" | "mcp" | "modes", - { - fileRegex?: string | undefined - description?: string | undefined - }, - ] - )[] - source?: ("global" | "project") | undefined - backendOnly?: boolean | undefined - }[] - | undefined - customModePrompts?: - | { - [x: string]: - | { - roleDefinition?: string | undefined - customInstructions?: string | undefined - } - | undefined - } - | undefined - customSupportPrompts?: - | { - [x: string]: string | undefined - } - | undefined - enhancementApiConfigId?: string | undefined - historyPreviewCollapsed?: boolean | undefined } type ClineMessage = { @@ -441,7 +483,6 @@ type ClineMessage = { | undefined progressStatus?: | { - id?: string | undefined icon?: string | undefined text?: string | undefined } @@ -517,7 +558,6 @@ type RooCodeEvents = { | undefined progressStatus?: | { - id?: string | undefined icon?: string | undefined text?: string | undefined } @@ -586,6 +626,1113 @@ type RooCodeEvents = { ] } +type IpcMessage = + | { + type: "Ack" + origin: "server" + data: { + clientId: string + pid: number + ppid: number + } + } + | { + type: "TaskCommand" + origin: "client" + clientId: string + data: + | { + commandName: "StartNewTask" + data: { + configuration: { + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + includeMaxTokens?: boolean | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + diffEnabled?: boolean | undefined + fuzzyMatchThreshold?: number | undefined + modelTemperature?: (number | null) | undefined + rateLimitSeconds?: number | undefined + modelMaxTokens?: number | undefined + modelMaxThinkingTokens?: number | undefined + apiModelId?: string | undefined + apiKey?: string | undefined + anthropicBaseUrl?: string | undefined + anthropicUseAuthToken?: boolean | undefined + glamaModelId?: string | undefined + glamaApiKey?: string | undefined + openRouterApiKey?: string | undefined + openRouterModelId?: string | undefined + openRouterBaseUrl?: string | undefined + openRouterSpecificProvider?: string | undefined + openRouterUseMiddleOutTransform?: boolean | undefined + awsAccessKey?: string | undefined + awsSecretKey?: string | undefined + awsSessionToken?: string | undefined + awsRegion?: string | undefined + awsUseCrossRegionInference?: boolean | undefined + awsUsePromptCache?: boolean | undefined + awsProfile?: string | undefined + awsUseProfile?: boolean | undefined + awsCustomArn?: string | undefined + vertexKeyFile?: string | undefined + vertexJsonCredentials?: string | undefined + vertexProjectId?: string | undefined + vertexRegion?: string | undefined + openAiBaseUrl?: string | undefined + openAiApiKey?: string | undefined + openAiLegacyFormat?: boolean | undefined + openAiR1FormatEnabled?: boolean | undefined + openAiModelId?: string | undefined + openAiCustomModelInfo?: + | ({ + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } | null) + | undefined + openAiUseAzure?: boolean | undefined + azureApiVersion?: string | undefined + openAiStreamingEnabled?: boolean | undefined + enableReasoningEffort?: boolean | undefined + openAiHostHeader?: string | undefined + openAiHeaders?: + | { + [x: string]: string + } + | undefined + ollamaModelId?: string | undefined + ollamaBaseUrl?: string | undefined + vsCodeLmModelSelector?: + | { + vendor?: string | undefined + family?: string | undefined + version?: string | undefined + id?: string | undefined + } + | undefined + lmStudioModelId?: string | undefined + lmStudioBaseUrl?: string | undefined + lmStudioDraftModelId?: string | undefined + lmStudioSpeculativeDecodingEnabled?: boolean | undefined + geminiApiKey?: string | undefined + googleGeminiBaseUrl?: string | undefined + openAiNativeApiKey?: string | undefined + openAiNativeBaseUrl?: string | undefined + mistralApiKey?: string | undefined + mistralCodestralUrl?: string | undefined + deepSeekBaseUrl?: string | undefined + deepSeekApiKey?: string | undefined + unboundApiKey?: string | undefined + unboundModelId?: string | undefined + requestyApiKey?: string | undefined + requestyModelId?: string | undefined + fakeAi?: unknown | undefined + pearaiBaseUrl?: string | undefined + pearaiModelId?: string | undefined + pearaiApiKey?: string | undefined + pearaiModelInfo?: + | ({ + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } | null) + | undefined + pearaiAgentModels?: + | { + models: { + [x: string]: { + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } + } + defaultModelId?: string | undefined + } + | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + xaiApiKey?: string | undefined + groqApiKey?: string | undefined + chutesApiKey?: string | undefined + litellmBaseUrl?: string | undefined + litellmApiKey?: string | undefined + litellmModelId?: string | undefined + currentApiConfigName?: string | undefined + listApiConfigMeta?: + | { + id: string + name: string + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + }[] + | undefined + pinnedApiConfigs?: + | { + [x: string]: boolean + } + | undefined + lastShownAnnouncementId?: string | undefined + customInstructions?: string | undefined + taskHistory?: + | { + id: string + number: number + ts: number + task: string + tokensIn: number + tokensOut: number + cacheWrites?: number | undefined + cacheReads?: number | undefined + totalCost: number + size?: number | undefined + workspace?: string | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + }[] + | undefined + autoApprovalEnabled?: boolean | undefined + alwaysAllowReadOnly?: boolean | undefined + alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined + alwaysAllowWrite?: boolean | undefined + alwaysAllowWriteOutsideWorkspace?: boolean | undefined + writeDelayMs?: number | undefined + alwaysAllowBrowser?: boolean | undefined + alwaysApproveResubmit?: boolean | undefined + requestDelaySeconds?: number | undefined + alwaysAllowMcp?: boolean | undefined + alwaysAllowModeSwitch?: boolean | undefined + alwaysAllowSubtasks?: boolean | undefined + alwaysAllowExecute?: boolean | undefined + allowedCommands?: string[] | undefined + browserToolEnabled?: boolean | undefined + browserViewportSize?: string | undefined + screenshotQuality?: number | undefined + remoteBrowserEnabled?: boolean | undefined + remoteBrowserHost?: string | undefined + cachedChromeHostUrl?: string | undefined + enableCheckpoints?: boolean | undefined + ttsEnabled?: boolean | undefined + ttsSpeed?: number | undefined + soundEnabled?: boolean | undefined + soundVolume?: number | undefined + maxOpenTabsContext?: number | undefined + maxWorkspaceFiles?: number | undefined + showRooIgnoredFiles?: boolean | undefined + maxReadFileLine?: number | undefined + terminalOutputLineLimit?: number | undefined + terminalShellIntegrationTimeout?: number | undefined + terminalShellIntegrationDisabled?: boolean | undefined + terminalCommandDelay?: number | undefined + terminalPowershellCounter?: boolean | undefined + terminalZshClearEolMark?: boolean | undefined + terminalZshOhMy?: boolean | undefined + terminalZshP10k?: boolean | undefined + terminalZdotdir?: boolean | undefined + terminalCompressProgressBar?: boolean | undefined + experiments?: + | { + autoCondenseContext: boolean + powerSteering: boolean + } + | undefined + language?: + | ( + | "ca" + | "de" + | "en" + | "es" + | "fr" + | "hi" + | "it" + | "ja" + | "ko" + | "nl" + | "pl" + | "pt-BR" + | "ru" + | "tr" + | "vi" + | "zh-CN" + | "zh-TW" + ) + | undefined + telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined + mcpEnabled?: boolean | undefined + enableMcpServerCreation?: boolean | undefined + mode?: string | undefined + modeApiConfigs?: + | { + [x: string]: string + } + | undefined + customModes?: + | { + slug: string + name: string + roleDefinition: string + whenToUse?: string | undefined + customInstructions?: string | undefined + groups: ( + | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") + | [ + "read" | "edit" | "browser" | "command" | "mcp" | "modes", + { + fileRegex?: string | undefined + description?: string | undefined + }, + ] + )[] + source?: ("global" | "project") | undefined + backendOnly?: boolean | undefined + }[] + | undefined + customModePrompts?: + | { + [x: string]: + | { + roleDefinition?: string | undefined + whenToUse?: string | undefined + customInstructions?: string | undefined + } + | undefined + } + | undefined + customSupportPrompts?: + | { + [x: string]: string | undefined + } + | undefined + enhancementApiConfigId?: string | undefined + historyPreviewCollapsed?: boolean | undefined + } + text: string + images?: string[] | undefined + newTab?: boolean | undefined + } + } + | { + commandName: "CancelTask" + data: string + } + | { + commandName: "CloseTask" + data: string + } + } + | { + type: "TaskEvent" + origin: "server" + relayClientId?: string | undefined + data: + | { + eventName: "message" + payload: [ + { + taskId: string + action: "created" | "updated" + message: { + ts: number + type: "ask" | "say" + ask?: + | ( + | "followup" + | "command" + | "command_output" + | "completion_result" + | "tool" + | "api_req_failed" + | "resume_task" + | "resume_completed_task" + | "mistake_limit_reached" + | "browser_action_launch" + | "use_mcp_server" + ) + | undefined + say?: + | ( + | "error" + | "api_req_started" + | "api_req_finished" + | "api_req_retried" + | "api_req_retry_delayed" + | "api_req_deleted" + | "text" + | "reasoning" + | "completion_result" + | "user_feedback" + | "user_feedback_diff" + | "command_output" + | "shell_integration_warning" + | "browser_action" + | "browser_action_result" + | "mcp_server_request_started" + | "mcp_server_response" + | "subtask_result" + | "checkpoint_saved" + | "rooignore_error" + | "diff_error" + ) + | undefined + text?: string | undefined + images?: string[] | undefined + partial?: boolean | undefined + reasoning?: string | undefined + conversationHistoryIndex?: number | undefined + checkpoint?: + | { + [x: string]: unknown + } + | undefined + progressStatus?: + | { + icon?: string | undefined + text?: string | undefined + } + | undefined + } + }, + ] + } + | { + eventName: "taskCreated" + payload: [string] + } + | { + eventName: "taskStarted" + payload: [string] + } + | { + eventName: "taskModeSwitched" + payload: [string, string] + } + | { + eventName: "taskPaused" + payload: [string] + } + | { + eventName: "taskUnpaused" + payload: [string] + } + | { + eventName: "taskAskResponded" + payload: [string] + } + | { + eventName: "taskAborted" + payload: [string] + } + | { + eventName: "taskSpawned" + payload: [string, string] + } + | { + eventName: "taskCompleted" + payload: [ + string, + { + totalTokensIn: number + totalTokensOut: number + totalCacheWrites?: number | undefined + totalCacheReads?: number | undefined + totalCost: number + contextTokens: number + }, + { + [x: string]: { + attempts: number + failures: number + } + }, + ] + } + | { + eventName: "taskTokenUsageUpdated" + payload: [ + string, + { + totalTokensIn: number + totalTokensOut: number + totalCacheWrites?: number | undefined + totalCacheReads?: number | undefined + totalCost: number + contextTokens: number + }, + ] + } + } + +type TaskCommand = + | { + commandName: "StartNewTask" + data: { + configuration: { + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + includeMaxTokens?: boolean | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + diffEnabled?: boolean | undefined + fuzzyMatchThreshold?: number | undefined + modelTemperature?: (number | null) | undefined + rateLimitSeconds?: number | undefined + modelMaxTokens?: number | undefined + modelMaxThinkingTokens?: number | undefined + apiModelId?: string | undefined + apiKey?: string | undefined + anthropicBaseUrl?: string | undefined + anthropicUseAuthToken?: boolean | undefined + glamaModelId?: string | undefined + glamaApiKey?: string | undefined + openRouterApiKey?: string | undefined + openRouterModelId?: string | undefined + openRouterBaseUrl?: string | undefined + openRouterSpecificProvider?: string | undefined + openRouterUseMiddleOutTransform?: boolean | undefined + awsAccessKey?: string | undefined + awsSecretKey?: string | undefined + awsSessionToken?: string | undefined + awsRegion?: string | undefined + awsUseCrossRegionInference?: boolean | undefined + awsUsePromptCache?: boolean | undefined + awsProfile?: string | undefined + awsUseProfile?: boolean | undefined + awsCustomArn?: string | undefined + vertexKeyFile?: string | undefined + vertexJsonCredentials?: string | undefined + vertexProjectId?: string | undefined + vertexRegion?: string | undefined + openAiBaseUrl?: string | undefined + openAiApiKey?: string | undefined + openAiLegacyFormat?: boolean | undefined + openAiR1FormatEnabled?: boolean | undefined + openAiModelId?: string | undefined + openAiCustomModelInfo?: + | ({ + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } | null) + | undefined + openAiUseAzure?: boolean | undefined + azureApiVersion?: string | undefined + openAiStreamingEnabled?: boolean | undefined + enableReasoningEffort?: boolean | undefined + openAiHostHeader?: string | undefined + openAiHeaders?: + | { + [x: string]: string + } + | undefined + ollamaModelId?: string | undefined + ollamaBaseUrl?: string | undefined + vsCodeLmModelSelector?: + | { + vendor?: string | undefined + family?: string | undefined + version?: string | undefined + id?: string | undefined + } + | undefined + lmStudioModelId?: string | undefined + lmStudioBaseUrl?: string | undefined + lmStudioDraftModelId?: string | undefined + lmStudioSpeculativeDecodingEnabled?: boolean | undefined + geminiApiKey?: string | undefined + googleGeminiBaseUrl?: string | undefined + openAiNativeApiKey?: string | undefined + openAiNativeBaseUrl?: string | undefined + mistralApiKey?: string | undefined + mistralCodestralUrl?: string | undefined + deepSeekBaseUrl?: string | undefined + deepSeekApiKey?: string | undefined + unboundApiKey?: string | undefined + unboundModelId?: string | undefined + requestyApiKey?: string | undefined + requestyModelId?: string | undefined + fakeAi?: unknown | undefined + pearaiBaseUrl?: string | undefined + pearaiModelId?: string | undefined + pearaiApiKey?: string | undefined + pearaiModelInfo?: + | ({ + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } | null) + | undefined + pearaiAgentModels?: + | { + models: { + [x: string]: { + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } + } + defaultModelId?: string | undefined + } + | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + xaiApiKey?: string | undefined + groqApiKey?: string | undefined + chutesApiKey?: string | undefined + litellmBaseUrl?: string | undefined + litellmApiKey?: string | undefined + litellmModelId?: string | undefined + currentApiConfigName?: string | undefined + listApiConfigMeta?: + | { + id: string + name: string + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + }[] + | undefined + pinnedApiConfigs?: + | { + [x: string]: boolean + } + | undefined + lastShownAnnouncementId?: string | undefined + customInstructions?: string | undefined + taskHistory?: + | { + id: string + number: number + ts: number + task: string + tokensIn: number + tokensOut: number + cacheWrites?: number | undefined + cacheReads?: number | undefined + totalCost: number + size?: number | undefined + workspace?: string | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + }[] + | undefined + autoApprovalEnabled?: boolean | undefined + alwaysAllowReadOnly?: boolean | undefined + alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined + alwaysAllowWrite?: boolean | undefined + alwaysAllowWriteOutsideWorkspace?: boolean | undefined + writeDelayMs?: number | undefined + alwaysAllowBrowser?: boolean | undefined + alwaysApproveResubmit?: boolean | undefined + requestDelaySeconds?: number | undefined + alwaysAllowMcp?: boolean | undefined + alwaysAllowModeSwitch?: boolean | undefined + alwaysAllowSubtasks?: boolean | undefined + alwaysAllowExecute?: boolean | undefined + allowedCommands?: string[] | undefined + browserToolEnabled?: boolean | undefined + browserViewportSize?: string | undefined + screenshotQuality?: number | undefined + remoteBrowserEnabled?: boolean | undefined + remoteBrowserHost?: string | undefined + cachedChromeHostUrl?: string | undefined + enableCheckpoints?: boolean | undefined + ttsEnabled?: boolean | undefined + ttsSpeed?: number | undefined + soundEnabled?: boolean | undefined + soundVolume?: number | undefined + maxOpenTabsContext?: number | undefined + maxWorkspaceFiles?: number | undefined + showRooIgnoredFiles?: boolean | undefined + maxReadFileLine?: number | undefined + terminalOutputLineLimit?: number | undefined + terminalShellIntegrationTimeout?: number | undefined + terminalShellIntegrationDisabled?: boolean | undefined + terminalCommandDelay?: number | undefined + terminalPowershellCounter?: boolean | undefined + terminalZshClearEolMark?: boolean | undefined + terminalZshOhMy?: boolean | undefined + terminalZshP10k?: boolean | undefined + terminalZdotdir?: boolean | undefined + terminalCompressProgressBar?: boolean | undefined + experiments?: + | { + autoCondenseContext: boolean + powerSteering: boolean + } + | undefined + language?: + | ( + | "ca" + | "de" + | "en" + | "es" + | "fr" + | "hi" + | "it" + | "ja" + | "ko" + | "nl" + | "pl" + | "pt-BR" + | "ru" + | "tr" + | "vi" + | "zh-CN" + | "zh-TW" + ) + | undefined + telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined + mcpEnabled?: boolean | undefined + enableMcpServerCreation?: boolean | undefined + mode?: string | undefined + modeApiConfigs?: + | { + [x: string]: string + } + | undefined + customModes?: + | { + slug: string + name: string + roleDefinition: string + whenToUse?: string | undefined + customInstructions?: string | undefined + groups: ( + | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") + | [ + "read" | "edit" | "browser" | "command" | "mcp" | "modes", + { + fileRegex?: string | undefined + description?: string | undefined + }, + ] + )[] + source?: ("global" | "project") | undefined + backendOnly?: boolean | undefined + }[] + | undefined + customModePrompts?: + | { + [x: string]: + | { + roleDefinition?: string | undefined + whenToUse?: string | undefined + customInstructions?: string | undefined + } + | undefined + } + | undefined + customSupportPrompts?: + | { + [x: string]: string | undefined + } + | undefined + enhancementApiConfigId?: string | undefined + historyPreviewCollapsed?: boolean | undefined + } + text: string + images?: string[] | undefined + newTab?: boolean | undefined + } + } + | { + commandName: "CancelTask" + data: string + } + | { + commandName: "CloseTask" + data: string + } + +type TaskEvent = + | { + eventName: "message" + payload: [ + { + taskId: string + action: "created" | "updated" + message: { + ts: number + type: "ask" | "say" + ask?: + | ( + | "followup" + | "command" + | "command_output" + | "completion_result" + | "tool" + | "api_req_failed" + | "resume_task" + | "resume_completed_task" + | "mistake_limit_reached" + | "browser_action_launch" + | "use_mcp_server" + ) + | undefined + say?: + | ( + | "error" + | "api_req_started" + | "api_req_finished" + | "api_req_retried" + | "api_req_retry_delayed" + | "api_req_deleted" + | "text" + | "reasoning" + | "completion_result" + | "user_feedback" + | "user_feedback_diff" + | "command_output" + | "shell_integration_warning" + | "browser_action" + | "browser_action_result" + | "mcp_server_request_started" + | "mcp_server_response" + | "subtask_result" + | "checkpoint_saved" + | "rooignore_error" + | "diff_error" + ) + | undefined + text?: string | undefined + images?: string[] | undefined + partial?: boolean | undefined + reasoning?: string | undefined + conversationHistoryIndex?: number | undefined + checkpoint?: + | { + [x: string]: unknown + } + | undefined + progressStatus?: + | { + icon?: string | undefined + text?: string | undefined + } + | undefined + } + }, + ] + } + | { + eventName: "taskCreated" + payload: [string] + } + | { + eventName: "taskStarted" + payload: [string] + } + | { + eventName: "taskModeSwitched" + payload: [string, string] + } + | { + eventName: "taskPaused" + payload: [string] + } + | { + eventName: "taskUnpaused" + payload: [string] + } + | { + eventName: "taskAskResponded" + payload: [string] + } + | { + eventName: "taskAborted" + payload: [string] + } + | { + eventName: "taskSpawned" + payload: [string, string] + } + | { + eventName: "taskCompleted" + payload: [ + string, + { + totalTokensIn: number + totalTokensOut: number + totalCacheWrites?: number | undefined + totalCacheReads?: number | undefined + totalCost: number + contextTokens: number + }, + { + [x: string]: { + attempts: number + failures: number + } + }, + ] + } + | { + eventName: "taskTokenUsageUpdated" + payload: [ + string, + { + totalTokensIn: number + totalTokensOut: number + totalCacheWrites?: number | undefined + totalCacheReads?: number | undefined + totalCost: number + contextTokens: number + }, + ] + } + /** * RooCodeEvent */ @@ -603,7 +1750,24 @@ declare enum RooCodeEventName { TaskTokenUsageUpdated = "taskTokenUsageUpdated", TaskToolFailed = "taskToolFailed", } +/** + * IpcMessage + */ +declare enum IpcMessageType { + Connect = "Connect", + Disconnect = "Disconnect", + Ack = "Ack", + TaskCommand = "TaskCommand", + TaskEvent = "TaskEvent", +} +declare enum IpcOrigin { + Client = "client", + Server = "server", +} +/** + * RooCodeAPI + */ type RooCodeSettings = GlobalSettings & ProviderSettings interface RooCodeAPI extends EventEmitter { /** @@ -662,6 +1826,10 @@ interface RooCodeAPI extends EventEmitter { * Simulates pressing the secondary button in the chat interface. */ pressSecondaryButton(): Promise + /** + * Returns true if the API is ready to use. + */ + isReady(): boolean /** * Returns the current configuration. * @returns The current configuration. @@ -672,28 +1840,43 @@ interface RooCodeAPI extends EventEmitter { * @param values An object containing key-value pairs to set. */ setConfiguration(values: RooCodeSettings): Promise - /** - * Creates a new API configuration profile - * @param name The name of the profile - * @returns The ID of the created profile - */ - createProfile(name: string): Promise /** * Returns a list of all configured profile names * @returns Array of profile names */ getProfiles(): string[] /** - * Changes the active API configuration profile - * @param name The name of the profile to activate + * Returns the profile entry for a given name + * @param name The name of the profile + * @returns The profile entry, or undefined if the profile does not exist + */ + getProfileEntry(name: string): ProviderSettingsEntry | undefined + /** + * Creates a new API configuration profile + * @param name The name of the profile + * @param profile The profile to create; defaults to an empty object + * @param activate Whether to activate the profile after creation; defaults to true + * @returns The ID of the created profile + * @throws Error if the profile already exists + */ + createProfile(name: string, profile?: ProviderSettings, activate?: boolean): Promise + /** + * Updates an existing API configuration profile + * @param name The name of the profile + * @param profile The profile to update + * @param activate Whether to activate the profile after update; defaults to true + * @returns The ID of the updated profile * @throws Error if the profile does not exist */ - setActiveProfile(name: string): Promise + updateProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise /** - * Returns the name of the currently active profile - * @returns The profile name, or undefined if no profile is active + * Creates a new API configuration profile or updates an existing one + * @param name The name of the profile + * @param profile The profile to create or update; defaults to an empty object + * @param activate Whether to activate the profile after upsert; defaults to true + * @returns The ID of the upserted profile */ - getActiveProfile(): string | undefined + upsertProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise /** * Deletes a profile by name * @param name The name of the profile to delete @@ -701,18 +1884,49 @@ interface RooCodeAPI extends EventEmitter { */ deleteProfile(name: string): Promise /** - * Returns true if the API is ready to use. + * Returns the name of the currently active profile + * @returns The profile name, or undefined if no profile is active */ - isReady(): boolean + getActiveProfile(): string | undefined + /** + * Changes the active API configuration profile + * @param name The name of the profile to activate + * @throws Error if the profile does not exist + */ + setActiveProfile(name: string): Promise +} +/** + * RooCodeIpcServer + */ +type IpcServerEvents = { + [IpcMessageType.Connect]: [clientId: string] + [IpcMessageType.Disconnect]: [clientId: string] + [IpcMessageType.TaskCommand]: [clientId: string, data: TaskCommand] + [IpcMessageType.TaskEvent]: [relayClientId: string | undefined, data: TaskEvent] +} +interface RooCodeIpcServer extends EventEmitter { + listen(): void + broadcast(message: IpcMessage): void + send(client: string | Socket, message: IpcMessage): void + get socketPath(): string + get isListening(): boolean } export { type ClineMessage, type GlobalSettings, + type IpcMessage, + IpcMessageType, + IpcOrigin, + type IpcServerEvents, type ProviderSettings, + type ProviderSettingsEntry, type RooCodeAPI, RooCodeEventName, type RooCodeEvents, + type RooCodeIpcServer, type RooCodeSettings, + type TaskCommand, + type TaskEvent, type TokenUsage, } diff --git a/src/exports/types.ts b/src/exports/types.ts index e964fdbe46c..9d8aad755ec 100644 --- a/src/exports/types.ts +++ b/src/exports/types.ts @@ -1,6 +1,190 @@ // This file is automatically generated by running `npm run generate-types` // Do not edit it directly. +type GlobalSettings = { + currentApiConfigName?: string | undefined + listApiConfigMeta?: + | { + id: string + name: string + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + }[] + | undefined + pinnedApiConfigs?: + | { + [x: string]: boolean + } + | undefined + lastShownAnnouncementId?: string | undefined + customInstructions?: string | undefined + taskHistory?: + | { + id: string + number: number + ts: number + task: string + tokensIn: number + tokensOut: number + cacheWrites?: number | undefined + cacheReads?: number | undefined + totalCost: number + size?: number | undefined + workspace?: string | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + }[] + | undefined + autoApprovalEnabled?: boolean | undefined + alwaysAllowReadOnly?: boolean | undefined + alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined + alwaysAllowWrite?: boolean | undefined + alwaysAllowWriteOutsideWorkspace?: boolean | undefined + writeDelayMs?: number | undefined + alwaysAllowBrowser?: boolean | undefined + alwaysApproveResubmit?: boolean | undefined + requestDelaySeconds?: number | undefined + alwaysAllowMcp?: boolean | undefined + alwaysAllowModeSwitch?: boolean | undefined + alwaysAllowSubtasks?: boolean | undefined + alwaysAllowExecute?: boolean | undefined + allowedCommands?: string[] | undefined + browserToolEnabled?: boolean | undefined + browserViewportSize?: string | undefined + screenshotQuality?: number | undefined + remoteBrowserEnabled?: boolean | undefined + remoteBrowserHost?: string | undefined + cachedChromeHostUrl?: string | undefined + enableCheckpoints?: boolean | undefined + ttsEnabled?: boolean | undefined + ttsSpeed?: number | undefined + soundEnabled?: boolean | undefined + soundVolume?: number | undefined + maxOpenTabsContext?: number | undefined + maxWorkspaceFiles?: number | undefined + showRooIgnoredFiles?: boolean | undefined + maxReadFileLine?: number | undefined + terminalOutputLineLimit?: number | undefined + terminalShellIntegrationTimeout?: number | undefined + terminalShellIntegrationDisabled?: boolean | undefined + terminalCommandDelay?: number | undefined + terminalPowershellCounter?: boolean | undefined + terminalZshClearEolMark?: boolean | undefined + terminalZshOhMy?: boolean | undefined + terminalZshP10k?: boolean | undefined + terminalZdotdir?: boolean | undefined + terminalCompressProgressBar?: boolean | undefined + rateLimitSeconds?: number | undefined + diffEnabled?: boolean | undefined + fuzzyMatchThreshold?: number | undefined + experiments?: + | { + autoCondenseContext: boolean + powerSteering: boolean + } + | undefined + language?: + | ( + | "ca" + | "de" + | "en" + | "es" + | "fr" + | "hi" + | "it" + | "ja" + | "ko" + | "nl" + | "pl" + | "pt-BR" + | "ru" + | "tr" + | "vi" + | "zh-CN" + | "zh-TW" + ) + | undefined + telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined + mcpEnabled?: boolean | undefined + enableMcpServerCreation?: boolean | undefined + mode?: string | undefined + modeApiConfigs?: + | { + [x: string]: string + } + | undefined + customModes?: + | { + slug: string + name: string + roleDefinition: string + whenToUse?: string | undefined + customInstructions?: string | undefined + groups: ( + | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") + | [ + "read" | "edit" | "browser" | "command" | "mcp" | "modes", + { + fileRegex?: string | undefined + description?: string | undefined + }, + ] + )[] + source?: ("global" | "project") | undefined + backendOnly?: boolean | undefined + }[] + | undefined + customModePrompts?: + | { + [x: string]: + | { + roleDefinition?: string | undefined + whenToUse?: string | undefined + customInstructions?: string | undefined + } + | undefined + } + | undefined + customSupportPrompts?: + | { + [x: string]: string | undefined + } + | undefined + enhancementApiConfigId?: string | undefined + historyPreviewCollapsed?: boolean | undefined +} + +export type { GlobalSettings } + type ProviderSettings = { apiProvider?: | ( @@ -23,8 +207,19 @@ type ProviderSettings = { | "fake-ai" | "pearai" | "xai" + | "groq" + | "chutes" + | "litellm" ) | undefined + includeMaxTokens?: boolean | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + diffEnabled?: boolean | undefined + fuzzyMatchThreshold?: number | undefined + modelTemperature?: (number | null) | undefined + rateLimitSeconds?: number | undefined + modelMaxTokens?: number | undefined + modelMaxThinkingTokens?: number | undefined apiModelId?: string | undefined apiKey?: string | undefined anthropicBaseUrl?: string | undefined @@ -42,7 +237,6 @@ type ProviderSettings = { awsRegion?: string | undefined awsUseCrossRegionInference?: boolean | undefined awsUsePromptCache?: boolean | undefined - awspromptCacheId?: string | undefined awsProfile?: string | undefined awsUseProfile?: boolean | undefined awsCustomArn?: string | undefined @@ -63,7 +257,6 @@ type ProviderSettings = { supportsImages?: boolean | undefined supportsComputerUse?: boolean | undefined supportsPromptCache: boolean - isPromptCacheOptional?: boolean | undefined inputPrice?: number | undefined outputPrice?: number | undefined cacheWritesPrice?: number | undefined @@ -122,16 +315,6 @@ type ProviderSettings = { unboundModelId?: string | undefined requestyApiKey?: string | undefined requestyModelId?: string | undefined - xaiApiKey?: string | undefined - modelMaxTokens?: number | undefined - modelMaxThinkingTokens?: number | undefined - includeMaxTokens?: boolean | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - promptCachingEnabled?: boolean | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - modelTemperature?: (number | null) | undefined - rateLimitSeconds?: number | undefined fakeAi?: unknown | undefined pearaiBaseUrl?: string | undefined pearaiModelId?: string | undefined @@ -144,7 +327,6 @@ type ProviderSettings = { supportsImages?: boolean | undefined supportsComputerUse?: boolean | undefined supportsPromptCache: boolean - isPromptCacheOptional?: boolean | undefined inputPrice?: number | undefined outputPrice?: number | undefined cacheWritesPrice?: number | undefined @@ -177,7 +359,6 @@ type ProviderSettings = { supportsImages?: boolean | undefined supportsComputerUse?: boolean | undefined supportsPromptCache: boolean - isPromptCacheOptional?: boolean | undefined inputPrice?: number | undefined outputPrice?: number | undefined cacheWritesPrice?: number | undefined @@ -210,186 +391,48 @@ type ProviderSettings = { newProjectPath?: string | undefined } | undefined + xaiApiKey?: string | undefined + groqApiKey?: string | undefined + chutesApiKey?: string | undefined + litellmBaseUrl?: string | undefined + litellmApiKey?: string | undefined + litellmModelId?: string | undefined } export type { ProviderSettings } -type GlobalSettings = { - currentApiConfigName?: string | undefined - listApiConfigMeta?: - | { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "pearai" - | "xai" - ) - | undefined - }[] - | undefined - pinnedApiConfigs?: - | { - [x: string]: boolean - } - | undefined - lastShownAnnouncementId?: string | undefined - customInstructions?: string | undefined - taskHistory?: - | { - id: string - number: number - ts: number - task: string - tokensIn: number - tokensOut: number - cacheWrites?: number | undefined - cacheReads?: number | undefined - totalCost: number - size?: number | undefined - workspace?: string | undefined - creatorModeConfig?: - | { - creatorMode?: boolean | undefined - newProjectType?: string | undefined - newProjectPath?: string | undefined - } - | undefined - }[] - | undefined - autoApprovalEnabled?: boolean | undefined - alwaysAllowReadOnly?: boolean | undefined - alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined - alwaysAllowWrite?: boolean | undefined - alwaysAllowWriteOutsideWorkspace?: boolean | undefined - writeDelayMs?: number | undefined - alwaysAllowBrowser?: boolean | undefined - alwaysApproveResubmit?: boolean | undefined - requestDelaySeconds?: number | undefined - alwaysAllowMcp?: boolean | undefined - alwaysAllowModeSwitch?: boolean | undefined - alwaysAllowSubtasks?: boolean | undefined - alwaysAllowExecute?: boolean | undefined - allowedCommands?: string[] | undefined - browserToolEnabled?: boolean | undefined - browserViewportSize?: string | undefined - screenshotQuality?: number | undefined - remoteBrowserEnabled?: boolean | undefined - remoteBrowserHost?: string | undefined - cachedChromeHostUrl?: string | undefined - enableCheckpoints?: boolean | undefined - ttsEnabled?: boolean | undefined - ttsSpeed?: number | undefined - soundEnabled?: boolean | undefined - soundVolume?: number | undefined - maxOpenTabsContext?: number | undefined - maxWorkspaceFiles?: number | undefined - showRooIgnoredFiles?: boolean | undefined - maxReadFileLine?: number | undefined - terminalOutputLineLimit?: number | undefined - terminalShellIntegrationTimeout?: number | undefined - terminalShellIntegrationDisabled?: boolean | undefined - terminalCommandDelay?: number | undefined - terminalPowershellCounter?: boolean | undefined - terminalZshClearEolMark?: boolean | undefined - terminalZshOhMy?: boolean | undefined - terminalZshP10k?: boolean | undefined - terminalZdotdir?: boolean | undefined - terminalCompressProgressBar?: boolean | undefined - rateLimitSeconds?: number | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - experiments?: - | { - powerSteering: boolean - } - | undefined - language?: +type ProviderSettingsEntry = { + id: string + name: string + apiProvider?: | ( - | "ca" - | "de" - | "en" - | "es" - | "fr" - | "hi" - | "it" - | "ja" - | "ko" - | "pl" - | "pt-BR" - | "ru" - | "tr" - | "vi" - | "zh-CN" - | "zh-TW" + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" ) | undefined - telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined - mcpEnabled?: boolean | undefined - enableMcpServerCreation?: boolean | undefined - mode?: string | undefined - modeApiConfigs?: - | { - [x: string]: string - } - | undefined - customModes?: - | { - slug: string - name: string - roleDefinition: string - customInstructions?: string | undefined - groups: ( - | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") - | [ - "read" | "edit" | "browser" | "command" | "mcp" | "modes", - { - fileRegex?: string | undefined - description?: string | undefined - }, - ] - )[] - source?: ("global" | "project") | undefined - backendOnly?: boolean | undefined - }[] - | undefined - customModePrompts?: - | { - [x: string]: - | { - roleDefinition?: string | undefined - customInstructions?: string | undefined - } - | undefined - } - | undefined - customSupportPrompts?: - | { - [x: string]: string | undefined - } - | undefined - enhancementApiConfigId?: string | undefined - historyPreviewCollapsed?: boolean | undefined } -export type { GlobalSettings } +export type { ProviderSettingsEntry } type ClineMessage = { ts: number @@ -446,7 +489,6 @@ type ClineMessage = { | undefined progressStatus?: | { - id?: string | undefined icon?: string | undefined text?: string | undefined } @@ -526,7 +568,6 @@ type RooCodeEvents = { | undefined progressStatus?: | { - id?: string | undefined icon?: string | undefined text?: string | undefined } @@ -596,3 +637,1116 @@ type RooCodeEvents = { } export type { RooCodeEvents } + +type IpcMessage = + | { + type: "Ack" + origin: "server" + data: { + clientId: string + pid: number + ppid: number + } + } + | { + type: "TaskCommand" + origin: "client" + clientId: string + data: + | { + commandName: "StartNewTask" + data: { + configuration: { + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + includeMaxTokens?: boolean | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + diffEnabled?: boolean | undefined + fuzzyMatchThreshold?: number | undefined + modelTemperature?: (number | null) | undefined + rateLimitSeconds?: number | undefined + modelMaxTokens?: number | undefined + modelMaxThinkingTokens?: number | undefined + apiModelId?: string | undefined + apiKey?: string | undefined + anthropicBaseUrl?: string | undefined + anthropicUseAuthToken?: boolean | undefined + glamaModelId?: string | undefined + glamaApiKey?: string | undefined + openRouterApiKey?: string | undefined + openRouterModelId?: string | undefined + openRouterBaseUrl?: string | undefined + openRouterSpecificProvider?: string | undefined + openRouterUseMiddleOutTransform?: boolean | undefined + awsAccessKey?: string | undefined + awsSecretKey?: string | undefined + awsSessionToken?: string | undefined + awsRegion?: string | undefined + awsUseCrossRegionInference?: boolean | undefined + awsUsePromptCache?: boolean | undefined + awsProfile?: string | undefined + awsUseProfile?: boolean | undefined + awsCustomArn?: string | undefined + vertexKeyFile?: string | undefined + vertexJsonCredentials?: string | undefined + vertexProjectId?: string | undefined + vertexRegion?: string | undefined + openAiBaseUrl?: string | undefined + openAiApiKey?: string | undefined + openAiLegacyFormat?: boolean | undefined + openAiR1FormatEnabled?: boolean | undefined + openAiModelId?: string | undefined + openAiCustomModelInfo?: + | ({ + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } | null) + | undefined + openAiUseAzure?: boolean | undefined + azureApiVersion?: string | undefined + openAiStreamingEnabled?: boolean | undefined + enableReasoningEffort?: boolean | undefined + openAiHostHeader?: string | undefined + openAiHeaders?: + | { + [x: string]: string + } + | undefined + ollamaModelId?: string | undefined + ollamaBaseUrl?: string | undefined + vsCodeLmModelSelector?: + | { + vendor?: string | undefined + family?: string | undefined + version?: string | undefined + id?: string | undefined + } + | undefined + lmStudioModelId?: string | undefined + lmStudioBaseUrl?: string | undefined + lmStudioDraftModelId?: string | undefined + lmStudioSpeculativeDecodingEnabled?: boolean | undefined + geminiApiKey?: string | undefined + googleGeminiBaseUrl?: string | undefined + openAiNativeApiKey?: string | undefined + openAiNativeBaseUrl?: string | undefined + mistralApiKey?: string | undefined + mistralCodestralUrl?: string | undefined + deepSeekBaseUrl?: string | undefined + deepSeekApiKey?: string | undefined + unboundApiKey?: string | undefined + unboundModelId?: string | undefined + requestyApiKey?: string | undefined + requestyModelId?: string | undefined + fakeAi?: unknown | undefined + pearaiBaseUrl?: string | undefined + pearaiModelId?: string | undefined + pearaiApiKey?: string | undefined + pearaiModelInfo?: + | ({ + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } | null) + | undefined + pearaiAgentModels?: + | { + models: { + [x: string]: { + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } + } + defaultModelId?: string | undefined + } + | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + xaiApiKey?: string | undefined + groqApiKey?: string | undefined + chutesApiKey?: string | undefined + litellmBaseUrl?: string | undefined + litellmApiKey?: string | undefined + litellmModelId?: string | undefined + currentApiConfigName?: string | undefined + listApiConfigMeta?: + | { + id: string + name: string + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + }[] + | undefined + pinnedApiConfigs?: + | { + [x: string]: boolean + } + | undefined + lastShownAnnouncementId?: string | undefined + customInstructions?: string | undefined + taskHistory?: + | { + id: string + number: number + ts: number + task: string + tokensIn: number + tokensOut: number + cacheWrites?: number | undefined + cacheReads?: number | undefined + totalCost: number + size?: number | undefined + workspace?: string | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + }[] + | undefined + autoApprovalEnabled?: boolean | undefined + alwaysAllowReadOnly?: boolean | undefined + alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined + alwaysAllowWrite?: boolean | undefined + alwaysAllowWriteOutsideWorkspace?: boolean | undefined + writeDelayMs?: number | undefined + alwaysAllowBrowser?: boolean | undefined + alwaysApproveResubmit?: boolean | undefined + requestDelaySeconds?: number | undefined + alwaysAllowMcp?: boolean | undefined + alwaysAllowModeSwitch?: boolean | undefined + alwaysAllowSubtasks?: boolean | undefined + alwaysAllowExecute?: boolean | undefined + allowedCommands?: string[] | undefined + browserToolEnabled?: boolean | undefined + browserViewportSize?: string | undefined + screenshotQuality?: number | undefined + remoteBrowserEnabled?: boolean | undefined + remoteBrowserHost?: string | undefined + cachedChromeHostUrl?: string | undefined + enableCheckpoints?: boolean | undefined + ttsEnabled?: boolean | undefined + ttsSpeed?: number | undefined + soundEnabled?: boolean | undefined + soundVolume?: number | undefined + maxOpenTabsContext?: number | undefined + maxWorkspaceFiles?: number | undefined + showRooIgnoredFiles?: boolean | undefined + maxReadFileLine?: number | undefined + terminalOutputLineLimit?: number | undefined + terminalShellIntegrationTimeout?: number | undefined + terminalShellIntegrationDisabled?: boolean | undefined + terminalCommandDelay?: number | undefined + terminalPowershellCounter?: boolean | undefined + terminalZshClearEolMark?: boolean | undefined + terminalZshOhMy?: boolean | undefined + terminalZshP10k?: boolean | undefined + terminalZdotdir?: boolean | undefined + terminalCompressProgressBar?: boolean | undefined + experiments?: + | { + autoCondenseContext: boolean + powerSteering: boolean + } + | undefined + language?: + | ( + | "ca" + | "de" + | "en" + | "es" + | "fr" + | "hi" + | "it" + | "ja" + | "ko" + | "nl" + | "pl" + | "pt-BR" + | "ru" + | "tr" + | "vi" + | "zh-CN" + | "zh-TW" + ) + | undefined + telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined + mcpEnabled?: boolean | undefined + enableMcpServerCreation?: boolean | undefined + mode?: string | undefined + modeApiConfigs?: + | { + [x: string]: string + } + | undefined + customModes?: + | { + slug: string + name: string + roleDefinition: string + whenToUse?: string | undefined + customInstructions?: string | undefined + groups: ( + | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") + | [ + "read" | "edit" | "browser" | "command" | "mcp" | "modes", + { + fileRegex?: string | undefined + description?: string | undefined + }, + ] + )[] + source?: ("global" | "project") | undefined + backendOnly?: boolean | undefined + }[] + | undefined + customModePrompts?: + | { + [x: string]: + | { + roleDefinition?: string | undefined + whenToUse?: string | undefined + customInstructions?: string | undefined + } + | undefined + } + | undefined + customSupportPrompts?: + | { + [x: string]: string | undefined + } + | undefined + enhancementApiConfigId?: string | undefined + historyPreviewCollapsed?: boolean | undefined + } + text: string + images?: string[] | undefined + newTab?: boolean | undefined + } + } + | { + commandName: "CancelTask" + data: string + } + | { + commandName: "CloseTask" + data: string + } + } + | { + type: "TaskEvent" + origin: "server" + relayClientId?: string | undefined + data: + | { + eventName: "message" + payload: [ + { + taskId: string + action: "created" | "updated" + message: { + ts: number + type: "ask" | "say" + ask?: + | ( + | "followup" + | "command" + | "command_output" + | "completion_result" + | "tool" + | "api_req_failed" + | "resume_task" + | "resume_completed_task" + | "mistake_limit_reached" + | "browser_action_launch" + | "use_mcp_server" + ) + | undefined + say?: + | ( + | "error" + | "api_req_started" + | "api_req_finished" + | "api_req_retried" + | "api_req_retry_delayed" + | "api_req_deleted" + | "text" + | "reasoning" + | "completion_result" + | "user_feedback" + | "user_feedback_diff" + | "command_output" + | "shell_integration_warning" + | "browser_action" + | "browser_action_result" + | "mcp_server_request_started" + | "mcp_server_response" + | "subtask_result" + | "checkpoint_saved" + | "rooignore_error" + | "diff_error" + ) + | undefined + text?: string | undefined + images?: string[] | undefined + partial?: boolean | undefined + reasoning?: string | undefined + conversationHistoryIndex?: number | undefined + checkpoint?: + | { + [x: string]: unknown + } + | undefined + progressStatus?: + | { + icon?: string | undefined + text?: string | undefined + } + | undefined + } + }, + ] + } + | { + eventName: "taskCreated" + payload: [string] + } + | { + eventName: "taskStarted" + payload: [string] + } + | { + eventName: "taskModeSwitched" + payload: [string, string] + } + | { + eventName: "taskPaused" + payload: [string] + } + | { + eventName: "taskUnpaused" + payload: [string] + } + | { + eventName: "taskAskResponded" + payload: [string] + } + | { + eventName: "taskAborted" + payload: [string] + } + | { + eventName: "taskSpawned" + payload: [string, string] + } + | { + eventName: "taskCompleted" + payload: [ + string, + { + totalTokensIn: number + totalTokensOut: number + totalCacheWrites?: number | undefined + totalCacheReads?: number | undefined + totalCost: number + contextTokens: number + }, + { + [x: string]: { + attempts: number + failures: number + } + }, + ] + } + | { + eventName: "taskTokenUsageUpdated" + payload: [ + string, + { + totalTokensIn: number + totalTokensOut: number + totalCacheWrites?: number | undefined + totalCacheReads?: number | undefined + totalCost: number + contextTokens: number + }, + ] + } + } + +export type { IpcMessage } + +type TaskCommand = + | { + commandName: "StartNewTask" + data: { + configuration: { + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + includeMaxTokens?: boolean | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + diffEnabled?: boolean | undefined + fuzzyMatchThreshold?: number | undefined + modelTemperature?: (number | null) | undefined + rateLimitSeconds?: number | undefined + modelMaxTokens?: number | undefined + modelMaxThinkingTokens?: number | undefined + apiModelId?: string | undefined + apiKey?: string | undefined + anthropicBaseUrl?: string | undefined + anthropicUseAuthToken?: boolean | undefined + glamaModelId?: string | undefined + glamaApiKey?: string | undefined + openRouterApiKey?: string | undefined + openRouterModelId?: string | undefined + openRouterBaseUrl?: string | undefined + openRouterSpecificProvider?: string | undefined + openRouterUseMiddleOutTransform?: boolean | undefined + awsAccessKey?: string | undefined + awsSecretKey?: string | undefined + awsSessionToken?: string | undefined + awsRegion?: string | undefined + awsUseCrossRegionInference?: boolean | undefined + awsUsePromptCache?: boolean | undefined + awsProfile?: string | undefined + awsUseProfile?: boolean | undefined + awsCustomArn?: string | undefined + vertexKeyFile?: string | undefined + vertexJsonCredentials?: string | undefined + vertexProjectId?: string | undefined + vertexRegion?: string | undefined + openAiBaseUrl?: string | undefined + openAiApiKey?: string | undefined + openAiLegacyFormat?: boolean | undefined + openAiR1FormatEnabled?: boolean | undefined + openAiModelId?: string | undefined + openAiCustomModelInfo?: + | ({ + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } | null) + | undefined + openAiUseAzure?: boolean | undefined + azureApiVersion?: string | undefined + openAiStreamingEnabled?: boolean | undefined + enableReasoningEffort?: boolean | undefined + openAiHostHeader?: string | undefined + openAiHeaders?: + | { + [x: string]: string + } + | undefined + ollamaModelId?: string | undefined + ollamaBaseUrl?: string | undefined + vsCodeLmModelSelector?: + | { + vendor?: string | undefined + family?: string | undefined + version?: string | undefined + id?: string | undefined + } + | undefined + lmStudioModelId?: string | undefined + lmStudioBaseUrl?: string | undefined + lmStudioDraftModelId?: string | undefined + lmStudioSpeculativeDecodingEnabled?: boolean | undefined + geminiApiKey?: string | undefined + googleGeminiBaseUrl?: string | undefined + openAiNativeApiKey?: string | undefined + openAiNativeBaseUrl?: string | undefined + mistralApiKey?: string | undefined + mistralCodestralUrl?: string | undefined + deepSeekBaseUrl?: string | undefined + deepSeekApiKey?: string | undefined + unboundApiKey?: string | undefined + unboundModelId?: string | undefined + requestyApiKey?: string | undefined + requestyModelId?: string | undefined + fakeAi?: unknown | undefined + pearaiBaseUrl?: string | undefined + pearaiModelId?: string | undefined + pearaiApiKey?: string | undefined + pearaiModelInfo?: + | ({ + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } | null) + | undefined + pearaiAgentModels?: + | { + models: { + [x: string]: { + maxTokens?: (number | null) | undefined + maxThinkingTokens?: (number | null) | undefined + contextWindow: number + supportsImages?: boolean | undefined + supportsComputerUse?: boolean | undefined + supportsPromptCache: boolean + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + description?: string | undefined + reasoningEffort?: ("low" | "medium" | "high") | undefined + thinking?: boolean | undefined + minTokensPerCachePoint?: number | undefined + maxCachePoints?: number | undefined + cachableFields?: string[] | undefined + underlyingModel?: string | undefined + tiers?: + | { + contextWindow: number + inputPrice?: number | undefined + outputPrice?: number | undefined + cacheWritesPrice?: number | undefined + cacheReadsPrice?: number | undefined + }[] + | undefined + } + } + defaultModelId?: string | undefined + } + | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + xaiApiKey?: string | undefined + groqApiKey?: string | undefined + chutesApiKey?: string | undefined + litellmBaseUrl?: string | undefined + litellmApiKey?: string | undefined + litellmModelId?: string | undefined + currentApiConfigName?: string | undefined + listApiConfigMeta?: + | { + id: string + name: string + apiProvider?: + | ( + | "anthropic" + | "glama" + | "openrouter" + | "bedrock" + | "vertex" + | "openai" + | "ollama" + | "vscode-lm" + | "lmstudio" + | "gemini" + | "openai-native" + | "mistral" + | "deepseek" + | "unbound" + | "requesty" + | "human-relay" + | "fake-ai" + | "pearai" + | "xai" + | "groq" + | "chutes" + | "litellm" + ) + | undefined + }[] + | undefined + pinnedApiConfigs?: + | { + [x: string]: boolean + } + | undefined + lastShownAnnouncementId?: string | undefined + customInstructions?: string | undefined + taskHistory?: + | { + id: string + number: number + ts: number + task: string + tokensIn: number + tokensOut: number + cacheWrites?: number | undefined + cacheReads?: number | undefined + totalCost: number + size?: number | undefined + workspace?: string | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined + }[] + | undefined + autoApprovalEnabled?: boolean | undefined + alwaysAllowReadOnly?: boolean | undefined + alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined + alwaysAllowWrite?: boolean | undefined + alwaysAllowWriteOutsideWorkspace?: boolean | undefined + writeDelayMs?: number | undefined + alwaysAllowBrowser?: boolean | undefined + alwaysApproveResubmit?: boolean | undefined + requestDelaySeconds?: number | undefined + alwaysAllowMcp?: boolean | undefined + alwaysAllowModeSwitch?: boolean | undefined + alwaysAllowSubtasks?: boolean | undefined + alwaysAllowExecute?: boolean | undefined + allowedCommands?: string[] | undefined + browserToolEnabled?: boolean | undefined + browserViewportSize?: string | undefined + screenshotQuality?: number | undefined + remoteBrowserEnabled?: boolean | undefined + remoteBrowserHost?: string | undefined + cachedChromeHostUrl?: string | undefined + enableCheckpoints?: boolean | undefined + ttsEnabled?: boolean | undefined + ttsSpeed?: number | undefined + soundEnabled?: boolean | undefined + soundVolume?: number | undefined + maxOpenTabsContext?: number | undefined + maxWorkspaceFiles?: number | undefined + showRooIgnoredFiles?: boolean | undefined + maxReadFileLine?: number | undefined + terminalOutputLineLimit?: number | undefined + terminalShellIntegrationTimeout?: number | undefined + terminalShellIntegrationDisabled?: boolean | undefined + terminalCommandDelay?: number | undefined + terminalPowershellCounter?: boolean | undefined + terminalZshClearEolMark?: boolean | undefined + terminalZshOhMy?: boolean | undefined + terminalZshP10k?: boolean | undefined + terminalZdotdir?: boolean | undefined + terminalCompressProgressBar?: boolean | undefined + experiments?: + | { + autoCondenseContext: boolean + powerSteering: boolean + } + | undefined + language?: + | ( + | "ca" + | "de" + | "en" + | "es" + | "fr" + | "hi" + | "it" + | "ja" + | "ko" + | "nl" + | "pl" + | "pt-BR" + | "ru" + | "tr" + | "vi" + | "zh-CN" + | "zh-TW" + ) + | undefined + telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined + mcpEnabled?: boolean | undefined + enableMcpServerCreation?: boolean | undefined + mode?: string | undefined + modeApiConfigs?: + | { + [x: string]: string + } + | undefined + customModes?: + | { + slug: string + name: string + roleDefinition: string + whenToUse?: string | undefined + customInstructions?: string | undefined + groups: ( + | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") + | [ + "read" | "edit" | "browser" | "command" | "mcp" | "modes", + { + fileRegex?: string | undefined + description?: string | undefined + }, + ] + )[] + source?: ("global" | "project") | undefined + backendOnly?: boolean | undefined + }[] + | undefined + customModePrompts?: + | { + [x: string]: + | { + roleDefinition?: string | undefined + whenToUse?: string | undefined + customInstructions?: string | undefined + } + | undefined + } + | undefined + customSupportPrompts?: + | { + [x: string]: string | undefined + } + | undefined + enhancementApiConfigId?: string | undefined + historyPreviewCollapsed?: boolean | undefined + } + text: string + images?: string[] | undefined + newTab?: boolean | undefined + } + } + | { + commandName: "CancelTask" + data: string + } + | { + commandName: "CloseTask" + data: string + } + +export type { TaskCommand } + +type TaskEvent = + | { + eventName: "message" + payload: [ + { + taskId: string + action: "created" | "updated" + message: { + ts: number + type: "ask" | "say" + ask?: + | ( + | "followup" + | "command" + | "command_output" + | "completion_result" + | "tool" + | "api_req_failed" + | "resume_task" + | "resume_completed_task" + | "mistake_limit_reached" + | "browser_action_launch" + | "use_mcp_server" + ) + | undefined + say?: + | ( + | "error" + | "api_req_started" + | "api_req_finished" + | "api_req_retried" + | "api_req_retry_delayed" + | "api_req_deleted" + | "text" + | "reasoning" + | "completion_result" + | "user_feedback" + | "user_feedback_diff" + | "command_output" + | "shell_integration_warning" + | "browser_action" + | "browser_action_result" + | "mcp_server_request_started" + | "mcp_server_response" + | "subtask_result" + | "checkpoint_saved" + | "rooignore_error" + | "diff_error" + ) + | undefined + text?: string | undefined + images?: string[] | undefined + partial?: boolean | undefined + reasoning?: string | undefined + conversationHistoryIndex?: number | undefined + checkpoint?: + | { + [x: string]: unknown + } + | undefined + progressStatus?: + | { + icon?: string | undefined + text?: string | undefined + } + | undefined + } + }, + ] + } + | { + eventName: "taskCreated" + payload: [string] + } + | { + eventName: "taskStarted" + payload: [string] + } + | { + eventName: "taskModeSwitched" + payload: [string, string] + } + | { + eventName: "taskPaused" + payload: [string] + } + | { + eventName: "taskUnpaused" + payload: [string] + } + | { + eventName: "taskAskResponded" + payload: [string] + } + | { + eventName: "taskAborted" + payload: [string] + } + | { + eventName: "taskSpawned" + payload: [string, string] + } + | { + eventName: "taskCompleted" + payload: [ + string, + { + totalTokensIn: number + totalTokensOut: number + totalCacheWrites?: number | undefined + totalCacheReads?: number | undefined + totalCost: number + contextTokens: number + }, + { + [x: string]: { + attempts: number + failures: number + } + }, + ] + } + | { + eventName: "taskTokenUsageUpdated" + payload: [ + string, + { + totalTokensIn: number + totalTokensOut: number + totalCacheWrites?: number | undefined + totalCacheReads?: number | undefined + totalCost: number + contextTokens: number + }, + ] + } + +export type { TaskEvent } diff --git a/src/extension.ts b/src/extension.ts index 94085091801..7ed49577b22 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -15,25 +15,25 @@ try { import "./utils/path" // Necessary to have access to String.prototype.toPosix. -import { initializeI18n } from "./i18n" import { ContextProxy } from "./core/config/ContextProxy" import { ClineProvider } from "./core/webview/ClineProvider" -import { CodeActionProvider } from "./core/CodeActionProvider" import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider" +import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry" import { McpServerManager } from "./services/mcp/McpServerManager" import { telemetryService } from "./services/telemetry/TelemetryService" -import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry" import { API } from "./exports/api" import { migrateSettings } from "./utils/migrateSettings" +import { formatLanguage } from "./shared/language" import { handleUri, registerCommands, registerCodeActions, registerTerminalActions, + CodeActionProvider, registerPearListener, } from "./activate" -import { formatLanguage } from "./shared/language" +import { initializeI18n } from "./i18n" /** * Built using https://github.com/microsoft/vscode-webview-ui-toolkit diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index 3f9cd4bdb83..af6aed5aa2b 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -89,5 +89,11 @@ "path_placeholder": "D:\\RooCodeStorage", "enter_absolute_path": "Introdueix una ruta completa (p. ex. D:\\RooCodeStorage o /home/user/storage)", "enter_valid_path": "Introdueix una ruta vàlida" + }, + "settings": { + "providers": { + "groqApiKey": "Clau API de Groq", + "getGroqApiKey": "Obté la clau API de Groq" + } } } diff --git a/src/i18n/locales/ca/tools.json b/src/i18n/locales/ca/tools.json index 14e7e438808..c0d8ac20afd 100644 --- a/src/i18n/locales/ca/tools.json +++ b/src/i18n/locales/ca/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (línies 1-{{end}})", "definitionsOnly": " (només definicions)", "maxLines": " (màxim {{max}} línies)" - } + }, + "toolRepetitionLimitReached": "Roo sembla estar atrapat en un bucle, intentant la mateixa acció ({{toolName}}) repetidament. Això podria indicar un problema amb la seva estratègia actual. Considera reformular la tasca, proporcionar instruccions més específiques o guiar-lo cap a un enfocament diferent." } diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index efee4e28623..b3d7abb18a3 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Was soll Roo tun?", "task_placeholder": "Gib deine Aufgabe hier ein" + }, + "settings": { + "providers": { + "groqApiKey": "Groq API-Schlüssel", + "getGroqApiKey": "Groq API-Schlüssel erhalten" + } } } diff --git a/src/i18n/locales/de/tools.json b/src/i18n/locales/de/tools.json index f1b7d850325..e4cb085141d 100644 --- a/src/i18n/locales/de/tools.json +++ b/src/i18n/locales/de/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (Zeilen 1-{{end}})", "definitionsOnly": " (nur Definitionen)", "maxLines": " (maximal {{max}} Zeilen)" - } + }, + "toolRepetitionLimitReached": "Roo scheint in einer Schleife festzustecken und versucht wiederholt dieselbe Aktion ({{toolName}}). Dies könnte auf ein Problem mit der aktuellen Strategie hindeuten. Überlege dir, die Aufgabe umzuformulieren, genauere Anweisungen zu geben oder Roo zu einem anderen Ansatz zu führen." } diff --git a/src/i18n/locales/en/tools.json b/src/i18n/locales/en/tools.json index bb258961ba9..70b0e8d9643 100644 --- a/src/i18n/locales/en/tools.json +++ b/src/i18n/locales/en/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (lines 1-{{end}})", "definitionsOnly": " (definitions only)", "maxLines": " (max {{max}} lines)" - } + }, + "toolRepetitionLimitReached": "Roo appears to be stuck in a loop, attempting the same action ({{toolName}}) repeatedly. This might indicate a problem with its current strategy. Consider rephrasing the task, providing more specific instructions, or guiding it towards a different approach." } diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 4a9b82e0be9..005524b0771 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "¿Qué debe hacer Roo?", "task_placeholder": "Escribe tu tarea aquí" + }, + "settings": { + "providers": { + "groqApiKey": "Clave API de Groq", + "getGroqApiKey": "Obtener clave API de Groq" + } } } diff --git a/src/i18n/locales/es/tools.json b/src/i18n/locales/es/tools.json index f6e4389206d..e5e7b86627f 100644 --- a/src/i18n/locales/es/tools.json +++ b/src/i18n/locales/es/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (líneas 1-{{end}})", "definitionsOnly": " (solo definiciones)", "maxLines": " (máximo {{max}} líneas)" - } + }, + "toolRepetitionLimitReached": "Roo parece estar atrapado en un bucle, intentando la misma acción ({{toolName}}) repetidamente. Esto podría indicar un problema con su estrategia actual. Considera reformular la tarea, proporcionar instrucciones más específicas o guiarlo hacia un enfoque diferente." } diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index 55e172100ee..c20650fd789 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Que doit faire Roo ?", "task_placeholder": "Écris ta tâche ici" + }, + "settings": { + "providers": { + "groqApiKey": "Clé API Groq", + "getGroqApiKey": "Obtenir la clé API Groq" + } } } diff --git a/src/i18n/locales/fr/tools.json b/src/i18n/locales/fr/tools.json index 97a640a18f9..777727c34e1 100644 --- a/src/i18n/locales/fr/tools.json +++ b/src/i18n/locales/fr/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (lignes 1-{{end}})", "definitionsOnly": " (définitions uniquement)", "maxLines": " (max {{max}} lignes)" - } + }, + "toolRepetitionLimitReached": "Roo semble être bloqué dans une boucle, tentant la même action ({{toolName}}) de façon répétée. Cela pourrait indiquer un problème avec sa stratégie actuelle. Envisage de reformuler la tâche, de fournir des instructions plus spécifiques ou de le guider vers une approche différente." } diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index e4f3a51ebb8..cba7456dd52 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Agent को क्या करना है?", "task_placeholder": "अपना कार्य यहाँ लिखें" + }, + "settings": { + "providers": { + "groqApiKey": "ग्रोक एपीआई कुंजी", + "getGroqApiKey": "ग्रोक एपीआई कुंजी प्राप्त करें" + } } } diff --git a/src/i18n/locales/hi/tools.json b/src/i18n/locales/hi/tools.json index 7f682391f49..7784f70480f 100644 --- a/src/i18n/locales/hi/tools.json +++ b/src/i18n/locales/hi/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (पंक्तियाँ 1-{{end}})", "definitionsOnly": " (केवल परिभाषाएँ)", "maxLines": " (अधिकतम {{max}} पंक्तियाँ)" - } + }, + "toolRepetitionLimitReached": "Roo एक लूप में फंसा हुआ लगता है, बार-बार एक ही क्रिया ({{toolName}}) को दोहरा रहा है। यह उसकी वर्तमान रणनीति में किसी समस्या का संकेत हो सकता है। कार्य को पुनः परिभाषित करने, अधिक विशिष्ट निर्देश देने, या उसे एक अलग दृष्टिकोण की ओर मार्गदर्शित करने पर विचार करें।" } diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index 94dda7ea3f1..c5335a1bbef 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Cosa deve fare Roo?", "task_placeholder": "Scrivi il tuo compito qui" + }, + "settings": { + "providers": { + "groqApiKey": "Chiave API Groq", + "getGroqApiKey": "Ottieni chiave API Groq" + } } } diff --git a/src/i18n/locales/it/tools.json b/src/i18n/locales/it/tools.json index a9ad538e9de..a639137eb81 100644 --- a/src/i18n/locales/it/tools.json +++ b/src/i18n/locales/it/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (righe 1-{{end}})", "definitionsOnly": " (solo definizioni)", "maxLines": " (max {{max}} righe)" - } + }, + "toolRepetitionLimitReached": "Roo sembra essere bloccato in un ciclo, tentando ripetutamente la stessa azione ({{toolName}}). Questo potrebbe indicare un problema con la sua strategia attuale. Considera di riformulare l'attività, fornire istruzioni più specifiche o guidarlo verso un approccio diverso." } diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index dc6a9da9299..0aa3615e7df 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Rooにどんなことをさせますか?", "task_placeholder": "タスクをここに入力してください" + }, + "settings": { + "providers": { + "groqApiKey": "Groq APIキー", + "getGroqApiKey": "Groq APIキーを取得" + } } } diff --git a/src/i18n/locales/ja/tools.json b/src/i18n/locales/ja/tools.json index 6daed74793f..fda79adb249 100644 --- a/src/i18n/locales/ja/tools.json +++ b/src/i18n/locales/ja/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (1-{{end}}行目)", "definitionsOnly": " (定義のみ)", "maxLines": " (最大{{max}}行)" - } + }, + "toolRepetitionLimitReached": "Rooが同じ操作({{toolName}})を繰り返し試みるループに陥っているようです。これは現在の方法に問題がある可能性を示しています。タスクの言い換え、より具体的な指示の提供、または別のアプローチへの誘導を検討してください。" } diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index 8e1ba132f44..6ef9b897269 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Roo에게 무엇을 시킬까요?", "task_placeholder": "여기에 작업을 입력하세요" + }, + "settings": { + "providers": { + "groqApiKey": "Groq API 키", + "getGroqApiKey": "Groq API 키 받기" + } } } diff --git a/src/i18n/locales/ko/tools.json b/src/i18n/locales/ko/tools.json index f4583d2d065..cd4679b2eb5 100644 --- a/src/i18n/locales/ko/tools.json +++ b/src/i18n/locales/ko/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (1-{{end}}행)", "definitionsOnly": " (정의만)", "maxLines": " (최대 {{max}}행)" - } + }, + "toolRepetitionLimitReached": "Roo가 같은 동작({{toolName}})을 반복적으로 시도하면서 루프에 갇힌 것 같습니다. 이는 현재 전략에 문제가 있을 수 있음을 나타냅니다. 작업을 다시 표현하거나, 더 구체적인 지침을 제공하거나, 다른 접근 방식으로 안내해 보세요." } diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json new file mode 100644 index 00000000000..e676235dc2c --- /dev/null +++ b/src/i18n/locales/nl/common.json @@ -0,0 +1,93 @@ +{ + "extension": { + "name": "Roo Code", + "description": "Een compleet ontwikkelteam van AI-agenten in je editor." + }, + "number_format": { + "thousand_suffix": "k", + "million_suffix": "m", + "billion_suffix": "mrd" + }, + "welcome": "Welkom, {{name}}! Je hebt {{count}} meldingen.", + "items": { + "zero": "Geen items", + "one": "Eén item", + "other": "{{count}} items" + }, + "confirmation": { + "reset_state": "Weet je zeker dat je alle status en geheime opslag in de extensie wilt resetten? Dit kan niet ongedaan worden gemaakt.", + "delete_config_profile": "Weet je zeker dat je dit configuratieprofiel wilt verwijderen?", + "delete_custom_mode": "Weet je zeker dat je deze aangepaste modus wilt verwijderen?", + "delete_message": "Wat wil je verwijderen?", + "just_this_message": "Alleen dit bericht", + "this_and_subsequent": "Dit en alle volgende berichten" + }, + "errors": { + "invalid_mcp_config": "Ongeldig project MCP-configuratieformaat", + "invalid_mcp_settings_format": "Ongeldig MCP-instellingen JSON-formaat. Zorg ervoor dat je instellingen het juiste JSON-formaat volgen.", + "invalid_mcp_settings_syntax": "Ongeldig MCP-instellingen JSON-formaat. Controleer je instellingenbestand op syntaxfouten.", + "invalid_mcp_settings_validation": "Ongeldig MCP-instellingenformaat: {{errorMessages}}", + "failed_initialize_project_mcp": "Initialiseren van project MCP-server mislukt: {{error}}", + "invalid_data_uri": "Ongeldig data-URI-formaat", + "checkpoint_timeout": "Time-out bij het herstellen van checkpoint.", + "checkpoint_failed": "Herstellen van checkpoint mislukt.", + "no_workspace": "Open eerst een projectmap", + "update_support_prompt": "Bijwerken van ondersteuningsprompt mislukt", + "reset_support_prompt": "Resetten van ondersteuningsprompt mislukt", + "enhance_prompt": "Verbeteren van prompt mislukt", + "get_system_prompt": "Ophalen van systeemprompt mislukt", + "search_commits": "Zoeken naar commits mislukt", + "save_api_config": "Opslaan van API-configuratie mislukt", + "create_api_config": "Aanmaken van API-configuratie mislukt", + "rename_api_config": "Hernoemen van API-configuratie mislukt", + "load_api_config": "Laden van API-configuratie mislukt", + "delete_api_config": "Verwijderen van API-configuratie mislukt", + "list_api_config": "Ophalen van lijst met API-configuraties mislukt", + "update_server_timeout": "Bijwerken van server-timeout mislukt", + "create_mcp_json": "Aanmaken of openen van .roo/mcp.json mislukt: {{error}}", + "hmr_not_running": "Lokale ontwikkelserver draait niet, HMR werkt niet. Voer 'npm run dev' uit voordat je de extensie start om HMR in te schakelen.", + "retrieve_current_mode": "Fout: ophalen van huidige modus uit status mislukt.", + "failed_delete_repo": "Verwijderen van gekoppelde schaduwrepository of branch mislukt: {{error}}", + "failed_remove_directory": "Verwijderen van taakmap mislukt: {{error}}", + "custom_storage_path_unusable": "Aangepast opslagpad \"{{path}}\" is onbruikbaar, standaardpad wordt gebruikt", + "cannot_access_path": "Kan pad {{path}} niet openen: {{error}}", + "failed_update_project_mcp": "Bijwerken van project MCP-servers mislukt" + }, + "warnings": { + "no_terminal_content": "Geen terminalinhoud geselecteerd", + "missing_task_files": "De bestanden van deze taak ontbreken. Wil je deze uit de takenlijst verwijderen?" + }, + "info": { + "no_changes": "Geen wijzigingen gevonden.", + "clipboard_copy": "Systeemprompt succesvol gekopieerd naar klembord", + "history_cleanup": "{{count}} taak/taken met ontbrekende bestanden uit geschiedenis verwijderd.", + "mcp_server_restarting": "{{serverName}} MCP-server wordt opnieuw gestart...", + "mcp_server_connected": "{{serverName}} MCP-server verbonden", + "mcp_server_deleted": "MCP-server verwijderd: {{serverName}}", + "mcp_server_not_found": "Server \"{{serverName}}\" niet gevonden in configuratie", + "custom_storage_path_set": "Aangepast opslagpad ingesteld: {{path}}", + "default_storage_path": "Terug naar standaard opslagpad", + "settings_imported": "Instellingen succesvol geïmporteerd." + }, + "answers": { + "yes": "Ja", + "no": "Nee", + "cancel": "Annuleren", + "remove": "Verwijderen", + "keep": "Behouden" + }, + "tasks": { + "canceled": "Taakfout: gestopt en geannuleerd door gebruiker.", + "deleted": "Taakfout: gestopt en verwijderd door gebruiker." + }, + "storage": { + "prompt_custom_path": "Voer een aangepast opslagpad voor gespreksgeschiedenis in, laat leeg voor standaardlocatie", + "path_placeholder": "D:\\RooCodeStorage", + "enter_absolute_path": "Voer een absoluut pad in (bijv. D:\\RooCodeStorage of /home/user/storage)", + "enter_valid_path": "Voer een geldig pad in" + }, + "input": { + "task_prompt": "Wat moet Roo doen?", + "task_placeholder": "Typ hier je taak" + } +} diff --git a/src/i18n/locales/nl/tools.json b/src/i18n/locales/nl/tools.json new file mode 100644 index 00000000000..a3f85a911e2 --- /dev/null +++ b/src/i18n/locales/nl/tools.json @@ -0,0 +1,10 @@ +{ + "readFile": { + "linesRange": " (regels {{start}}-{{end}})", + "linesFromToEnd": " (regels {{start}}-einde)", + "linesFromStartTo": " (regels 1-{{end}})", + "definitionsOnly": " (alleen definities)", + "maxLines": " (max {{max}} regels)" + }, + "toolRepetitionLimitReached": "Roo lijkt vast te zitten in een lus, waarbij hij herhaaldelijk dezelfde actie ({{toolName}}) probeert. Dit kan duiden op een probleem met de huidige strategie. Overweeg de taak te herformuleren, specifiekere instructies te geven of Roo naar een andere aanpak te leiden." +} diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index dbc459ad581..5cfb640da15 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Co ma zrobić Roo?", "task_placeholder": "Wpisz swoje zadanie tutaj" + }, + "settings": { + "providers": { + "groqApiKey": "Klucz API Groq", + "getGroqApiKey": "Uzyskaj klucz API Groq" + } } } diff --git a/src/i18n/locales/pl/tools.json b/src/i18n/locales/pl/tools.json index 33edb77cfae..6de5cc8f789 100644 --- a/src/i18n/locales/pl/tools.json +++ b/src/i18n/locales/pl/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (linie 1-{{end}})", "definitionsOnly": " (tylko definicje)", "maxLines": " (maks. {{max}} linii)" - } + }, + "toolRepetitionLimitReached": "Wygląda na to, że Roo utknął w pętli, wielokrotnie próbując wykonać tę samą akcję ({{toolName}}). Może to wskazywać na problem z jego obecną strategią. Rozważ przeformułowanie zadania, podanie bardziej szczegółowych instrukcji lub nakierowanie go na inne podejście." } diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index 0de62c10a54..386902feb47 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -89,5 +89,11 @@ "path_placeholder": "D:\\RooCodeStorage", "enter_absolute_path": "Por favor, digite um caminho absoluto (ex: D:\\RooCodeStorage ou /home/user/storage)", "enter_valid_path": "Por favor, digite um caminho válido" + }, + "settings": { + "providers": { + "groqApiKey": "Chave de API Groq", + "getGroqApiKey": "Obter chave de API Groq" + } } } diff --git a/src/i18n/locales/pt-BR/tools.json b/src/i18n/locales/pt-BR/tools.json index 0992809bdd0..096aadad200 100644 --- a/src/i18n/locales/pt-BR/tools.json +++ b/src/i18n/locales/pt-BR/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (linhas 1-{{end}})", "definitionsOnly": " (apenas definições)", "maxLines": " (máx. {{max}} linhas)" - } + }, + "toolRepetitionLimitReached": "Roo parece estar preso em um loop, tentando a mesma ação ({{toolName}}) repetidamente. Isso pode indicar um problema com sua estratégia atual. Considere reformular a tarefa, fornecer instruções mais específicas ou guiá-lo para uma abordagem diferente." } diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index f9f3a87b2d0..73f20cc0c09 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Что должен сделать Roo?", "task_placeholder": "Введите вашу задачу здесь" + }, + "settings": { + "providers": { + "groqApiKey": "Ключ API Groq", + "getGroqApiKey": "Получить ключ API Groq" + } } } diff --git a/src/i18n/locales/ru/tools.json b/src/i18n/locales/ru/tools.json index 4f4aaed97ca..2968c08935b 100644 --- a/src/i18n/locales/ru/tools.json +++ b/src/i18n/locales/ru/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (строки 1-{{end}})", "definitionsOnly": " (только определения)", "maxLines": " (макс. {{max}} строк)" - } + }, + "toolRepetitionLimitReached": "Похоже, что Roo застрял в цикле, многократно пытаясь выполнить одно и то же действие ({{toolName}}). Это может указывать на проблему с его текущей стратегией. Попробуйте переформулировать задачу, предоставить более конкретные инструкции или направить его к другому подходу." } diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 0b3045166c5..eef0901e0de 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Agent ne yapsın?", "task_placeholder": "Görevini buraya yaz" + }, + "settings": { + "providers": { + "groqApiKey": "Groq API Anahtarı", + "getGroqApiKey": "Groq API Anahtarı Al" + } } } diff --git a/src/i18n/locales/tr/tools.json b/src/i18n/locales/tr/tools.json index 19b0158d139..86fcada627d 100644 --- a/src/i18n/locales/tr/tools.json +++ b/src/i18n/locales/tr/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (satır 1-{{end}})", "definitionsOnly": " (sadece tanımlar)", "maxLines": " (maks. {{max}} satır)" - } + }, + "toolRepetitionLimitReached": "Roo bir döngüye takılmış gibi görünüyor, aynı eylemi ({{toolName}}) tekrar tekrar deniyor. Bu, mevcut stratejisinde bir sorun olduğunu gösterebilir. Görevi yeniden ifade etmeyi, daha spesifik talimatlar vermeyi veya onu farklı bir yaklaşıma yönlendirmeyi düşünün." } diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index 1375111ed95..c6d52b559c6 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "Bạn muốn Roo làm gì?", "task_placeholder": "Nhập nhiệm vụ của bạn ở đây" + }, + "settings": { + "providers": { + "groqApiKey": "Khóa API Groq", + "getGroqApiKey": "Lấy khóa API Groq" + } } } diff --git a/src/i18n/locales/vi/tools.json b/src/i18n/locales/vi/tools.json index 76af39abe1f..d77fd34f1d0 100644 --- a/src/i18n/locales/vi/tools.json +++ b/src/i18n/locales/vi/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (dòng 1-{{end}})", "definitionsOnly": " (chỉ định nghĩa)", "maxLines": " (tối đa {{max}} dòng)" - } + }, + "toolRepetitionLimitReached": "Roo dường như đang bị mắc kẹt trong một vòng lặp, liên tục cố gắng thực hiện cùng một hành động ({{toolName}}). Điều này có thể cho thấy vấn đề với chiến lược hiện tại. Hãy cân nhắc việc diễn đạt lại nhiệm vụ, cung cấp hướng dẫn cụ thể hơn, hoặc hướng Roo theo một cách tiếp cận khác." } diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 66e84d55e07..e046d15950e 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "让Roo做什么?", "task_placeholder": "在这里输入任务" + }, + "settings": { + "providers": { + "groqApiKey": "Groq API 密钥", + "getGroqApiKey": "获取 Groq API 密钥" + } } } diff --git a/src/i18n/locales/zh-CN/tools.json b/src/i18n/locales/zh-CN/tools.json index 5f9b2ddaf74..30ca1f7ab12 100644 --- a/src/i18n/locales/zh-CN/tools.json +++ b/src/i18n/locales/zh-CN/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (第 1-{{end}} 行)", "definitionsOnly": " (仅定义)", "maxLines": " (最多 {{max}} 行)" - } + }, + "toolRepetitionLimitReached": "Roo 似乎陷入循环,反复尝试同一操作 ({{toolName}})。这可能表明当前策略存在问题。请考虑重新描述任务、提供更具体的指示或引导其尝试不同的方法。" } diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index 61f01c30622..4fd084ae681 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -89,5 +89,11 @@ "input": { "task_prompt": "讓 Roo 做什麼?", "task_placeholder": "在這裡輸入工作" + }, + "settings": { + "providers": { + "groqApiKey": "Groq API 金鑰", + "getGroqApiKey": "取得 Groq API 金鑰" + } } } diff --git a/src/i18n/locales/zh-TW/tools.json b/src/i18n/locales/zh-TW/tools.json index d64d1cca479..2416fe57f68 100644 --- a/src/i18n/locales/zh-TW/tools.json +++ b/src/i18n/locales/zh-TW/tools.json @@ -5,5 +5,6 @@ "linesFromStartTo": " (第 1-{{end}} 行)", "definitionsOnly": " (僅定義)", "maxLines": " (最多 {{max}} 行)" - } + }, + "toolRepetitionLimitReached": "Roo 似乎陷入循環,反覆嘗試同一操作 ({{toolName}})。這可能表明目前策略存在問題。請考慮重新描述工作、提供更具體的指示或引導其嘗試不同的方法。" } diff --git a/src/integrations/diagnostics/__tests__/diagnostics.test.ts b/src/integrations/diagnostics/__tests__/diagnostics.test.ts new file mode 100644 index 00000000000..3cf9ede4f65 --- /dev/null +++ b/src/integrations/diagnostics/__tests__/diagnostics.test.ts @@ -0,0 +1,376 @@ +import * as vscode from "vscode" +import { diagnosticsToProblemsString } from ".." + +// Mock path module +jest.mock("path", () => ({ + relative: jest.fn((cwd, fullPath) => { + // Handle the specific case already present + if (cwd === "/project/root" && fullPath === "/project/root/src/utils/file.ts") { + return "src/utils/file.ts" + } + // Handle the test cases with /path/to as cwd + if (cwd === "/path/to") { + // Simple relative path calculation for the test cases + return fullPath.replace(cwd + "/", "") + } + // Fallback for other cases (can be adjusted if needed) + return fullPath + }), +})) + +// Mock vscode module +jest.mock("vscode", () => ({ + Uri: { + file: jest.fn((path) => ({ + fsPath: path, + toString: jest.fn(() => path), + })), + }, + Diagnostic: jest.fn().mockImplementation((range, message, severity) => ({ + range, + message, + severity, + source: "test", + })), + Range: jest.fn().mockImplementation((startLine, startChar, endLine, endChar) => ({ + start: { line: startLine, character: startChar }, + end: { line: endLine, character: endChar }, + })), + DiagnosticSeverity: { + Error: 0, + Warning: 1, + Information: 2, + Hint: 3, + }, + FileType: { + Unknown: 0, + File: 1, + Directory: 2, + SymbolicLink: 64, + }, + workspace: { + fs: { + stat: jest.fn(), + }, + openTextDocument: jest.fn(), + }, +})) + +describe("diagnosticsToProblemsString", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("should filter diagnostics by severity and include correct labels", async () => { + // Mock file URI + const fileUri = vscode.Uri.file("/path/to/file.ts") + + // Create diagnostics with different severities + const diagnostics = [ + new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), "Error message", vscode.DiagnosticSeverity.Error), + new vscode.Diagnostic(new vscode.Range(1, 0, 1, 10), "Warning message", vscode.DiagnosticSeverity.Warning), + new vscode.Diagnostic(new vscode.Range(2, 0, 2, 10), "Info message", vscode.DiagnosticSeverity.Information), + new vscode.Diagnostic(new vscode.Range(3, 0, 3, 10), "Hint message", vscode.DiagnosticSeverity.Hint), + ] + + // Mock fs.stat to return file type + const mockStat = { + type: vscode.FileType.File, + } + vscode.workspace.fs.stat = jest.fn().mockResolvedValue(mockStat) + + // Mock document content + const mockDocument = { + lineAt: jest.fn((line) => ({ + text: `Line ${line + 1} content`, + })), + } + vscode.workspace.openTextDocument = jest.fn().mockResolvedValue(mockDocument) + + // Test with Error and Warning severities only + const result = await diagnosticsToProblemsString( + [[fileUri, diagnostics]], + [vscode.DiagnosticSeverity.Error, vscode.DiagnosticSeverity.Warning], + "/path/to", + ) + + // Verify only Error and Warning diagnostics are included + expect(result).toContain("Error message") + expect(result).toContain("Warning message") + expect(result).not.toContain("Info message") + expect(result).not.toContain("Hint message") + + // Verify correct severity labels are used + expect(result).toContain("[test Error]") + expect(result).toContain("[test Warning]") + expect(result).not.toContain("[test Information]") + expect(result).not.toContain("[test Hint]") + + // Verify line content is included + expect(result).toContain("Line 1 content") + expect(result).toContain("Line 2 content") + }) + + it("should handle directory URIs correctly without attempting to open as document", async () => { + // Mock directory URI + const dirUri = vscode.Uri.file("/path/to/directory/") + + // Mock diagnostic for directory + const diagnostic = new vscode.Diagnostic( + new vscode.Range(0, 0, 0, 10), + "Directory diagnostic message", + vscode.DiagnosticSeverity.Error, + ) + + // Mock fs.stat to return directory type + const mockStat = { + type: vscode.FileType.Directory, + } + vscode.workspace.fs.stat = jest.fn().mockResolvedValue(mockStat) + + // Mock openTextDocument to ensure it's not called + vscode.workspace.openTextDocument = jest.fn() + + // Call the function + const result = await diagnosticsToProblemsString( + [[dirUri, [diagnostic]]], + [vscode.DiagnosticSeverity.Error], + "/path/to", + ) + + // Verify fs.stat was called with the directory URI + expect(vscode.workspace.fs.stat).toHaveBeenCalledWith(dirUri) + + // Verify openTextDocument was not called + expect(vscode.workspace.openTextDocument).not.toHaveBeenCalled() + + // Verify the output contains the expected directory indicator + expect(result).toContain("(directory)") + expect(result).toContain("Directory diagnostic message") + expect(result).toMatch(/directory\/\n- \[test Error\] 1 \| \(directory\) : Directory diagnostic message/) + }) + + it("should correctly handle multiple diagnostics for the same file", async () => { + // Mock file URI + const fileUri = vscode.Uri.file("/path/to/file.ts") + + // Create multiple diagnostics for the same file + const diagnostics = [ + new vscode.Diagnostic(new vscode.Range(4, 0, 4, 10), "Later line error", vscode.DiagnosticSeverity.Error), + new vscode.Diagnostic( + new vscode.Range(0, 0, 0, 10), + "First line warning", + vscode.DiagnosticSeverity.Warning, + ), + new vscode.Diagnostic( + new vscode.Range(2, 0, 2, 10), + "Middle line info", + vscode.DiagnosticSeverity.Information, + ), + ] + + // Mock fs.stat to return file type + const mockStat = { + type: vscode.FileType.File, + } + vscode.workspace.fs.stat = jest.fn().mockResolvedValue(mockStat) + + // Mock document content with specific line texts for each test case + const mockDocument = { + lineAt: jest.fn((line: number) => { + const lineTexts: Record = { + 0: "Line 0 content for warning", + 2: "Line 2 content for info", + 4: "Line 4 content for error", + } + return { text: lineTexts[line] } + }), + } + vscode.workspace.openTextDocument = jest.fn().mockResolvedValue(mockDocument) + + // Call the function with all severities + const result = await diagnosticsToProblemsString( + [[fileUri, diagnostics]], + [vscode.DiagnosticSeverity.Error, vscode.DiagnosticSeverity.Warning, vscode.DiagnosticSeverity.Information], + "/path/to", + ) + + // Verify all diagnostics are included in the output + expect(result).toContain("First line warning") + expect(result).toContain("Middle line info") + expect(result).toContain("Later line error") + + // Verify line content is included for each diagnostic and matches the test case + expect(result).toContain("Line 0 content for warning") + expect(result).toContain("Line 2 content for info") + expect(result).toContain("Line 4 content for error") + + // Verify the output contains all severity labels + expect(result).toContain("[test Warning]") + expect(result).toContain("[test Information]") + expect(result).toContain("[test Error]") + + // Verify diagnostics appear in line number order (even though input wasn't sorted) + // Verify exact output format + expect(result).toBe( + "file.ts\n" + + "- [test Warning] 1 | Line 0 content for warning : First line warning\n" + + "- [test Information] 3 | Line 2 content for info : Middle line info\n" + + "- [test Error] 5 | Line 4 content for error : Later line error", + ) + }) + + it("should correctly handle diagnostics from multiple files", async () => { + // Mock URIs for different files + const fileUri1 = vscode.Uri.file("/path/to/file1.ts") + const fileUri2 = vscode.Uri.file("/path/to/subdir/file2.ts") + + // Create diagnostics for each file + const diagnostics1 = [ + new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), "File1 error", vscode.DiagnosticSeverity.Error), + ] + + const diagnostics2 = [ + new vscode.Diagnostic(new vscode.Range(1, 0, 1, 10), "File2 warning", vscode.DiagnosticSeverity.Warning), + new vscode.Diagnostic(new vscode.Range(2, 0, 2, 10), "File2 info", vscode.DiagnosticSeverity.Information), + ] + + // Mock fs.stat to return file type for both files + const mockStat = { + type: vscode.FileType.File, + } + vscode.workspace.fs.stat = jest.fn().mockResolvedValue(mockStat) + + // Mock document content with specific line texts for each test case + const mockDocument1 = { + lineAt: jest.fn((_line) => ({ + text: "Line 1 content for error", + })), + } + const mockDocument2 = { + lineAt: jest.fn((line) => { + const lineTexts = ["Line 1 content", "Line 2 content for warning", "Line 3 content for info"] + return { text: lineTexts[line] } + }), + } + vscode.workspace.openTextDocument = jest + .fn() + .mockResolvedValueOnce(mockDocument1) + .mockResolvedValueOnce(mockDocument2) + + // Call the function with all severities + const result = await diagnosticsToProblemsString( + [ + [fileUri1, diagnostics1], + [fileUri2, diagnostics2], + ], + [vscode.DiagnosticSeverity.Error, vscode.DiagnosticSeverity.Warning, vscode.DiagnosticSeverity.Information], + "/path/to", + ) + + // Verify file paths are correctly shown with relative paths + expect(result).toContain("file1.ts") + expect(result).toContain("subdir/file2.ts") + + // Verify diagnostics are grouped under their respective files + const file1Section = result.split("file1.ts")[1] + expect(file1Section).toContain("File1 error") + expect(file1Section).toContain("Line 1 content for error") + + const file2Section = result.split("subdir/file2.ts")[1] + expect(file2Section).toContain("File2 warning") + expect(file2Section).toContain("Line 2 content for warning") + expect(file2Section).toContain("File2 info") + expect(file2Section).toContain("Line 3 content for info") + + // Verify exact output format + expect(result).toBe( + "file1.ts\n" + + "- [test Error] 1 | Line 1 content for error : File1 error\n\n" + + "subdir/file2.ts\n" + + "- [test Warning] 2 | Line 2 content for warning : File2 warning\n" + + "- [test Information] 3 | Line 3 content for info : File2 info", + ) + }) + + it("should return empty string when no diagnostics match the severity filter", async () => { + // Mock file URI + const fileUri = vscode.Uri.file("/path/to/file.ts") + + // Create diagnostics with Error and Warning severities + const diagnostics = [ + new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), "Error message", vscode.DiagnosticSeverity.Error), + new vscode.Diagnostic(new vscode.Range(1, 0, 1, 10), "Warning message", vscode.DiagnosticSeverity.Warning), + ] + + // Mock fs.stat to return file type + const mockStat = { + type: vscode.FileType.File, + } + vscode.workspace.fs.stat = jest.fn().mockResolvedValue(mockStat) + + // Mock document content (though it shouldn't be accessed in this case) + const mockDocument = { + lineAt: jest.fn((line) => ({ + text: `Line ${line + 1} content`, + })), + } + vscode.workspace.openTextDocument = jest.fn().mockResolvedValue(mockDocument) + + // Test with Information and Hint severities only (which don't match our diagnostics) + const result = await diagnosticsToProblemsString( + [[fileUri, diagnostics]], + [vscode.DiagnosticSeverity.Information, vscode.DiagnosticSeverity.Hint], + "/path/to", + ) + + // Verify empty string is returned + expect(result).toBe("") + + // Verify no unnecessary calls were made + expect(vscode.workspace.fs.stat).not.toHaveBeenCalled() + expect(vscode.workspace.openTextDocument).not.toHaveBeenCalled() + }) + + it("should correctly handle cwd parameter for relative file paths", async () => { + // Mock file URI in a subdirectory + const fileUri = vscode.Uri.file("/project/root/src/utils/file.ts") + + // Create a diagnostic for the file + const diagnostic = new vscode.Diagnostic( + new vscode.Range(4, 0, 4, 10), + "Relative path test error", + vscode.DiagnosticSeverity.Error, + ) + + // Mock fs.stat to return file type + const mockStat = { + type: vscode.FileType.File, + } + vscode.workspace.fs.stat = jest.fn().mockResolvedValue(mockStat) + + // Mock document content matching test assertion + const mockDocument = { + lineAt: jest.fn((line) => ({ + text: `Line ${line + 1} content for error`, + })), + } + vscode.workspace.openTextDocument = jest.fn().mockResolvedValue(mockDocument) + + // Call the function with cwd set to the project root + const result = await diagnosticsToProblemsString( + [[fileUri, [diagnostic]]], + [vscode.DiagnosticSeverity.Error], + "/project/root", + ) + + // Verify exact output format + expect(result).toBe( + "src/utils/file.ts\n" + "- [test Error] 5 | Line 5 content for error : Relative path test error", + ) + + // Verify fs.stat and openTextDocument were called + expect(vscode.workspace.fs.stat).toHaveBeenCalledWith(fileUri) + expect(vscode.workspace.openTextDocument).toHaveBeenCalledWith(fileUri) + }) +}) diff --git a/src/integrations/diagnostics/index.ts b/src/integrations/diagnostics/index.ts index 2d829f26e76..97b83353534 100644 --- a/src/integrations/diagnostics/index.ts +++ b/src/integrations/diagnostics/index.ts @@ -76,9 +76,12 @@ export async function diagnosticsToProblemsString( cwd: string, ): Promise { const documents = new Map() + const fileStats = new Map() let result = "" for (const [uri, fileDiagnostics] of diagnostics) { - const problems = fileDiagnostics.filter((d) => severities.includes(d.severity)) + const problems = fileDiagnostics + .filter((d) => severities.includes(d.severity)) + .sort((a, b) => a.range.start.line - b.range.start.line) if (problems.length > 0) { result += `\n\n${path.relative(cwd, uri.fsPath).toPosix()}` for (const diagnostic of problems) { @@ -101,10 +104,23 @@ export async function diagnosticsToProblemsString( } const line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed const source = diagnostic.source ? `${diagnostic.source} ` : "" - const document = documents.get(uri) || (await vscode.workspace.openTextDocument(uri)) - documents.set(uri, document) - const lineContent = document.lineAt(diagnostic.range.start.line).text - result += `\n- [${source}${label}] ${line} | ${lineContent} : ${diagnostic.message}` + try { + let fileStat = fileStats.get(uri) + if (!fileStat) { + fileStat = await vscode.workspace.fs.stat(uri) + fileStats.set(uri, fileStat) + } + if (fileStat.type === vscode.FileType.File) { + const document = documents.get(uri) || (await vscode.workspace.openTextDocument(uri)) + documents.set(uri, document) + const lineContent = document.lineAt(diagnostic.range.start.line).text + result += `\n- [${source}${label}] ${line} | ${lineContent} : ${diagnostic.message}` + } else { + result += `\n- [${source}${label}] 1 | (directory) : ${diagnostic.message}` + } + } catch { + result += `\n- [${source}${label}] ${line} | (unavailable) : ${diagnostic.message}` + } } } } diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index ac17d88d667..c2c471fdf1b 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -1,16 +1,19 @@ import * as vscode from "vscode" import * as path from "path" import * as fs from "fs/promises" +import * as diff from "diff" +import stripBom from "strip-bom" + import { createDirectoriesForFile } from "../../utils/fs" import { arePathsEqual } from "../../utils/path" import { formatResponse } from "../../core/prompts/responses" -import { DecorationController } from "./DecorationController" -import * as diff from "diff" import { diagnosticsToProblemsString, getNewDiagnostics } from "../diagnostics" -import stripBom from "strip-bom" + +import { DecorationController } from "./DecorationController" export const DIFF_VIEW_URI_SCHEME = "cline-diff" +// TODO: https://github.com/cline/cline/pull/3354 export class DiffViewProvider { editType?: "create" | "modify" isEditing = false @@ -32,17 +35,21 @@ export class DiffViewProvider { const fileExists = this.editType === "modify" const absolutePath = path.resolve(this.cwd, relPath) this.isEditing = true - // if the file is already open, ensure it's not dirty before getting its contents + + // If the file is already open, ensure it's not dirty before getting its + // contents. if (fileExists) { const existingDocument = vscode.workspace.textDocuments.find((doc) => arePathsEqual(doc.uri.fsPath, absolutePath), ) + if (existingDocument && existingDocument.isDirty) { await existingDocument.save() } } - // get diagnostics before editing the file, we'll compare to diagnostics after editing to see if cline needs to fix anything + // Get diagnostics before editing the file, we'll compare to diagnostics + // after editing to see if cline needs to fix anything. this.preDiagnostics = vscode.languages.getDiagnostics() if (fileExists) { @@ -50,33 +57,41 @@ export class DiffViewProvider { } else { this.originalContent = "" } - // for new files, create any necessary directories and keep track of new directories to delete if the user denies the operation + + // For new files, create any necessary directories and keep track of new + // directories to delete if the user denies the operation. this.createdDirs = await createDirectoriesForFile(absolutePath) - // make sure the file exists before we open it + + // Make sure the file exists before we open it. if (!fileExists) { await fs.writeFile(absolutePath, "") } - // if the file was already open, close it (must happen after showing the diff view since if it's the only tab the column will close) + + // If the file was already open, close it (must happen after showing the + // diff view since if it's the only tab the column will close). this.documentWasOpen = false - // close the tab if it's open (it's already saved above) + + // Close the tab if it's open (it's already saved above). const tabs = vscode.window.tabGroups.all .map((tg) => tg.tabs) .flat() .filter( (tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath), ) + for (const tab of tabs) { if (!tab.isDirty) { await vscode.window.tabGroups.close(tab) } this.documentWasOpen = true } + this.activeDiffEditor = await this.openDiffEditor() this.fadedOverlayController = new DecorationController("fadedOverlay", this.activeDiffEditor) this.activeLineController = new DecorationController("activeLine", this.activeDiffEditor) - // Apply faded overlay to all lines initially + // Apply faded overlay to all lines initially. this.fadedOverlayController.addLines(0, this.activeDiffEditor.document.lineCount) - this.scrollEditorToLine(0) // will this crash for new files? + this.scrollEditorToLine(0) // Will this crash for new files? this.streamedLines = [] } @@ -84,58 +99,70 @@ export class DiffViewProvider { if (!this.relPath || !this.activeLineController || !this.fadedOverlayController) { throw new Error("Required values not set") } + this.newContent = accumulatedContent const accumulatedLines = accumulatedContent.split("\n") + if (!isFinal) { - accumulatedLines.pop() // remove the last partial line only if it's not the final update + accumulatedLines.pop() // Remove the last partial line only if it's not the final update. } const diffEditor = this.activeDiffEditor const document = diffEditor?.document + if (!diffEditor || !document) { throw new Error("User closed text editor, unable to edit file...") } - // Place cursor at the beginning of the diff editor to keep it out of the way of the stream animation + // Place cursor at the beginning of the diff editor to keep it out of + // the way of the stream animation. const beginningOfDocument = new vscode.Position(0, 0) diffEditor.selection = new vscode.Selection(beginningOfDocument, beginningOfDocument) const endLine = accumulatedLines.length - // Replace all content up to the current line with accumulated lines + // Replace all content up to the current line with accumulated lines. const edit = new vscode.WorkspaceEdit() const rangeToReplace = new vscode.Range(0, 0, endLine + 1, 0) const contentToReplace = accumulatedLines.slice(0, endLine + 1).join("\n") + "\n" edit.replace(document.uri, rangeToReplace, this.stripAllBOMs(contentToReplace)) await vscode.workspace.applyEdit(edit) - // Update decorations + // Update decorations. this.activeLineController.setActiveLine(endLine) this.fadedOverlayController.updateOverlayAfterLine(endLine, document.lineCount) - // Scroll to the current line + // Scroll to the current line. this.scrollEditorToLine(endLine) - // Update the streamedLines with the new accumulated content + // Update the streamedLines with the new accumulated content. this.streamedLines = accumulatedLines + if (isFinal) { - // Handle any remaining lines if the new content is shorter than the original + // Handle any remaining lines if the new content is shorter than the + // original. if (this.streamedLines.length < document.lineCount) { const edit = new vscode.WorkspaceEdit() edit.delete(document.uri, new vscode.Range(this.streamedLines.length, 0, document.lineCount, 0)) await vscode.workspace.applyEdit(edit) } - // Preserve empty last line if original content had one + + // Preserve empty last line if original content had one. const hasEmptyLastLine = this.originalContent?.endsWith("\n") + if (hasEmptyLastLine && !accumulatedContent.endsWith("\n")) { accumulatedContent += "\n" } - // Apply the final content + + // Apply the final content. const finalEdit = new vscode.WorkspaceEdit() + finalEdit.replace( document.uri, new vscode.Range(0, 0, document.lineCount, 0), this.stripAllBOMs(accumulatedContent), ) + await vscode.workspace.applyEdit(finalEdit) - // Clear all decorations at the end (after applying final edit) + + // Clear all decorations at the end (after applying final edit). this.fadedOverlayController.clear() this.activeLineController.clear() } @@ -149,59 +176,68 @@ export class DiffViewProvider { if (!this.relPath || !this.newContent || !this.activeDiffEditor) { return { newProblemsMessage: undefined, userEdits: undefined, finalContent: undefined } } + const absolutePath = path.resolve(this.cwd, this.relPath) const updatedDocument = this.activeDiffEditor.document const editedContent = updatedDocument.getText() + if (updatedDocument.isDirty) { await updatedDocument.save() } - await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false }) + await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, preserveFocus: true }) await this.closeAllDiffViews() - /* - Getting diagnostics before and after the file edit is a better approach than - automatically tracking problems in real-time. This method ensures we only - report new problems that are a direct result of this specific edit. - Since these are new problems resulting from Agent's edit, we know they're - directly related to the work he's doing. This eliminates the risk of Roo - going off-task or getting distracted by unrelated issues, which was a problem - with the previous auto-debug approach. Some users' machines may be slow to - update diagnostics, so this approach provides a good balance between automation - and avoiding potential issues where Roo might get stuck in loops due to - outdated problem information. If no new problems show up by the time the user - accepts the changes, they can always debug later using the '@problems' mention. - This way, Roo only becomes aware of new problems resulting from his edits - and can address them accordingly. If problems don't change immediately after - applying a fix, won't be notified, which is generally fine since the - initial fix is usually correct and it may just take time for linters to catch up. - */ + // Getting diagnostics before and after the file edit is a better approach than + // automatically tracking problems in real-time. This method ensures we only + // report new problems that are a direct result of this specific edit. + // Since these are new problems resulting from Roo's edit, we know they're + // directly related to the work he's doing. This eliminates the risk of Roo + // going off-task or getting distracted by unrelated issues, which was a problem + // with the previous auto-debug approach. Some users' machines may be slow to + // update diagnostics, so this approach provides a good balance between automation + // and avoiding potential issues where Roo might get stuck in loops due to + // outdated problem information. If no new problems show up by the time the user + // accepts the changes, they can always debug later using the '@problems' mention. + // This way, Roo only becomes aware of new problems resulting from his edits + // and can address them accordingly. If problems don't change immediately after + // applying a fix, won't be notified, which is generally fine since the + // initial fix is usually correct and it may just take time for linters to catch up. const postDiagnostics = vscode.languages.getDiagnostics() + const newProblems = await diagnosticsToProblemsString( getNewDiagnostics(this.preDiagnostics, postDiagnostics), [ vscode.DiagnosticSeverity.Error, // only including errors since warnings can be distracting (if user wants to fix warnings they can use the @problems mention) ], this.cwd, - ) // will be empty string if no errors + ) // Will be empty string if no errors. + const newProblemsMessage = newProblems.length > 0 ? `\n\nNew problems detected after saving the file:\n${newProblems}` : "" - // If the edited content has different EOL characters, we don't want to show a diff with all the EOL differences. + // If the edited content has different EOL characters, we don't want to + // show a diff with all the EOL differences. const newContentEOL = this.newContent.includes("\r\n") ? "\r\n" : "\n" - const normalizedEditedContent = editedContent.replace(/\r\n|\n/g, newContentEOL).trimEnd() + newContentEOL // trimEnd to fix issue where editor adds in extra new line automatically - // just in case the new content has a mix of varying EOL characters + + // `trimEnd` to fix issue where editor adds in extra new line + // automatically. + const normalizedEditedContent = editedContent.replace(/\r\n|\n/g, newContentEOL).trimEnd() + newContentEOL + + // Just in case the new content has a mix of varying EOL characters. const normalizedNewContent = this.newContent.replace(/\r\n|\n/g, newContentEOL).trimEnd() + newContentEOL + if (normalizedEditedContent !== normalizedNewContent) { - // user made changes before approving edit + // User made changes before approving edit. const userEdits = formatResponse.createPrettyPatch( this.relPath.toPosix(), normalizedNewContent, normalizedEditedContent, ) + return { newProblemsMessage, userEdits, finalContent: normalizedEditedContent } } else { - // no changes to cline's edits + // No changes to Roo's edits. return { newProblemsMessage, userEdits: undefined, finalContent: normalizedEditedContent } } } @@ -210,42 +246,55 @@ export class DiffViewProvider { if (!this.relPath || !this.activeDiffEditor) { return } + const fileExists = this.editType === "modify" const updatedDocument = this.activeDiffEditor.document const absolutePath = path.resolve(this.cwd, this.relPath) + if (!fileExists) { if (updatedDocument.isDirty) { await updatedDocument.save() } + await this.closeAllDiffViews() await fs.unlink(absolutePath) - // Remove only the directories we created, in reverse order + + // Remove only the directories we created, in reverse order. for (let i = this.createdDirs.length - 1; i >= 0; i--) { await fs.rmdir(this.createdDirs[i]) console.log(`Directory ${this.createdDirs[i]} has been deleted.`) } + console.log(`File ${absolutePath} has been deleted.`) } else { - // revert document + // Revert document. const edit = new vscode.WorkspaceEdit() + const fullRange = new vscode.Range( updatedDocument.positionAt(0), updatedDocument.positionAt(updatedDocument.getText().length), ) + edit.replace(updatedDocument.uri, fullRange, this.originalContent ?? "") - // Apply the edit and save, since contents shouldnt have changed this wont show in local history unless of course the user made changes and saved during the edit + + // Apply the edit and save, since contents shouldnt have changed + // this won't show in local history unless of course the user made + // changes and saved during the edit. await vscode.workspace.applyEdit(edit) await updatedDocument.save() console.log(`File ${absolutePath} has been reverted to its original content.`) + if (this.documentWasOpen) { await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, + preserveFocus: true, }) } + await this.closeAllDiffViews() } - // edit is done + // Edit is done. await this.reset() } @@ -257,8 +306,9 @@ export class DiffViewProvider { tab.input instanceof vscode.TabInputTextDiff && tab.input?.original?.scheme === DIFF_VIEW_URI_SCHEME, ) + for (const tab of tabs) { - // trying to close dirty views results in save popup + // Trying to close dirty views results in save popup. if (!tab.isDirty) { await vscode.window.tabGroups.close(tab) } @@ -269,8 +319,12 @@ export class DiffViewProvider { if (!this.relPath) { throw new Error("No file path set") } + const uri = vscode.Uri.file(path.resolve(this.cwd, this.relPath)) - // If this diff editor is already open (ie if a previous write file was interrupted) then we should activate that instead of opening a new diff + + // If this diff editor is already open (ie if a previous write file was + // interrupted) then we should activate that instead of opening a new + // diff. const diffTab = vscode.window.tabGroups.all .flatMap((group) => group.tabs) .find( @@ -279,20 +333,24 @@ export class DiffViewProvider { tab.input?.original?.scheme === DIFF_VIEW_URI_SCHEME && arePathsEqual(tab.input.modified.fsPath, uri.fsPath), ) + if (diffTab && diffTab.input instanceof vscode.TabInputTextDiff) { - const editor = await vscode.window.showTextDocument(diffTab.input.modified) + const editor = await vscode.window.showTextDocument(diffTab.input.modified, { preserveFocus: true }) return editor } - // Open new diff editor + + // Open new diff editor. return new Promise((resolve, reject) => { const fileName = path.basename(uri.fsPath) const fileExists = this.editType === "modify" + const disposable = vscode.window.onDidChangeActiveTextEditor((editor) => { if (editor && arePathsEqual(editor.document.uri.fsPath, uri.fsPath)) { disposable.dispose() resolve(editor) } }) + vscode.commands.executeCommand( "vscode.diff", vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({ @@ -301,7 +359,8 @@ export class DiffViewProvider { uri, `${fileName}: ${fileExists ? "Original ↔ Agent's Changes" : "New File"} (Editable)`, ) - // This may happen on very slow machines ie project idx + + // This may happen on very slow machines i.e. project idx. setTimeout(() => { disposable.dispose() reject(new Error("Failed to open diff editor, please try again...")) @@ -312,6 +371,7 @@ export class DiffViewProvider { private scrollEditorToLine(line: number) { if (this.activeDiffEditor) { const scrollLine = line + 4 + this.activeDiffEditor.revealRange( new vscode.Range(scrollLine, 0, scrollLine, 0), vscode.TextEditorRevealType.InCenter, @@ -323,18 +383,23 @@ export class DiffViewProvider { if (!this.activeDiffEditor) { return } + const currentContent = this.activeDiffEditor.document.getText() const diffs = diff.diffLines(this.originalContent || "", currentContent) + let lineCount = 0 + for (const part of diffs) { if (part.added || part.removed) { - // Found the first diff, scroll to it + // Found the first diff, scroll to it. this.activeDiffEditor.revealRange( new vscode.Range(lineCount, 0, lineCount, 0), vscode.TextEditorRevealType.InCenter, ) + return } + if (!part.removed) { lineCount += part.count || 0 } @@ -344,15 +409,24 @@ export class DiffViewProvider { private stripAllBOMs(input: string): string { let result = input let previous + do { previous = result result = stripBom(result) } while (result !== previous) + return result } - // close editor if open? async reset() { + // Ensure any diff views opened by this provider are closed to release + // memory. + try { + await this.closeAllDiffViews() + } catch (error) { + console.error("Error closing diff views", error) + } + this.editType = undefined this.isEditing = false this.originalContent = undefined diff --git a/src/core/EditorUtils.ts b/src/integrations/editor/EditorUtils.ts similarity index 100% rename from src/core/EditorUtils.ts rename to src/integrations/editor/EditorUtils.ts diff --git a/src/core/__tests__/EditorUtils.test.ts b/src/integrations/editor/__tests__/EditorUtils.test.ts similarity index 98% rename from src/core/__tests__/EditorUtils.test.ts rename to src/integrations/editor/__tests__/EditorUtils.test.ts index 44b079fcd12..402e45a6e3c 100644 --- a/src/core/__tests__/EditorUtils.test.ts +++ b/src/integrations/editor/__tests__/EditorUtils.test.ts @@ -1,4 +1,4 @@ -// npx jest src/core/__tests__/EditorUtils.test.ts +// npx jest src/integrations/editor/__tests__/EditorUtils.test.ts import * as vscode from "vscode" diff --git a/src/core/__tests__/read-file-tool.test.ts b/src/integrations/misc/__tests__/read-file-tool.test.ts similarity index 84% rename from src/core/__tests__/read-file-tool.test.ts rename to src/integrations/misc/__tests__/read-file-tool.test.ts index 151b6df2bc9..12f4560f9c2 100644 --- a/src/core/__tests__/read-file-tool.test.ts +++ b/src/integrations/misc/__tests__/read-file-tool.test.ts @@ -1,21 +1,21 @@ -// npx jest src/core/__tests__/read-file-tool.test.ts +// npx jest src/integrations/misc/__tests__/read-file-tool.test.ts import * as path from "path" -import { countFileLines } from "../../integrations/misc/line-counter" -import { readLines } from "../../integrations/misc/read-lines" -import { extractTextFromFile, addLineNumbers } from "../../integrations/misc/extract-text" +import { countFileLines } from "../line-counter" +import { readLines } from "../read-lines" +import { extractTextFromFile, addLineNumbers } from "../extract-text" // Mock the required functions -jest.mock("../../integrations/misc/line-counter") -jest.mock("../../integrations/misc/read-lines") -jest.mock("../../integrations/misc/extract-text") +jest.mock("../line-counter") +jest.mock("../read-lines") +jest.mock("../extract-text") describe("read_file tool with maxReadFileLine setting", () => { // Mock original implementation first to use in tests - const originalCountFileLines = jest.requireActual("../../integrations/misc/line-counter").countFileLines - const originalReadLines = jest.requireActual("../../integrations/misc/read-lines").readLines - const originalExtractTextFromFile = jest.requireActual("../../integrations/misc/extract-text").extractTextFromFile - const originalAddLineNumbers = jest.requireActual("../../integrations/misc/extract-text").addLineNumbers + const originalCountFileLines = jest.requireActual("../line-counter").countFileLines + const originalReadLines = jest.requireActual("../read-lines").readLines + const originalExtractTextFromFile = jest.requireActual("../extract-text").extractTextFromFile + const originalAddLineNumbers = jest.requireActual("../extract-text").addLineNumbers beforeEach(() => { jest.resetAllMocks() diff --git a/src/integrations/misc/open-file.ts b/src/integrations/misc/open-file.ts index b3724068b62..52352314b3e 100644 --- a/src/integrations/misc/open-file.ts +++ b/src/integrations/misc/open-file.ts @@ -23,6 +23,7 @@ export async function openImage(dataUri: string) { interface OpenFileOptions { create?: boolean content?: string + line?: number } export async function openFile(filePath: string, options: OpenFileOptions = {}) { @@ -75,7 +76,12 @@ export async function openFile(filePath: string, options: OpenFileOptions = {}) } catch {} // not essential, sometimes tab operations fail const document = await vscode.workspace.openTextDocument(uri) - await vscode.window.showTextDocument(document, { preview: false }) + const selection = + options.line !== undefined ? new vscode.Selection(options.line - 1, 0, options.line - 1, 0) : undefined + await vscode.window.showTextDocument(document, { + preview: false, + selection, + }) } catch (error) { if (error instanceof Error) { vscode.window.showErrorMessage(`Could not open file: ${error.message}`) diff --git a/src/integrations/terminal/__tests__/ExecaTerminal.spec.ts b/src/integrations/terminal/__tests__/ExecaTerminal.spec.ts new file mode 100644 index 00000000000..314238a94ad --- /dev/null +++ b/src/integrations/terminal/__tests__/ExecaTerminal.spec.ts @@ -0,0 +1,35 @@ +// npx vitest run src/integrations/terminal/__tests__/ExecaTerminal.spec.ts + +import { vi, describe, it, expect } from "vitest" + +import { RooTerminalCallbacks } from "../types" +import { ExecaTerminal } from "../ExecaTerminal" + +describe("ExecaTerminal", () => { + it("should run terminal commands and collect output", async () => { + // TODO: Run the equivalent test for Windows. + if (process.platform === "win32") { + return + } + + const terminal = new ExecaTerminal(1, "/tmp") + let result + + const callbacks: RooTerminalCallbacks = { + onLine: vi.fn(), + onCompleted: (output) => (result = output), + onShellExecutionStarted: vi.fn(), + onShellExecutionComplete: vi.fn(), + } + + const subprocess = terminal.runCommand("ls -al", callbacks) + await subprocess + + expect(callbacks.onLine).toHaveBeenCalled() + expect(callbacks.onShellExecutionStarted).toHaveBeenCalled() + expect(callbacks.onShellExecutionComplete).toHaveBeenCalled() + + expect(result).toBeTypeOf("string") + expect(result).toContain("total") + }) +}) diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 9b8175053b8..cc6cdc74374 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -30,6 +30,9 @@ export const providerNames = [ "fake-ai", "pearai", "xai", + "groq", + "chutes", + "litellm", ] as const export const providerNamesSchema = z.enum(providerNames) @@ -60,6 +63,7 @@ export const languages = [ "it", "ja", "ko", + "nl", "pl", "pt-BR", "ru", @@ -106,7 +110,6 @@ export const modelInfoSchema = z.object({ supportsImages: z.boolean().optional(), supportsComputerUse: z.boolean().optional(), supportsPromptCache: z.boolean(), - isPromptCacheOptional: z.boolean().optional(), inputPrice: z.number().optional(), outputPrice: z.number().optional(), cacheWritesPrice: z.number().optional(), @@ -133,18 +136,6 @@ export const modelInfoSchema = z.object({ export type ModelInfo = z.infer -/** - * ApiConfigMeta - */ - -export const apiConfigMetaSchema = z.object({ - id: z.string(), - name: z.string(), - apiProvider: providerNamesSchema.optional(), -}) - -export type ApiConfigMeta = z.infer - /** * HistoryItem */ @@ -212,6 +203,7 @@ export const modeConfigSchema = z.object({ slug: z.string().regex(/^[a-zA-Z0-9-]+$/, "Slug must contain only letters numbers and dashes"), name: z.string().min(1, "Name is required"), roleDefinition: z.string().min(1, "Role definition is required"), + whenToUse: z.string().optional(), customInstructions: z.string().optional(), groups: groupEntryArraySchema, source: z.enum(["global", "project"]).optional(), @@ -252,6 +244,7 @@ export type CustomModesSettings = z.infer export const promptComponentSchema = z.object({ roleDefinition: z.string().optional(), + whenToUse: z.string().optional(), customInstructions: z.string().optional(), }) @@ -306,7 +299,7 @@ export type CommandExecutionStatus = z.infer */ const experimentsSchema = z.object({ + autoCondenseContext: z.boolean(), powerSteering: z.boolean(), }) @@ -354,42 +348,77 @@ export const historyItemSchema = z.object({ export type HistoryItem = z.infer /** - * ProviderSettings + * ProviderSettingsEntry */ -export const providerSettingsSchema = z.object({ +export const providerSettingsEntrySchema = z.object({ + id: z.string(), + name: z.string(), apiProvider: providerNamesSchema.optional(), - // Anthropic +}) + +export type ProviderSettingsEntry = z.infer + +/** + * ProviderSettings + */ + +const baseProviderSettingsSchema = z.object({ + includeMaxTokens: z.boolean().optional(), + reasoningEffort: reasoningEffortsSchema.optional(), + diffEnabled: z.boolean().optional(), + fuzzyMatchThreshold: z.number().optional(), + modelTemperature: z.number().nullish(), + rateLimitSeconds: z.number().optional(), + // Claude 3.7 Sonnet Thinking + modelMaxTokens: z.number().optional(), + modelMaxThinkingTokens: z.number().optional(), +}) + +// Several of the providers share common model config properties. +const apiModelIdProviderModelSchema = baseProviderSettingsSchema.extend({ apiModelId: z.string().optional(), +}) + +const anthropicSchema = apiModelIdProviderModelSchema.extend({ apiKey: z.string().optional(), anthropicBaseUrl: z.string().optional(), anthropicUseAuthToken: z.boolean().optional(), - // Glama +}) + +const glamaSchema = baseProviderSettingsSchema.extend({ glamaModelId: z.string().optional(), glamaApiKey: z.string().optional(), - // OpenRouter +}) + +const openRouterSchema = baseProviderSettingsSchema.extend({ openRouterApiKey: z.string().optional(), openRouterModelId: z.string().optional(), openRouterBaseUrl: z.string().optional(), openRouterSpecificProvider: z.string().optional(), openRouterUseMiddleOutTransform: z.boolean().optional(), - // Amazon Bedrock +}) + +const bedrockSchema = apiModelIdProviderModelSchema.extend({ awsAccessKey: z.string().optional(), awsSecretKey: z.string().optional(), awsSessionToken: z.string().optional(), awsRegion: z.string().optional(), awsUseCrossRegionInference: z.boolean().optional(), awsUsePromptCache: z.boolean().optional(), - awspromptCacheId: z.string().optional(), awsProfile: z.string().optional(), awsUseProfile: z.boolean().optional(), awsCustomArn: z.string().optional(), - // Google Vertex +}) + +const vertexSchema = apiModelIdProviderModelSchema.extend({ vertexKeyFile: z.string().optional(), vertexJsonCredentials: z.string().optional(), vertexProjectId: z.string().optional(), vertexRegion: z.string().optional(), - // OpenAI +}) + +const openAiSchema = baseProviderSettingsSchema.extend({ openAiBaseUrl: z.string().optional(), openAiApiKey: z.string().optional(), openAiLegacyFormat: z.boolean().optional(), @@ -400,12 +429,16 @@ export const providerSettingsSchema = z.object({ azureApiVersion: z.string().optional(), openAiStreamingEnabled: z.boolean().optional(), enableReasoningEffort: z.boolean().optional(), - openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration + openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. openAiHeaders: z.record(z.string(), z.string()).optional(), - // Ollama +}) + +const ollamaSchema = baseProviderSettingsSchema.extend({ ollamaModelId: z.string().optional(), ollamaBaseUrl: z.string().optional(), - // VS Code LM +}) + +const vsCodeLmSchema = baseProviderSettingsSchema.extend({ vsCodeLmModelSelector: z .object({ vendor: z.string().optional(), @@ -414,43 +447,48 @@ export const providerSettingsSchema = z.object({ id: z.string().optional(), }) .optional(), - // LM Studio +}) + +const lmStudioSchema = baseProviderSettingsSchema.extend({ lmStudioModelId: z.string().optional(), lmStudioBaseUrl: z.string().optional(), lmStudioDraftModelId: z.string().optional(), lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), - // Gemini +}) + +const geminiSchema = apiModelIdProviderModelSchema.extend({ geminiApiKey: z.string().optional(), googleGeminiBaseUrl: z.string().optional(), - // OpenAI Native +}) + +const openAiNativeSchema = apiModelIdProviderModelSchema.extend({ openAiNativeApiKey: z.string().optional(), openAiNativeBaseUrl: z.string().optional(), - // Mistral +}) + +const mistralSchema = apiModelIdProviderModelSchema.extend({ mistralApiKey: z.string().optional(), mistralCodestralUrl: z.string().optional(), - // DeepSeek +}) + +const deepSeekSchema = apiModelIdProviderModelSchema.extend({ deepSeekBaseUrl: z.string().optional(), deepSeekApiKey: z.string().optional(), - // Unbound +}) + +const unboundSchema = baseProviderSettingsSchema.extend({ unboundApiKey: z.string().optional(), unboundModelId: z.string().optional(), - // Requesty +}) + +const requestySchema = baseProviderSettingsSchema.extend({ requestyApiKey: z.string().optional(), requestyModelId: z.string().optional(), - // X.AI (Grok) - xaiApiKey: z.string().optional(), - // Claude 3.7 Sonnet Thinking - modelMaxTokens: z.number().optional(), - modelMaxThinkingTokens: z.number().optional(), - // Generic - includeMaxTokens: z.boolean().optional(), - reasoningEffort: reasoningEffortsSchema.optional(), - promptCachingEnabled: z.boolean().optional(), - diffEnabled: z.boolean().optional(), - fuzzyMatchThreshold: z.number().optional(), - modelTemperature: z.number().nullish(), - rateLimitSeconds: z.number().optional(), - // Fake AI +}) + +const humanRelaySchema = baseProviderSettingsSchema + +const fakeAiSchema = baseProviderSettingsSchema.extend({ fakeAi: z.unknown().optional(), // PearAI pearaiBaseUrl: z.string().optional(), @@ -466,6 +504,79 @@ export const providerSettingsSchema = z.object({ creatorModeConfig: creatorModeConfigSchema.optional(), }) +const xaiSchema = apiModelIdProviderModelSchema.extend({ + xaiApiKey: z.string().optional(), +}) + +const groqSchema = apiModelIdProviderModelSchema.extend({ + groqApiKey: z.string().optional(), +}) + +const chutesSchema = apiModelIdProviderModelSchema.extend({ + chutesApiKey: z.string().optional(), +}) + +const litellmSchema = baseProviderSettingsSchema.extend({ + litellmBaseUrl: z.string().optional(), + litellmApiKey: z.string().optional(), + litellmModelId: z.string().optional(), +}) + +const defaultSchema = z.object({ + apiProvider: z.undefined(), +}) + +export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProvider", [ + anthropicSchema.merge(z.object({ apiProvider: z.literal("anthropic") })), + glamaSchema.merge(z.object({ apiProvider: z.literal("glama") })), + openRouterSchema.merge(z.object({ apiProvider: z.literal("openrouter") })), + bedrockSchema.merge(z.object({ apiProvider: z.literal("bedrock") })), + vertexSchema.merge(z.object({ apiProvider: z.literal("vertex") })), + openAiSchema.merge(z.object({ apiProvider: z.literal("openai") })), + ollamaSchema.merge(z.object({ apiProvider: z.literal("ollama") })), + vsCodeLmSchema.merge(z.object({ apiProvider: z.literal("vscode-lm") })), + lmStudioSchema.merge(z.object({ apiProvider: z.literal("lmstudio") })), + geminiSchema.merge(z.object({ apiProvider: z.literal("gemini") })), + openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })), + mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })), + deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })), + unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })), + requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })), + humanRelaySchema.merge(z.object({ apiProvider: z.literal("human-relay") })), + fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })), + xaiSchema.merge(z.object({ apiProvider: z.literal("xai") })), + groqSchema.merge(z.object({ apiProvider: z.literal("groq") })), + chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })), + litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })), + defaultSchema, +]) + +export const providerSettingsSchema = z + .object({ + apiProvider: providerNamesSchema.optional(), + }) + .merge(anthropicSchema) + .merge(glamaSchema) + .merge(openRouterSchema) + .merge(bedrockSchema) + .merge(vertexSchema) + .merge(openAiSchema) + .merge(ollamaSchema) + .merge(vsCodeLmSchema) + .merge(lmStudioSchema) + .merge(geminiSchema) + .merge(openAiNativeSchema) + .merge(mistralSchema) + .merge(deepSeekSchema) + .merge(unboundSchema) + .merge(requestySchema) + .merge(humanRelaySchema) + .merge(fakeAiSchema) + .merge(xaiSchema) + .merge(groqSchema) + .merge(chutesSchema) + .merge(litellmSchema) + export type ProviderSettings = z.infer type ProviderSettingsRecord = Record, undefined> @@ -493,7 +604,6 @@ const providerSettingsRecord: ProviderSettingsRecord = { awsRegion: undefined, awsUseCrossRegionInference: undefined, awsUsePromptCache: undefined, - awspromptCacheId: undefined, awsProfile: undefined, awsUseProfile: undefined, awsCustomArn: undefined, @@ -548,7 +658,6 @@ const providerSettingsRecord: ProviderSettingsRecord = { // Generic includeMaxTokens: undefined, reasoningEffort: undefined, - promptCachingEnabled: undefined, diffEnabled: undefined, fuzzyMatchThreshold: undefined, modelTemperature: undefined, @@ -564,6 +673,14 @@ const providerSettingsRecord: ProviderSettingsRecord = { creatorModeConfig: undefined, // X.AI (Grok) xaiApiKey: undefined, + // Groq + groqApiKey: undefined, + // Chutes AI + chutesApiKey: undefined, + // LiteLLM + litellmBaseUrl: undefined, + litellmApiKey: undefined, + litellmModelId: undefined, } export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Keys[] @@ -574,7 +691,7 @@ export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Key export const globalSettingsSchema = z.object({ currentApiConfigName: z.string().optional(), - listApiConfigMeta: z.array(apiConfigMetaSchema).optional(), + listApiConfigMeta: z.array(providerSettingsEntrySchema).optional(), pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(), lastShownAnnouncementId: z.string().optional(), @@ -757,6 +874,9 @@ export type SecretState = Pick< | "requestyApiKey" | "pearaiApiKey" | "xaiApiKey" + | "groqApiKey" + | "chutesApiKey" + | "litellmApiKey" > type SecretStateRecord = Record, undefined> @@ -777,6 +897,9 @@ const secretStateRecord: SecretStateRecord = { requestyApiKey: undefined, pearaiApiKey: undefined, xaiApiKey: undefined, + groqApiKey: undefined, + chutesApiKey: undefined, + litellmApiKey: undefined, } export const SECRET_STATE_KEYS = Object.keys(secretStateRecord) as Keys[] @@ -854,7 +977,6 @@ export type ClineSay = z.infer */ export const toolProgressStatusSchema = z.object({ - id: z.string().optional(), icon: z.string().optional(), text: z.string().optional(), }) @@ -980,6 +1102,142 @@ export const rooCodeEventsSchema = z.object({ export type RooCodeEvents = z.infer +/** + * Ack + */ + +export const ackSchema = z.object({ + clientId: z.string(), + pid: z.number(), + ppid: z.number(), +}) + +export type Ack = z.infer + +/** + * TaskCommand + */ + +export enum TaskCommandName { + StartNewTask = "StartNewTask", + CancelTask = "CancelTask", + CloseTask = "CloseTask", +} + +export const taskCommandSchema = z.discriminatedUnion("commandName", [ + z.object({ + commandName: z.literal(TaskCommandName.StartNewTask), + data: z.object({ + configuration: rooCodeSettingsSchema, + text: z.string(), + images: z.array(z.string()).optional(), + newTab: z.boolean().optional(), + }), + }), + z.object({ + commandName: z.literal(TaskCommandName.CancelTask), + data: z.string(), + }), + z.object({ + commandName: z.literal(TaskCommandName.CloseTask), + data: z.string(), + }), +]) + +export type TaskCommand = z.infer + +/** + * TaskEvent + */ + +export const taskEventSchema = z.discriminatedUnion("eventName", [ + z.object({ + eventName: z.literal(RooCodeEventName.Message), + payload: rooCodeEventsSchema.shape[RooCodeEventName.Message], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskCreated), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCreated], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskStarted), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskStarted], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskModeSwitched), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskModeSwitched], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskPaused), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskPaused], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskUnpaused), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskUnpaused], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskAskResponded), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAskResponded], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskAborted), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAborted], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskSpawned), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskSpawned], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskCompleted), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCompleted], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskTokenUsageUpdated), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskTokenUsageUpdated], + }), +]) + +export type TaskEvent = z.infer + +/** + * IpcMessage + */ + +export enum IpcMessageType { + Connect = "Connect", + Disconnect = "Disconnect", + Ack = "Ack", + TaskCommand = "TaskCommand", + TaskEvent = "TaskEvent", +} + +export enum IpcOrigin { + Client = "client", + Server = "server", +} + +export const ipcMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal(IpcMessageType.Ack), + origin: z.literal(IpcOrigin.Server), + data: ackSchema, + }), + z.object({ + type: z.literal(IpcMessageType.TaskCommand), + origin: z.literal(IpcOrigin.Client), + clientId: z.string(), + data: taskCommandSchema, + }), + z.object({ + type: z.literal(IpcMessageType.TaskEvent), + origin: z.literal(IpcOrigin.Server), + relayClientId: z.string().optional(), + data: taskEventSchema, + }), +]) + +export type IpcMessage = z.infer + /** * TypeDefinition */ @@ -990,12 +1248,16 @@ export type TypeDefinition = { } export const typeDefinitions: TypeDefinition[] = [ - { schema: providerSettingsSchema, identifier: "ProviderSettings" }, { schema: globalSettingsSchema, identifier: "GlobalSettings" }, + { schema: providerSettingsSchema, identifier: "ProviderSettings" }, + { schema: providerSettingsEntrySchema, identifier: "ProviderSettingsEntry" }, { schema: clineMessageSchema, identifier: "ClineMessage" }, { schema: tokenUsageSchema, identifier: "TokenUsage" }, { schema: rooCodeEventsSchema, identifier: "RooCodeEvents" }, + { schema: ipcMessageSchema, identifier: "IpcMessage" }, + { schema: taskCommandSchema, identifier: "TaskCommand" }, + { schema: taskEventSchema, identifier: "TaskEvent" }, ] -// Also export as default for ESM compatibility +// Also export as default for ESM compatibility. export default { typeDefinitions } diff --git a/src/schemas/ipc.ts b/src/schemas/ipc.ts deleted file mode 100644 index 08ab0c974dd..00000000000 --- a/src/schemas/ipc.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { z } from "zod" - -import { RooCodeEventName, rooCodeEventsSchema, rooCodeSettingsSchema } from "./index" - -/** - * Ack - */ - -export const ackSchema = z.object({ - clientId: z.string(), - pid: z.number(), - ppid: z.number(), -}) - -export type Ack = z.infer - -/** - * TaskCommand - */ - -export enum TaskCommandName { - StartNewTask = "StartNewTask", - CancelTask = "CancelTask", - CloseTask = "CloseTask", -} - -export const taskCommandSchema = z.discriminatedUnion("commandName", [ - z.object({ - commandName: z.literal(TaskCommandName.StartNewTask), - data: z.object({ - configuration: rooCodeSettingsSchema, - text: z.string(), - images: z.array(z.string()).optional(), - newTab: z.boolean().optional(), - }), - }), - z.object({ - commandName: z.literal(TaskCommandName.CancelTask), - data: z.string(), - }), - z.object({ - commandName: z.literal(TaskCommandName.CloseTask), - data: z.string(), - }), -]) - -export type TaskCommand = z.infer - -/** - * TaskEvent - */ - -export const taskEventSchema = z.discriminatedUnion("eventName", [ - z.object({ - eventName: z.literal(RooCodeEventName.Message), - payload: rooCodeEventsSchema.shape[RooCodeEventName.Message], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskCreated), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCreated], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskStarted), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskStarted], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskModeSwitched), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskModeSwitched], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskPaused), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskPaused], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskUnpaused), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskUnpaused], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskAskResponded), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAskResponded], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskAborted), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAborted], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskSpawned), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskSpawned], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskCompleted), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCompleted], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskTokenUsageUpdated), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskTokenUsageUpdated], - }), -]) - -export type TaskEvent = z.infer - -/** - * IpcMessage - */ - -export enum IpcMessageType { - Connect = "Connect", - Disconnect = "Disconnect", - Ack = "Ack", - TaskCommand = "TaskCommand", - TaskEvent = "TaskEvent", -} - -export enum IpcOrigin { - Client = "client", - Server = "server", -} - -export const ipcMessageSchema = z.discriminatedUnion("type", [ - z.object({ - type: z.literal(IpcMessageType.Ack), - origin: z.literal(IpcOrigin.Server), - data: ackSchema, - }), - z.object({ - type: z.literal(IpcMessageType.TaskCommand), - origin: z.literal(IpcOrigin.Client), - clientId: z.string(), - data: taskCommandSchema, - }), - z.object({ - type: z.literal(IpcMessageType.TaskEvent), - origin: z.literal(IpcOrigin.Server), - relayClientId: z.string().optional(), - data: taskEventSchema, - }), -]) - -export type IpcMessage = z.infer diff --git a/src/services/glob/list-files.ts b/src/services/glob/list-files.ts index 6d30930ecb5..e1809ba4e82 100644 --- a/src/services/glob/list-files.ts +++ b/src/services/glob/list-files.ts @@ -40,6 +40,7 @@ const DIRS_TO_IGNORE = [ export async function listFiles(dirPath: string, recursive: boolean, limit: number): Promise<[string[], boolean]> { // Handle special directories const specialResult = await handleSpecialDirectories(dirPath) + if (specialResult) { return specialResult } diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index bb6ab004b3a..f849a10fa27 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -634,6 +634,7 @@ export class McpHub { disabled: config.disabled, source, projectPath: source === "project" ? vscode.workspace.workspaceFolders?.[0]?.uri.fsPath : undefined, + errorHistory: [], }, client, transport, @@ -660,13 +661,31 @@ export class McpHub { } } - private appendErrorMessage(connection: McpConnection, error: string) { + private appendErrorMessage(connection: McpConnection, error: string, level: "error" | "warn" | "info" = "error") { const MAX_ERROR_LENGTH = 1000 - const newError = connection.server.error ? `${connection.server.error}\n${error}` : error - connection.server.error = - newError.length > MAX_ERROR_LENGTH - ? `${newError.substring(0, MAX_ERROR_LENGTH)}...(error message truncated)` - : newError + const truncatedError = + error.length > MAX_ERROR_LENGTH + ? `${error.substring(0, MAX_ERROR_LENGTH)}...(error message truncated)` + : error + + // Add to error history + if (!connection.server.errorHistory) { + connection.server.errorHistory = [] + } + + connection.server.errorHistory.push({ + message: truncatedError, + timestamp: Date.now(), + level, + }) + + // Keep only the last 100 errors + if (connection.server.errorHistory.length > 100) { + connection.server.errorHistory = connection.server.errorHistory.slice(-100) + } + + // Update current error display + connection.server.error = truncatedError } /** diff --git a/src/services/tree-sitter/languageParser.ts b/src/services/tree-sitter/languageParser.ts index 28150addc2f..5e75fd3255c 100644 --- a/src/services/tree-sitter/languageParser.ts +++ b/src/services/tree-sitter/languageParser.ts @@ -27,6 +27,7 @@ import { zigQuery, embeddedTemplateQuery, elispQuery, + elixirQuery, } from "./queries" export interface LanguageParser { @@ -196,6 +197,11 @@ export async function loadRequiredLanguageParsers(filesToParse: string[]): Promi language = await loadLanguage("elisp") query = language.query(elispQuery) break + case "ex": + case "exs": + language = await loadLanguage("elixir") + query = language.query(elixirQuery) + break default: throw new Error(`Unsupported language: ${ext}`) } diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index e6f0c1297c8..c71b1e8beac 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -2,8 +2,8 @@ import { GitCommit } from "../utils/git" import { GlobalSettings, - ApiConfigMeta, - ProviderSettings as ApiConfiguration, + ProviderSettingsEntry, + ProviderSettings, HistoryItem, ModeConfig, TelemetrySetting, @@ -17,7 +17,7 @@ import { McpServer } from "./mcp" import { Mode } from "./modes" import { RouterModels } from "./api" -export type { ApiConfigMeta, ToolProgressStatus } +export type { ProviderSettingsEntry, ToolProgressStatus } export interface LanguageModelChatSelector { vendor?: string @@ -69,6 +69,7 @@ export interface ExtensionMessage { | "setHistoryPreviewCollapsed" | "commandExecutionStatus" | "creatorModeUpdate" + | "vsCodeSetting" text?: string action?: | "chatButtonClicked" @@ -96,7 +97,7 @@ export interface ExtensionMessage { vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[] mcpServers?: McpServer[] commits?: GitCommit[] - listApiConfig?: ApiConfigMeta[] + listApiConfig?: ProviderSettingsEntry[] mode?: Mode customMode?: ModeConfig slug?: string @@ -106,6 +107,8 @@ export interface ExtensionMessage { promptText?: string results?: { path: string; type: "file" | "folder"; label?: string }[] error?: string + setting?: string + value?: any } export type ExtensionState = Pick< @@ -171,7 +174,7 @@ export type ExtensionState = Pick< version: string clineMessages: ClineMessage[] currentTaskItem?: HistoryItem - apiConfiguration?: ApiConfiguration + apiConfiguration?: ProviderSettings uriScheme?: string shouldShowAnnouncement: boolean diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 60bd0a5daa9..3651c17c24c 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -1,6 +1,6 @@ import { z } from "zod" -import { ApiConfiguration } from "./api" +import { ProviderSettings } from "./api" import { Mode, PromptComponent, ModeConfig } from "./modes" export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse" @@ -11,7 +11,6 @@ export type AudioType = "notification" | "celebration" | "progress_loop" export interface WebviewMessage { type: - | "apiConfiguration" | "deleteMultipleTasksWithIds" | "currentApiConfigName" | "saveApiConfiguration" @@ -42,6 +41,7 @@ export interface WebviewMessage { | "importSettings" | "exportSettings" | "resetState" + | "flushRouterModels" | "requestRouterModels" | "requestOpenAiModels" | "requestOllamaModels" @@ -51,6 +51,9 @@ export interface WebviewMessage { | "openFile" | "openMention" | "cancelTask" + | "updateVSCodeSetting" + | "getVSCodeSetting" + | "vsCodeSetting" | "alwaysAllowBrowser" | "alwaysAllowMcp" | "alwaysAllowModeSwitch" @@ -132,7 +135,7 @@ export interface WebviewMessage { text?: string disabled?: boolean askResponse?: ClineAskResponse - apiConfiguration?: ApiConfiguration + apiConfiguration?: ProviderSettings images?: string[] bool?: boolean value?: number @@ -147,6 +150,7 @@ export interface WebviewMessage { dataUrls?: string[] values?: Record query?: string + setting?: string slug?: string modeConfig?: ModeConfig timeout?: number diff --git a/src/shared/__tests__/checkExistApiConfig.test.ts b/src/shared/__tests__/checkExistApiConfig.test.ts index c99ddddbc45..a8e603db8ac 100644 --- a/src/shared/__tests__/checkExistApiConfig.test.ts +++ b/src/shared/__tests__/checkExistApiConfig.test.ts @@ -1,5 +1,5 @@ import { checkExistKey } from "../checkExistApiConfig" -import { ApiConfiguration } from "../api" +import { ProviderSettings } from "../api" describe("checkExistKey", () => { it("should return false for undefined config", () => { @@ -7,19 +7,19 @@ describe("checkExistKey", () => { }) it("should return false for empty config", () => { - const config: ApiConfiguration = {} + const config: ProviderSettings = {} expect(checkExistKey(config)).toBe(false) }) it("should return true when one key is defined", () => { - const config: ApiConfiguration = { + const config: ProviderSettings = { apiKey: "test-key", } expect(checkExistKey(config)).toBe(true) }) it("should return true when multiple keys are defined", () => { - const config: ApiConfiguration = { + const config: ProviderSettings = { apiKey: "test-key", glamaApiKey: "glama-key", openRouterApiKey: "openrouter-key", @@ -28,7 +28,7 @@ describe("checkExistKey", () => { }) it("should return true when only non-key fields are undefined", () => { - const config: ApiConfiguration = { + const config: ProviderSettings = { apiKey: "test-key", apiProvider: undefined, anthropicBaseUrl: undefined, @@ -38,7 +38,7 @@ describe("checkExistKey", () => { }) it("should return false when all key fields are undefined", () => { - const config: ApiConfiguration = { + const config: ProviderSettings = { apiKey: undefined, glamaApiKey: undefined, openRouterApiKey: undefined, diff --git a/src/shared/__tests__/experiments.test.ts b/src/shared/__tests__/experiments.test.ts index b68de1ced12..9d0e6dab9c6 100644 --- a/src/shared/__tests__/experiments.test.ts +++ b/src/shared/__tests__/experiments.test.ts @@ -10,17 +10,28 @@ describe("experiments", () => { }) }) + describe("AUTO_CONDENSE_CONTEXT", () => { + it("is configured correctly", () => { + expect(EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT).toBe("autoCondenseContext") + expect(experimentConfigsMap.AUTO_CONDENSE_CONTEXT).toMatchObject({ + enabled: false, + }) + }) + }) + describe("isEnabled", () => { - it("returns false when experiment is not enabled", () => { + it("returns false when POWER_STEERING experiment is not enabled", () => { const experiments: Record = { powerSteering: false, + autoCondenseContext: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) - it("returns true when experiment is enabled", () => { + it("returns true when experiment POWER_STEERING is enabled", () => { const experiments: Record = { powerSteering: true, + autoCondenseContext: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true) }) @@ -28,8 +39,25 @@ describe("experiments", () => { it("returns false when experiment is not present", () => { const experiments: Record = { powerSteering: false, + autoCondenseContext: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) + + it("returns false when AUTO_CONDENSE_CONTEXT experiment is not enabled", () => { + const experiments: Record = { + powerSteering: false, + autoCondenseContext: false, + } + expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT)).toBe(false) + }) + + it("returns true when AUTO_CONDENSE_CONTEXT experiment is enabled", () => { + const experiments: Record = { + powerSteering: false, + autoCondenseContext: true, + } + expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT)).toBe(true) + }) }) }) diff --git a/src/shared/api.ts b/src/shared/api.ts index 9cfc1290d8c..d12b3baddcb 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -1,11 +1,9 @@ import { ModelInfo, ProviderName, ProviderSettings } from "../schemas" -export type { ModelInfo, ProviderName as ApiProvider } +export type { ModelInfo, ProviderName, ProviderSettings } export type ApiHandlerOptions = Omit -export type ApiConfiguration = ProviderSettings - // Anthropic // https://docs.anthropic.com/en/docs/about-claude/models export type AnthropicModelId = keyof typeof anthropicModels @@ -437,7 +435,7 @@ export const glamaDefaultModelInfo: ModelInfo = { // Requesty // https://requesty.ai/router-2 -export const requestyDefaultModelId = "anthropic/claude-3-7-sonnet-latest" +export const requestyDefaultModelId = "coding/claude-3-7-sonnet" export const requestyDefaultModelInfo: ModelInfo = { maxTokens: 8192, contextWindow: 200_000, @@ -449,7 +447,7 @@ export const requestyDefaultModelInfo: ModelInfo = { cacheWritesPrice: 3.75, cacheReadsPrice: 0.3, description: - "Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)", + "The best coding model, optimized by Requesty, and automatically routed to the fastest provider. Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)", } // OpenRouter @@ -498,7 +496,14 @@ export const vertexModels = { contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: true, - isPromptCacheOptional: true, + inputPrice: 2.5, + outputPrice: 15, + }, + "gemini-2.5-pro-preview-05-06": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, inputPrice: 2.5, outputPrice: 15, }, @@ -523,7 +528,6 @@ export const vertexModels = { contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: true, - isPromptCacheOptional: true, inputPrice: 0.15, outputPrice: 0.6, }, @@ -548,7 +552,6 @@ export const vertexModels = { contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: true, - isPromptCacheOptional: true, inputPrice: 0.075, outputPrice: 0.3, }, @@ -683,7 +686,30 @@ export const geminiModels = { contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: true, - isPromptCacheOptional: true, + inputPrice: 2.5, // This is the pricing for prompts above 200k tokens. + outputPrice: 15, + cacheReadsPrice: 0.625, + cacheWritesPrice: 4.5, + tiers: [ + { + contextWindow: 200_000, + inputPrice: 1.25, + outputPrice: 10, + cacheReadsPrice: 0.31, + }, + { + contextWindow: Infinity, + inputPrice: 2.5, + outputPrice: 15, + cacheReadsPrice: 0.625, + }, + ], + }, + "gemini-2.5-pro-preview-05-06": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, inputPrice: 2.5, // This is the pricing for prompts above 200k tokens. outputPrice: 15, cacheReadsPrice: 0.625, @@ -708,7 +734,6 @@ export const geminiModels = { contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: true, - isPromptCacheOptional: true, inputPrice: 0.1, outputPrice: 0.4, cacheReadsPrice: 0.025, @@ -767,7 +792,6 @@ export const geminiModels = { contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: true, - isPromptCacheOptional: true, inputPrice: 0.15, // This is the pricing for prompts above 128k tokens. outputPrice: 0.6, cacheReadsPrice: 0.0375, @@ -1110,6 +1134,20 @@ export const unboundDefaultModelInfo: ModelInfo = { cacheReadsPrice: 0.3, } +// LiteLLM +// https://docs.litellm.ai/ +export const litellmDefaultModelId = "anthropic/claude-3-7-sonnet-20250219" +export const litellmDefaultModelInfo: ModelInfo = { + maxTokens: 8192, + contextWindow: 200_000, + supportsImages: true, + supportsComputerUse: true, + supportsPromptCache: true, + inputPrice: 3.0, + outputPrice: 15.0, + cacheWritesPrice: 3.75, + cacheReadsPrice: 0.3, +} // xAI // https://docs.x.ai/docs/api-reference export type XAIModelId = keyof typeof xaiModels @@ -1408,6 +1446,251 @@ export const vscodeLlmModels = { } > +// Groq +// https://console.groq.com/docs/models +export type GroqModelId = + | "llama-3.1-8b-instant" + | "llama-3.3-70b-versatile" + | "meta-llama/llama-4-scout-17b-16e-instruct" + | "meta-llama/llama-4-maverick-17b-128e-instruct" + | "mistral-saba-24b" + | "qwen-qwq-32b" + | "deepseek-r1-distill-llama-70b" +export const groqDefaultModelId: GroqModelId = "llama-3.3-70b-versatile" // Defaulting to Llama3 70B Versatile +export const groqModels = { + // Models based on API response: https://api.groq.com/openai/v1/models + "llama-3.1-8b-instant": { + maxTokens: 131072, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Meta Llama 3.1 8B Instant model, 128K context.", + }, + "llama-3.3-70b-versatile": { + maxTokens: 32768, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Meta Llama 3.3 70B Versatile model, 128K context.", + }, + "meta-llama/llama-4-scout-17b-16e-instruct": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Meta Llama 4 Scout 17B Instruct model, 128K context.", + }, + "meta-llama/llama-4-maverick-17b-128e-instruct": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Meta Llama 4 Maverick 17B Instruct model, 128K context.", + }, + "mistral-saba-24b": { + maxTokens: 32768, + contextWindow: 32768, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Mistral Saba 24B model, 32K context.", + }, + "qwen-qwq-32b": { + maxTokens: 131072, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Alibaba Qwen QwQ 32B model, 128K context.", + }, + "deepseek-r1-distill-llama-70b": { + maxTokens: 131072, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "DeepSeek R1 Distill Llama 70B model, 128K context.", + }, +} as const satisfies Record + +// Chutes AI +// https://llm.chutes.ai/v1 (OpenAI compatible) +export type ChutesModelId = + | "deepseek-ai/DeepSeek-R1" + | "deepseek-ai/DeepSeek-V3" + | "unsloth/Llama-3.3-70B-Instruct" + | "chutesai/Llama-4-Scout-17B-16E-Instruct" + | "unsloth/Mistral-Nemo-Instruct-2407" + | "unsloth/gemma-3-12b-it" + | "NousResearch/DeepHermes-3-Llama-3-8B-Preview" + | "unsloth/gemma-3-4b-it" + | "nvidia/Llama-3_3-Nemotron-Super-49B-v1" + | "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1" + | "chutesai/Llama-4-Maverick-17B-128E-Instruct-FP8" + | "deepseek-ai/DeepSeek-V3-Base" + | "deepseek-ai/DeepSeek-R1-Zero" + | "deepseek-ai/DeepSeek-V3-0324" + | "microsoft/MAI-DS-R1-FP8" + | "tngtech/DeepSeek-R1T-Chimera" +export const chutesDefaultModelId: ChutesModelId = "deepseek-ai/DeepSeek-R1" +export const chutesModels = { + "deepseek-ai/DeepSeek-R1": { + maxTokens: 32768, + contextWindow: 163840, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "DeepSeek R1 model.", + }, + "deepseek-ai/DeepSeek-V3": { + maxTokens: 32768, + contextWindow: 163840, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "DeepSeek V3 model.", + }, + "unsloth/Llama-3.3-70B-Instruct": { + maxTokens: 32768, // From Groq + contextWindow: 131072, // From Groq + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Unsloth Llama 3.3 70B Instruct model.", + }, + "chutesai/Llama-4-Scout-17B-16E-Instruct": { + maxTokens: 32768, + contextWindow: 512000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "ChutesAI Llama 4 Scout 17B Instruct model, 512K context.", + }, + "unsloth/Mistral-Nemo-Instruct-2407": { + maxTokens: 32768, + contextWindow: 128000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Unsloth Mistral Nemo Instruct model.", + }, + "unsloth/gemma-3-12b-it": { + maxTokens: 32768, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Unsloth Gemma 3 12B IT model.", + }, + "NousResearch/DeepHermes-3-Llama-3-8B-Preview": { + maxTokens: 32768, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Nous DeepHermes 3 Llama 3 8B Preview model.", + }, + "unsloth/gemma-3-4b-it": { + maxTokens: 32768, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Unsloth Gemma 3 4B IT model.", + }, + "nvidia/Llama-3_3-Nemotron-Super-49B-v1": { + maxTokens: 32768, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Nvidia Llama 3.3 Nemotron Super 49B model.", + }, + "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1": { + maxTokens: 32768, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Nvidia Llama 3.1 Nemotron Ultra 253B model.", + }, + "chutesai/Llama-4-Maverick-17B-128E-Instruct-FP8": { + maxTokens: 32768, + contextWindow: 256000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "ChutesAI Llama 4 Maverick 17B Instruct FP8 model.", + }, + "deepseek-ai/DeepSeek-V3-Base": { + maxTokens: 32768, + contextWindow: 163840, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "DeepSeek V3 Base model.", + }, + "deepseek-ai/DeepSeek-R1-Zero": { + maxTokens: 32768, + contextWindow: 163840, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "DeepSeek R1 Zero model.", + }, + "deepseek-ai/DeepSeek-V3-0324": { + maxTokens: 32768, + contextWindow: 163840, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "DeepSeek V3 (0324) model.", + }, + "microsoft/MAI-DS-R1-FP8": { + maxTokens: 32768, + contextWindow: 163840, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "Microsoft MAI-DS-R1 FP8 model.", + }, + "tngtech/DeepSeek-R1T-Chimera": { + maxTokens: 32768, + contextWindow: 163840, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + description: "TNGTech DeepSeek R1T Chimera model.", + }, +} as const satisfies Record + /** * Constants */ @@ -1434,16 +1717,9 @@ export const PROMPT_CACHING_MODELS = new Set([ "anthropic/claude-3.7-sonnet", "anthropic/claude-3.7-sonnet:beta", "anthropic/claude-3.7-sonnet:thinking", - "google/gemini-2.5-pro-preview-03-25", - "google/gemini-2.0-flash-001", - "google/gemini-flash-1.5", - "google/gemini-flash-1.5-8b", -]) - -// These models don't have prompt caching enabled by default (you can turn it on -// in settings). -export const OPTIONAL_PROMPT_CACHING_MODELS = new Set([ - "google/gemini-2.5-pro-preview-03-25", + "google/gemini-2.5-pro-preview", + "google/gemini-2.5-flash-preview", + "google/gemini-2.5-flash-preview:thinking", "google/gemini-2.0-flash-001", "google/gemini-flash-1.5", "google/gemini-flash-1.5-8b", @@ -1458,12 +1734,19 @@ export const COMPUTER_USE_MODELS = new Set([ "anthropic/claude-3.7-sonnet:thinking", ]) -const routerNames = ["openrouter", "requesty", "glama", "unbound"] as const +const routerNames = ["openrouter", "requesty", "glama", "unbound", "litellm"] as const export type RouterName = (typeof routerNames)[number] export const isRouterName = (value: string): value is RouterName => routerNames.includes(value as RouterName) +export function toRouterName(value?: string): RouterName { + if (value && isRouterName(value)) { + return value + } + throw new Error(`Invalid router name: ${value}`) +} + export type ModelRecord = Record export type RouterModels = Record diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index b8917d8b108..0c9cba9c670 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -4,6 +4,7 @@ import { AssertEqual, Equals, Keys, Values } from "../utils/type-fu" export type { ExperimentId } export const EXPERIMENT_IDS = { + AUTO_CONDENSE_CONTEXT: "autoCondenseContext", POWER_STEERING: "powerSteering", } as const satisfies Record @@ -16,6 +17,7 @@ interface ExperimentConfig { } export const experimentConfigsMap: Record = { + AUTO_CONDENSE_CONTEXT: { enabled: false }, POWER_STEERING: { enabled: false }, } diff --git a/src/shared/language.ts b/src/shared/language.ts index b5b27472ac3..82947ccbcdf 100644 --- a/src/shared/language.ts +++ b/src/shared/language.ts @@ -16,6 +16,7 @@ export const LANGUAGES: Record = { it: "Italiano", ja: "日本語", ko: "한국어", + nl: "Nederlands", pl: "Polski", "pt-BR": "Português", ru: "Русский", diff --git a/src/shared/mcp.ts b/src/shared/mcp.ts index 7a490851bcf..547c0658ac1 100644 --- a/src/shared/mcp.ts +++ b/src/shared/mcp.ts @@ -1,8 +1,15 @@ +export type McpErrorEntry = { + message: string + timestamp: number + level: "error" | "warn" | "info" +} + export type McpServer = { name: string config: string status: "connected" | "connecting" | "disconnected" error?: string + errorHistory?: McpErrorEntry[] tools?: McpTool[] resources?: McpResource[] resourceTemplates?: McpResourceTemplate[] @@ -55,6 +62,11 @@ export type McpToolCallResponse = { data: string mimeType: string } + | { + type: "audio" + data: string + mimeType: string + } | { type: "resource" resource: { diff --git a/src/shared/modes.ts b/src/shared/modes.ts index d109c872b3d..e27705ec362 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -87,7 +87,7 @@ export const modes: readonly ModeConfig[] = [ "You are PearAI Agent (Powered by Roo Code / Cline), a knowledgeable technical assistant focused on answering questions and providing information about software development, technology, and related topics.", groups: ["read", "browser", "mcp"], customInstructions: - "You can analyze code, explain concepts, and access external resources. Make sure to answer the user's questions and don't rush to switch to implementing code. Include Mermaid diagrams if they help make your response clearer.", + "You can analyze code, explain concepts, and access external resources. Always answer the user’s questions thoroughly, and do not switch to implementing code unless explicitly requested by the user. Include Mermaid diagrams when they clarify your response.", }, { slug: "debug", @@ -245,6 +245,7 @@ export const defaultPrompts: Readonly = Object.freeze( mode.slug, { roleDefinition: mode.roleDefinition, + whenToUse: mode.whenToUse, customInstructions: mode.customInstructions, }, ]), @@ -260,6 +261,7 @@ export async function getAllModesWithPrompts(context: vscode.ExtensionContext): return allModes.map((mode) => ({ ...mode, roleDefinition: customModePrompts[mode.slug]?.roleDefinition ?? mode.roleDefinition, + whenToUse: customModePrompts[mode.slug]?.whenToUse ?? mode.whenToUse, customInstructions: customModePrompts[mode.slug]?.customInstructions ?? mode.customInstructions, })) } @@ -283,6 +285,7 @@ export async function getFullModeDetails( // Get the base custom instructions const baseCustomInstructions = promptComponent?.customInstructions || baseMode.customInstructions || "" + const baseWhenToUse = promptComponent?.whenToUse || baseMode.whenToUse || "" // If we have cwd, load and combine all custom instructions let fullCustomInstructions = baseCustomInstructions @@ -300,6 +303,7 @@ export async function getFullModeDetails( return { ...baseMode, roleDefinition: promptComponent?.roleDefinition || baseMode.roleDefinition, + whenToUse: baseWhenToUse, customInstructions: fullCustomInstructions, } } @@ -314,6 +318,16 @@ export function getRoleDefinition(modeSlug: string, customModes?: ModeConfig[]): return mode.roleDefinition } +// Helper function to safely get whenToUse +export function getWhenToUse(modeSlug: string, customModes?: ModeConfig[]): string { + const mode = getModeBySlug(modeSlug, customModes) + if (!mode) { + console.warn(`No mode found for slug: ${modeSlug}`) + return "" + } + return mode.whenToUse ?? "" +} + // Helper function to safely get custom instructions export function getCustomInstructions(modeSlug: string, customModes?: ModeConfig[]): string { const mode = getModeBySlug(modeSlug, customModes) diff --git a/src/utils/__tests__/enhance-prompt.test.ts b/src/utils/__tests__/enhance-prompt.test.ts index adda8860ebb..524f99c8179 100644 --- a/src/utils/__tests__/enhance-prompt.test.ts +++ b/src/utils/__tests__/enhance-prompt.test.ts @@ -1,5 +1,5 @@ import { singleCompletionHandler } from "../single-completion-handler" -import { ApiConfiguration } from "../../shared/api" +import { ProviderSettings } from "../../shared/api" import { buildApiHandler, SingleCompletionHandler } from "../../api" import { supportPrompt } from "../../shared/support-prompt" @@ -9,7 +9,7 @@ jest.mock("../../api", () => ({ })) describe("enhancePrompt", () => { - const mockApiConfig: ApiConfiguration = { + const mockApiConfig: ProviderSettings = { apiProvider: "openai", openAiApiKey: "test-key", openAiBaseUrl: "https://api.openai.com/v1", @@ -69,7 +69,7 @@ describe("enhancePrompt", () => { }) it("throws error for missing API configuration", async () => { - await expect(singleCompletionHandler({} as ApiConfiguration, "Test prompt")).rejects.toThrow( + await expect(singleCompletionHandler({} as ProviderSettings, "Test prompt")).rejects.toThrow( "No valid API configuration provided", ) }) @@ -94,7 +94,7 @@ describe("enhancePrompt", () => { }) it("uses appropriate model based on provider", async () => { - const openRouterConfig: ApiConfiguration = { + const openRouterConfig: ProviderSettings = { apiProvider: "openrouter", openRouterApiKey: "test-key", openRouterModelId: "test-model", diff --git a/src/utils/single-completion-handler.ts b/src/utils/single-completion-handler.ts index 5e049d47267..7434d07e195 100644 --- a/src/utils/single-completion-handler.ts +++ b/src/utils/single-completion-handler.ts @@ -1,11 +1,11 @@ -import { ApiConfiguration } from "../shared/api" +import { ProviderSettings } from "../shared/api" import { buildApiHandler, SingleCompletionHandler } from "../api" /** * Enhances a prompt using the configured API without creating a full Cline instance or task history. * This is a lightweight alternative that only uses the API's completion functionality. */ -export async function singleCompletionHandler(apiConfiguration: ApiConfiguration, promptText: string): Promise { +export async function singleCompletionHandler(apiConfiguration: ProviderSettings, promptText: string): Promise { if (!promptText) { throw new Error("No prompt text provided") } diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000000..778f7bc32c9 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + include: ["**/__tests__/**/*.spec.ts"], + }, +}) diff --git a/webview-ui/.eslintrc.json b/webview-ui/.eslintrc.json index af7d58c90a4..5a91a2d59e5 100644 --- a/webview-ui/.eslintrc.json +++ b/webview-ui/.eslintrc.json @@ -1,7 +1,16 @@ { "extends": "react-app", "rules": { - "@typescript-eslint/no-unused-vars": "off" + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" }] }, - "ignorePatterns": ["!.storybook"] + "overrides": [ + { + "files": ["webview-ui/src/__tests__/utils/command-validation.test.ts"], + "rules": { + "no-template-curly-in-string": "off", + "no-useless-escape": "off" + } + } + ] } diff --git a/webview-ui/index.html b/webview-ui/index.html index 9ca170dc5bd..b5ea9569faa 100644 --- a/webview-ui/index.html +++ b/webview-ui/index.html @@ -1,12 +1,12 @@ - + - - - - Roo Code - - -
- - - \ No newline at end of file + + + + Roo Code + + +
+ + + diff --git a/webview-ui/jest.config.cjs b/webview-ui/jest.config.cjs index 2d683ebab3e..347fcb39c63 100644 --- a/webview-ui/jest.config.cjs +++ b/webview-ui/jest.config.cjs @@ -12,14 +12,14 @@ module.exports = { "^vscrui$": "/src/__mocks__/vscrui.ts", "^@vscode/webview-ui-toolkit/react$": "/src/__mocks__/@vscode/webview-ui-toolkit/react.ts", "^@/(.*)$": "/src/$1", - '^@roo/(.*)$': '/../src/$1', - '^@src/(.*)$': '/src/$1', + "^@roo/(.*)$": "/../src/$1", + "^@src/(.*)$": "/src/$1", "^src/i18n/setup$": "/src/__mocks__/i18n/setup.ts", "^\\.\\./setup$": "/src/__mocks__/i18n/setup.ts", "^\\./setup$": "/src/__mocks__/i18n/setup.ts", "^src/i18n/TranslationContext$": "/src/__mocks__/i18n/TranslationContext.tsx", "^\\.\\./TranslationContext$": "/src/__mocks__/i18n/TranslationContext.tsx", - "^\\./TranslationContext$": "/src/__mocks__/i18n/TranslationContext.tsx" + "^\\./TranslationContext$": "/src/__mocks__/i18n/TranslationContext.tsx", }, reporters: [["jest-simple-dot-reporter", {}]], transformIgnorePatterns: [ diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index d0d15af855c..da10c89e3d1 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -30,13 +30,14 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.0.0", + "date-fns": "^4.1.0", "debounce": "^2.1.1", "fast-deep-equal": "^3.1.3", "fzf": "^0.5.2", "i18next": "^24.2.2", "i18next-http-backend": "^3.0.2", "knuth-shuffle-seeded": "^1.0.6", - "lucide-react": "^0.475.0", + "lucide-react": "^0.510.0", "mermaid": "^11.4.1", "posthog-js": "^1.227.2", "react": "^18.3.1", @@ -52,6 +53,7 @@ "remove-markdown": "^0.6.0", "shell-quote": "^1.8.2", "shiki": "^3.2.1", + "source-map": "^0.7.4", "styled-components": "^6.1.13", "tailwind-merge": "^2.6.0", "tailwindcss": "^4.0.0", @@ -81,7 +83,7 @@ "eslint-config-react-app": "^7.0.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-storybook": "^0.11.2", + "eslint-plugin-storybook": "^0.12.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -90,7 +92,7 @@ "storybook-dark-mode": "^4.0.2", "ts-jest": "^29.2.5", "typescript": "^5.4.5", - "vite": "6.0.11" + "vite": "6.3.5" } }, "../../pearai-submodule/core": { @@ -192,7 +194,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -223,24 +224,24 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", - "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", "dev": true, "license": "MIT", "engines": { @@ -248,22 +249,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", - "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.7", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.26.7", - "@babel/types": "^7.26.7", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -308,14 +309,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", - "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/types": "^7.26.5", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -338,14 +339,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -426,29 +427,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -531,9 +532,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -541,9 +542,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -551,9 +552,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -576,27 +577,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", - "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10" + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", - "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.10" + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -2264,32 +2265,32 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", - "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2298,14 +2299,14 @@ } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2327,6 +2328,7 @@ "version": "11.0.3", "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "license": "Apache-2.0", "dependencies": { "@chevrotain/gast": "11.0.3", "@chevrotain/types": "11.0.3", @@ -2337,6 +2339,7 @@ "version": "11.0.3", "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "license": "Apache-2.0", "dependencies": { "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" @@ -2345,17 +2348,20 @@ "node_modules/@chevrotain/regexp-to-ast": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", - "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==" + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "license": "Apache-2.0" }, "node_modules/@chevrotain/types": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", - "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==" + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "license": "Apache-2.0" }, "node_modules/@chevrotain/utils": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", - "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==" + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "license": "Apache-2.0" }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.2", @@ -2379,9 +2385,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", "cpu": [ "ppc64" ], @@ -2395,9 +2401,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", "cpu": [ "arm" ], @@ -2411,9 +2417,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", "cpu": [ "arm64" ], @@ -2427,9 +2433,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", "cpu": [ "x64" ], @@ -2443,9 +2449,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", "cpu": [ "arm64" ], @@ -2459,9 +2465,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", "cpu": [ "x64" ], @@ -2475,9 +2481,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", "cpu": [ "arm64" ], @@ -2491,9 +2497,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", "cpu": [ "x64" ], @@ -2507,9 +2513,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", "cpu": [ "arm" ], @@ -2523,9 +2529,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", "cpu": [ "arm64" ], @@ -2539,9 +2545,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", "cpu": [ "ia32" ], @@ -2555,9 +2561,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", "cpu": [ "loong64" ], @@ -2571,9 +2577,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", "cpu": [ "mips64el" ], @@ -2587,9 +2593,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", "cpu": [ "ppc64" ], @@ -2603,9 +2609,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", "cpu": [ "riscv64" ], @@ -2619,9 +2625,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", "cpu": [ "s390x" ], @@ -2635,9 +2641,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", "cpu": [ "x64" ], @@ -2651,9 +2657,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", "cpu": [ "arm64" ], @@ -2667,9 +2673,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", "cpu": [ "x64" ], @@ -2683,9 +2689,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", "cpu": [ "arm64" ], @@ -2699,9 +2705,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", "cpu": [ "x64" ], @@ -2715,9 +2721,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", "cpu": [ "x64" ], @@ -2731,9 +2737,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", "cpu": [ "arm64" ], @@ -2747,9 +2753,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", "cpu": [ "ia32" ], @@ -2763,9 +2769,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", "cpu": [ "x64" ], @@ -2779,9 +2785,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -3129,6 +3135,18 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3668,7 +3686,6 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -3683,7 +3700,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -3693,7 +3709,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -3709,7 +3724,6 @@ "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -3771,11 +3785,12 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz", - "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", + "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", + "license": "MIT", "dependencies": { - "langium": "3.0.0" + "langium": "3.3.1" } }, "node_modules/@microsoft/fast-element": { @@ -5888,9 +5903,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz", - "integrity": "sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", + "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", "cpu": [ "arm" ], @@ -5901,9 +5916,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz", - "integrity": "sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", + "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", "cpu": [ "arm64" ], @@ -5914,9 +5929,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz", - "integrity": "sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", + "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", "cpu": [ "arm64" ], @@ -5927,9 +5942,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz", - "integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", + "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", "cpu": [ "x64" ], @@ -5940,9 +5955,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz", - "integrity": "sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", + "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", "cpu": [ "arm64" ], @@ -5953,9 +5968,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz", - "integrity": "sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", + "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", "cpu": [ "x64" ], @@ -5966,9 +5981,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz", - "integrity": "sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", + "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", "cpu": [ "arm" ], @@ -5979,9 +5994,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz", - "integrity": "sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", + "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", "cpu": [ "arm" ], @@ -5992,9 +6007,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz", - "integrity": "sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", + "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", "cpu": [ "arm64" ], @@ -6005,9 +6020,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz", - "integrity": "sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", + "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", "cpu": [ "arm64" ], @@ -6018,9 +6033,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz", - "integrity": "sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", + "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", "cpu": [ "loong64" ], @@ -6031,9 +6046,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz", - "integrity": "sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", + "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", "cpu": [ "ppc64" ], @@ -6044,9 +6059,22 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz", - "integrity": "sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", + "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", + "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", "cpu": [ "riscv64" ], @@ -6057,9 +6085,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz", - "integrity": "sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", + "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", "cpu": [ "s390x" ], @@ -6070,9 +6098,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz", - "integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", + "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==", "cpu": [ "x64" ], @@ -6083,9 +6111,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz", - "integrity": "sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz", + "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==", "cpu": [ "x64" ], @@ -6096,9 +6124,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz", - "integrity": "sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", + "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", "cpu": [ "arm64" ], @@ -6109,9 +6137,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz", - "integrity": "sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", + "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", "cpu": [ "ia32" ], @@ -6122,9 +6150,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz", - "integrity": "sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", + "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", "cpu": [ "x64" ], @@ -6149,60 +6177,60 @@ "license": "MIT" }, "node_modules/@shikijs/core": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.3.0.tgz", - "integrity": "sha512-CovkFL2WVaHk6PCrwv6ctlmD4SS1qtIfN8yEyDXDYWh4ONvomdM9MaFw20qHuqJOcb8/xrkqoWQRJ//X10phOQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.4.0.tgz", + "integrity": "sha512-0YOzTSRDn/IAfQWtK791gs1u8v87HNGToU6IwcA3K7nPoVOrS2Dh6X6A6YfXgPTSkTwR5y6myk0MnI0htjnwrA==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.3.0", + "@shikijs/types": "3.4.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "node_modules/@shikijs/engine-javascript": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.3.0.tgz", - "integrity": "sha512-XlhnFGv0glq7pfsoN0KyBCz9FJU678LZdQ2LqlIdAj6JKsg5xpYKay3DkazXWExp3DTJJK9rMOuGzU2911pg7Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.4.0.tgz", + "integrity": "sha512-1ywDoe+z/TPQKj9Jw0eU61B003J9DqUFRfH+DVSzdwPUFhR7yOmfyLzUrFz0yw8JxFg/NgzXoQyyykXgO21n5Q==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.3.0", + "@shikijs/types": "3.4.0", "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.2.0" + "oniguruma-to-es": "^4.3.3" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.3.0.tgz", - "integrity": "sha512-l0vIw+GxeNU7uGnsu6B+Crpeqf+WTQ2Va71cHb5ZYWEVEPdfYwY5kXwYqRJwHrxz9WH+pjSpXQz+TJgAsrkA5A==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.4.0.tgz", + "integrity": "sha512-zwcWlZ4OQuJ/+1t32ClTtyTU1AiDkK1lhtviRWoq/hFqPjCNyLj22bIg9rB7BfoZKOEOfrsGz7No33BPCf+WlQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.3.0", + "@shikijs/types": "3.4.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "node_modules/@shikijs/langs": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.3.0.tgz", - "integrity": "sha512-zt6Kf/7XpBQKSI9eqku+arLkAcDQ3NHJO6zFjiChI8w0Oz6Jjjay7pToottjQGjSDCFk++R85643WbyINcuL+g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.4.0.tgz", + "integrity": "sha512-bQkR+8LllaM2duU9BBRQU0GqFTx7TuF5kKlw/7uiGKoK140n1xlLAwCgXwSxAjJ7Htk9tXTFwnnsJTCU5nDPXQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.3.0" + "@shikijs/types": "3.4.0" } }, "node_modules/@shikijs/themes": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.3.0.tgz", - "integrity": "sha512-tXeCvLXBnqq34B0YZUEaAD1lD4lmN6TOHAhnHacj4Owh7Ptb/rf5XCDeROZt2rEOk5yuka3OOW2zLqClV7/SOg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.4.0.tgz", + "integrity": "sha512-YPP4PKNFcFGLxItpbU0ZW1Osyuk8AyZ24YEFaq04CFsuCbcqydMvMUTi40V2dkc0qs1U2uZFrnU6s5zI6IH+uA==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.3.0" + "@shikijs/types": "3.4.0" } }, "node_modules/@shikijs/types": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.3.0.tgz", - "integrity": "sha512-KPCGnHG6k06QG/2pnYGbFtFvpVJmC3uIpXrAiPrawETifujPBv0Se2oUxm5qYgjCvGJS9InKvjytOdN+bGuX+Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.4.0.tgz", + "integrity": "sha512-EUT/0lGiE//7j5N/yTMNMT3eCWNcHJLrRKxT0NDXWIfdfSmFJKfPX7nMmRBrQnWboAzIsUziCThrYMMhjbMS1A==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", @@ -6243,9 +6271,9 @@ } }, "node_modules/@storybook/addon-actions": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.5.6.tgz", - "integrity": "sha512-kREkqUNmaYFYL5NsgbtYXxuFbVGuoA1reQPYl/ToqI/ujXZo1XDo0o+Sztjj8r2GVAjaM6a96FUxEJ7yg1yBCg==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.6.12.tgz", + "integrity": "sha512-B5kfiRvi35oJ0NIo53CGH66H471A3XTzrfaa6SxXEJsgxxSeKScG5YeXcCvLiZfvANRQ7QDsmzPUgg0o3hdMXw==", "dev": true, "license": "MIT", "dependencies": { @@ -6260,13 +6288,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-backgrounds": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.5.6.tgz", - "integrity": "sha512-vdkYPtrd9FtWPU22QylQF5GTh6hJa//s5I2r2+AZ3huHeqWvyOcFHyOM//RlwcPjkNDnaCbaSotDdeP6C77rcQ==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.6.12.tgz", + "integrity": "sha512-lmIAma9BiiCTbJ8YfdZkXjpnAIrOUcgboLkt1f6XJ78vNEMnLNzD9gnh7Tssz1qrqvm34v9daDjIb+ggdiKp3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6279,13 +6307,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-controls": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.5.6.tgz", - "integrity": "sha512-OiIwgfKfx/4lOjHl4CEkO+d4eM31nsV2PfrCgtMsTOwg1YKZ4K5/Sq6HvEmqoAdJReonSlKnzBOzoVFVeG9A+A==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.6.12.tgz", + "integrity": "sha512-9VSRPJWQVb9wLp21uvpxDGNctYptyUX0gbvxIWOHMH3R2DslSoq41lsC/oQ4l4zSHVdL+nq8sCTkhBxIsjKqdQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6298,20 +6326,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-docs": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.5.6.tgz", - "integrity": "sha512-LOBupHN4K8eaSrfG/byl2d3lnFOIIkp4rDnsglgEbDe0Rv9E/yjaigcSW1pzFQ0pgRH7tg7sZz26cISHBvr50A==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.6.12.tgz", + "integrity": "sha512-kEezQjAf/p3SpDzLABgg4fbT48B6dkT2LiZCKTRmCrJVtuReaAr4R9MMM6Jsph6XjbIj/SvOWf3CMeOPXOs9sg==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/blocks": "8.5.6", - "@storybook/csf-plugin": "8.5.6", - "@storybook/react-dom-shim": "8.5.6", + "@storybook/blocks": "8.6.12", + "@storybook/csf-plugin": "8.6.12", + "@storybook/react-dom-shim": "8.6.12", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" @@ -6321,25 +6349,25 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-essentials": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.5.6.tgz", - "integrity": "sha512-CtOCbJ1TkCqvOoqrksKMTattJdIIe4N/x/o4IBNzvmaLJD0TUYbCnEsYAzm4WXTVdxQ9uJO4f/BHRkNShuHbew==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.6.12.tgz", + "integrity": "sha512-Y/7e8KFlttaNfv7q2zoHMPdX6hPXHdsuQMAjYl5NG9HOAJREu4XBy4KZpbcozRe4ApZ78rYsN/MO1EuA+bNMIA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-actions": "8.5.6", - "@storybook/addon-backgrounds": "8.5.6", - "@storybook/addon-controls": "8.5.6", - "@storybook/addon-docs": "8.5.6", - "@storybook/addon-highlight": "8.5.6", - "@storybook/addon-measure": "8.5.6", - "@storybook/addon-outline": "8.5.6", - "@storybook/addon-toolbars": "8.5.6", - "@storybook/addon-viewport": "8.5.6", + "@storybook/addon-actions": "8.6.12", + "@storybook/addon-backgrounds": "8.6.12", + "@storybook/addon-controls": "8.6.12", + "@storybook/addon-docs": "8.6.12", + "@storybook/addon-highlight": "8.6.12", + "@storybook/addon-measure": "8.6.12", + "@storybook/addon-outline": "8.6.12", + "@storybook/addon-toolbars": "8.6.12", + "@storybook/addon-viewport": "8.6.12", "ts-dedent": "^2.0.0" }, "funding": { @@ -6347,13 +6375,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-highlight": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.5.6.tgz", - "integrity": "sha512-uuwBe+FwT9vKbEG9S/yqwZLD1GP3y5Mpu2gsiNcYcfhxHpwDQVbknOSeJJig/CGhuDMqy95GcgItIs/kPPFKqg==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.6.12.tgz", + "integrity": "sha512-9FITVxdoycZ+eXuAZL9ElWyML/0fPPn9UgnnAkrU7zkMi+Segq/Tx7y+WWanC5zfWZrXAuG6WTOYEXeWQdm//w==", "dev": true, "license": "MIT", "dependencies": { @@ -6364,13 +6392,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-measure": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.5.6.tgz", - "integrity": "sha512-Q83k/75/vcFcXz3YAvwfWpHQubJyOzpNT/jTLdeK27uXatVH6eq0+dRt/fW1plri9GA52HJmiZ7SvJ6MAHFQzQ==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.6.12.tgz", + "integrity": "sha512-tACmwqqOvutaQSduw8SMb62wICaT1rWaHtMN3vtWXuxgDPSdJQxLP+wdVyRYMAgpxhLyIO7YRf++Hfha9RHgFg==", "dev": true, "license": "MIT", "dependencies": { @@ -6382,13 +6410,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-outline": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.5.6.tgz", - "integrity": "sha512-HypYCQ5aF0Htyhc8E+ZhJEnSojuNheYWq7Kgd51WnSYLtZbZfPbLKYiw/VHPvYWbS2IpKJ5YDAvkUPzgwqgBgA==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.6.12.tgz", + "integrity": "sha512-1ylwm+n1s40S91No0v9T4tCjZORu3GbnjINlyjYTDLLhQHyBQd3nWR1Y1eewU4xH4cW9SnSLcMQFS/82xHqU6A==", "dev": true, "license": "MIT", "dependencies": { @@ -6400,13 +6428,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-toolbars": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.5.6.tgz", - "integrity": "sha512-e6wJne/bH0EOnqUCz4SDIYxwuEgDzLOYcJZvcl8aNWfoHTgZBSI/5ai9d23CvM0SFY9dGdKwjEejvdJjwRcK0w==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.6.12.tgz", + "integrity": "sha512-HEcSzo1DyFtIu5/ikVOmh5h85C1IvK9iFKSzBR6ice33zBOaehVJK+Z5f487MOXxPsZ63uvWUytwPyViGInj+g==", "dev": true, "license": "MIT", "funding": { @@ -6414,13 +6442,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/addon-viewport": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.5.6.tgz", - "integrity": "sha512-i0PJN587K9GMViXJr9Mb4cFF7ZiGvFpk215xRgtC33Pv7mIp8yRjbjNgi3TgEfDe4GQFQ1hKoisqk/pjs9quXg==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.6.12.tgz", + "integrity": "sha512-EXK2LArAnABsPP0leJKy78L/lbMWow+EIJfytEP5fHaW4EhMR6h7Hzaqzre6U0IMMr/jVFa1ci+m0PJ0eQc2bw==", "dev": true, "license": "MIT", "dependencies": { @@ -6431,17 +6459,16 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/blocks": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.5.6.tgz", - "integrity": "sha512-5RL2hnk3y9MX8TxJUY4OxGw0rBuJ8OhuWtBK4DlFug3dRKd/TuOuAfIqVWzV5KybI6LyQLD0GOgt+REqP4YQeA==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.6.12.tgz", + "integrity": "sha512-DohlTq6HM1jDbHYiXL4ZvZ00VkhpUp5uftzj/CZDLY1fYHRjqtaTwWm2/OpceivMA8zDitLcq5atEZN+f+siTg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf": "0.1.12", "@storybook/icons": "^1.2.12", "ts-dedent": "^2.0.0" }, @@ -6452,7 +6479,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^8.5.6" + "storybook": "^8.6.12" }, "peerDependenciesMeta": { "react": { @@ -6464,13 +6491,13 @@ } }, "node_modules/@storybook/builder-vite": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-8.5.6.tgz", - "integrity": "sha512-uvNo8wAULW2+IOlsFCrszvH6juBDoOEYZIn0WLGzRKbMvLGt3j6CB6d2QjRrLs9p62ayia51fTpJfhIISM9new==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-8.6.12.tgz", + "integrity": "sha512-Gju21ud/3Qw4v2vLNaa5SuJECsI9ICNRr2G0UyCCzRvCHg8jpA9lDReu2NqhLDyFIuDG+ZYT38gcaHEUoNQ8KQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf-plugin": "8.5.6", + "@storybook/csf-plugin": "8.6.12", "browser-assert": "^1.2.1", "ts-dedent": "^2.0.0" }, @@ -6479,14 +6506,14 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6", + "storybook": "^8.6.12", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" } }, "node_modules/@storybook/components": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.5.6.tgz", - "integrity": "sha512-d2mhnnce2C03lRhBEtVR9lS78YueQGBS949R3QXPsEXmrkfDMpcnFI3DIOByjnea6ZeS0+i4lYjnfiAJb0oiMQ==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.6.12.tgz", + "integrity": "sha512-FiaE8xvCdvKC2arYusgtlDNZ77b8ysr8njAYQZwwaIHjy27TbR2tEpLDCmUwSbANNmivtc/xGEiDDwcNppMWlQ==", "dev": true, "license": "MIT", "funding": { @@ -6498,13 +6525,13 @@ } }, "node_modules/@storybook/core": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.7.tgz", - "integrity": "sha512-FcvLFA+Qn3+D6LgQkk0MOXA5FBz8DGc0UZmZuVbIwIUV4MV4ywCMwtKdG0cyhtzQg0YNyfiIYWJr7lZ4jLLhYg==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.12.tgz", + "integrity": "sha512-t+ZuDzAlsXKa6tLxNZT81gEAt4GNwsKP/Id2wluhmUWD/lwYW0uum1JiPUuanw8xD6TdakCW/7ULZc7aQUBLCQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/theming": "8.6.7", + "@storybook/theming": "8.6.12", "better-opn": "^3.0.2", "browser-assert": "^1.2.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", @@ -6543,20 +6570,6 @@ "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, - "node_modules/@storybook/core/node_modules/@storybook/theming": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.7.tgz", - "integrity": "sha512-F/i4XS5bew9dvtNiHvDJF0mko1IUbPM9PUjTYPaw6cK8ytS0kdec703MsJ/GUA7seeEWBeGdZjV3ua0pys650A==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" - } - }, "node_modules/@storybook/core/node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", @@ -6581,9 +6594,9 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.5.6.tgz", - "integrity": "sha512-60JBEVsW8x7u4hc+NmrCE0ij36QnaitqTDsxaT8BhbDrqFUvxwUjeaEmoyMn/UCJh080fQfKc2+dqBkFfbkAww==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.6.12.tgz", + "integrity": "sha512-6s8CnP1aoKPb3XtC0jRLUp8M5vTA8RhGAwQDKUsFpCC7g89JR9CaKs9FY2ZSzsNbjR15uASi7b3K8BzeYumYQg==", "dev": true, "license": "MIT", "dependencies": { @@ -6594,7 +6607,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/csf/node_modules/type-fest": { @@ -6632,9 +6645,9 @@ } }, "node_modules/@storybook/manager-api": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.5.6.tgz", - "integrity": "sha512-24Fm1LnRs1uCTMDid1Mmii0mQvmyM//IfzdtuVvzh0OSvatEKKLX+o3vdG/3/QCN1FVyq1hI9uHnkOaz6EuH4Q==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.6.12.tgz", + "integrity": "sha512-O0SpISeJLNTQvhSBOsWzzkCgs8vCjOq1578rwqHlC6jWWm4QmtfdyXqnv7rR1Hk08kQ+Dzqh0uhwHx0nfwy4nQ==", "dev": true, "license": "MIT", "funding": { @@ -6646,9 +6659,9 @@ } }, "node_modules/@storybook/preview-api": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.5.6.tgz", - "integrity": "sha512-brT8jvw+QYoAyddOtPTqMc6tHDKye24oYkL5Bvp96nQi5AcNhkpL1eYfS7dtcQFV7j010Ox6RlzHPt+Ln8XB+Q==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.6.12.tgz", + "integrity": "sha512-84FE3Hrs0AYKHqpDZOwx1S/ffOfxBdL65lhCoeI8GoWwCkzwa9zEP3kvXBo/BnEDO7nAfxvMhjASTZXbKRJh5Q==", "dev": true, "license": "MIT", "funding": { @@ -6660,18 +6673,18 @@ } }, "node_modules/@storybook/react": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.5.6.tgz", - "integrity": "sha512-i+h3Kbeus7XaQBdxuAa2oLATMH/pMW3rLlilGXo/lnYkPanslRD77Eb4Oc+ChubzQZe2njda+C/SnHYgnp9tEg==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.6.12.tgz", + "integrity": "sha512-NzxlHLA5DkDgZM/dMwTYinuzRs6rsUPmlqP+NIv6YaciQ4NGnTYyOC7R/SqI6HHFm8ZZ5eMYvpfiFmhZ9rU+rQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/components": "8.5.6", + "@storybook/components": "8.6.12", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "8.5.6", - "@storybook/preview-api": "8.5.6", - "@storybook/react-dom-shim": "8.5.6", - "@storybook/theming": "8.5.6" + "@storybook/manager-api": "8.6.12", + "@storybook/preview-api": "8.6.12", + "@storybook/react-dom-shim": "8.6.12", + "@storybook/theming": "8.6.12" }, "engines": { "node": ">=18.0.0" @@ -6681,10 +6694,10 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/test": "8.5.6", + "@storybook/test": "8.6.12", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.5.6", + "storybook": "^8.6.12", "typescript": ">= 4.2.x" }, "peerDependenciesMeta": { @@ -6697,9 +6710,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.5.6.tgz", - "integrity": "sha512-Wfu7HCLRyG+0HpHwz+YPeiY70KyZ0mBzcGrgdP+wJ0n6jVXx3+LWheN+5f21tEydAGbpdBT8FN784k2juPkE7A==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.6.12.tgz", + "integrity": "sha512-51QvoimkBzYs8s3rCYnY5h0cFqLz/Mh0vRcughwYaXckWzDBV8l67WBO5Xf5nBsukCbWyqBVPpEQLww8s7mrLA==", "dev": true, "license": "MIT", "funding": { @@ -6709,20 +6722,20 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.5.6" + "storybook": "^8.6.12" } }, "node_modules/@storybook/react-vite": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-8.5.6.tgz", - "integrity": "sha512-hLxWRF51tqTJVqmDP+EOLYRKJX9GKYpPNE2vDrFM9DoSuyckAeEPrVr0NhuMUzEBZ+2lP6BIkoWTWvjZSm+rhw==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-8.6.12.tgz", + "integrity": "sha512-UA2Kule99oyFgHdhcuhrRwCKyWu/yMbqbl9U7NwowFHNwWWFjVMMir/AmfShb/H1C1DQ3LqOad6/QwJyPLjP8g==", "dev": true, "license": "MIT", "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "0.5.0", "@rollup/pluginutils": "^5.0.2", - "@storybook/builder-vite": "8.5.6", - "@storybook/react": "8.5.6", + "@storybook/builder-vite": "8.6.12", + "@storybook/react": "8.6.12", "find-up": "^5.0.0", "magic-string": "^0.30.0", "react-docgen": "^7.0.0", @@ -6737,10 +6750,10 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/test": "8.5.6", + "@storybook/test": "8.6.12", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.5.6", + "storybook": "^8.6.12", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" }, "peerDependenciesMeta": { @@ -6750,9 +6763,9 @@ } }, "node_modules/@storybook/theming": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.5.6.tgz", - "integrity": "sha512-WX0NjPn6sao56OCSm3NVPqBjFhLhMLPjjDwC4fHCW25HZgI+u7oByNk/7YHcxpBYtoHSWMKMiCjOSJuW6731+A==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.12.tgz", + "integrity": "sha512-6VjZg8HJ2Op7+KV7ihJpYrDnFtd9D1jrQnUS8LckcpuBXrIEbaut5+34ObY8ssQnSqkk2GwIZBBBQYQBCVvkOw==", "dev": true, "license": "MIT", "funding": { @@ -6773,14 +6786,18 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.0.tgz", - "integrity": "sha512-tfG2uBvo6j6kDIPmntxwXggCOZAt7SkpAXJ6pTIYirNdk5FBqh/CZZ9BZPpgcl/tNFLs6zc4yghM76sqiELG9g==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.6.tgz", + "integrity": "sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==", "license": "MIT", "dependencies": { - "enhanced-resolve": "^5.18.0", + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", - "tailwindcss": "4.0.0" + "lightningcss": "1.29.2", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.6" } }, "node_modules/@tailwindcss/node/node_modules/jiti": { @@ -6793,31 +6810,37 @@ } }, "node_modules/@tailwindcss/oxide": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.0.tgz", - "integrity": "sha512-W3FjpJgy4VV1JiL7iBYDf2n/WkeDg1Il+0Q7eWnqPyvkPPCo/Mbwc5BiaT7dfBNV6tQKAhVE34rU5xl8pSl50w==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.6.tgz", + "integrity": "sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==", + "hasInstallScript": true, "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.0.0", - "@tailwindcss/oxide-darwin-arm64": "4.0.0", - "@tailwindcss/oxide-darwin-x64": "4.0.0", - "@tailwindcss/oxide-freebsd-x64": "4.0.0", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.0", - "@tailwindcss/oxide-linux-arm64-gnu": "4.0.0", - "@tailwindcss/oxide-linux-arm64-musl": "4.0.0", - "@tailwindcss/oxide-linux-x64-gnu": "4.0.0", - "@tailwindcss/oxide-linux-x64-musl": "4.0.0", - "@tailwindcss/oxide-win32-arm64-msvc": "4.0.0", - "@tailwindcss/oxide-win32-x64-msvc": "4.0.0" + "@tailwindcss/oxide-android-arm64": "4.1.6", + "@tailwindcss/oxide-darwin-arm64": "4.1.6", + "@tailwindcss/oxide-darwin-x64": "4.1.6", + "@tailwindcss/oxide-freebsd-x64": "4.1.6", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.6", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.6", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.6", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.6", + "@tailwindcss/oxide-linux-x64-musl": "4.1.6", + "@tailwindcss/oxide-wasm32-wasi": "4.1.6", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.6", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.6" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.0.tgz", - "integrity": "sha512-EAhjU0+FIdyGPR+7MbBWubLLPtmOu+p7c2egTTFBRk/n//zYjNvVK0WhcBK5Y7oUB5mo4EjA2mCbY7dcEMWSRw==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.6.tgz", + "integrity": "sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==", "cpu": [ "arm64" ], @@ -6831,9 +6854,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.0.tgz", - "integrity": "sha512-hdz4xnSWS11cIp+7ye+3dGHqs0X33z+BXXTtgPOguDWVa+TdXUzwxonklSzf5wlJFuot3dv5eWzhlNai0oYYQg==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.6.tgz", + "integrity": "sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==", "cpu": [ "arm64" ], @@ -6847,9 +6870,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.0.tgz", - "integrity": "sha512-+dOUUaXTkPKKhtUI9QtVaYg+MpmLh2CN0dHohiYXaBirEyPMkjaT0zbRgzQlNnQWjCVVXPQluIEb0OMEjSTH+Q==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.6.tgz", + "integrity": "sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==", "cpu": [ "x64" ], @@ -6863,9 +6886,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.0.tgz", - "integrity": "sha512-CJhGDhxnrmu4SwyC62fA+wP24MhA/TZlIhRHqg1kRuIHoGoVR2uSSm1qxTxU37tSSZj8Up0q6jsBJCAP4k7rgQ==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.6.tgz", + "integrity": "sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==", "cpu": [ "x64" ], @@ -6879,9 +6902,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.0.tgz", - "integrity": "sha512-Wy7Av0xzXfY2ujZBcYy4+7GQm25/J1iHvlQU2CfwdDCuPWfIjYzR6kggz+uVdSJyKV2s64znchBxRE8kV4uXSA==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.6.tgz", + "integrity": "sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==", "cpu": [ "arm" ], @@ -6895,9 +6918,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.0.tgz", - "integrity": "sha512-srwBo2l6pvM0swBntc1ucuhGsfFOLkqPRFQ3dWARRTfSkL1U9nAsob2MKc/n47Eva/W9pZZgMOuf7rDw8pK1Ew==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.6.tgz", + "integrity": "sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==", "cpu": [ "arm64" ], @@ -6911,9 +6934,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.0.tgz", - "integrity": "sha512-abhusswkduYWuezkBmgo0K0/erGq3M4Se5xP0fhc/0dKs0X/rJUYYCFWntHb3IGh3aVzdQ0SXJs93P76DbUqtw==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.6.tgz", + "integrity": "sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==", "cpu": [ "arm64" ], @@ -6927,9 +6950,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.0.tgz", - "integrity": "sha512-hGtRYIUEx377/HlU49+jvVKKwU1MDSKYSMMs0JFO2Wp7LGxk5+0j5+RBk9NFnmp/lbp32yPTgIOO5m1BmDq36A==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.6.tgz", + "integrity": "sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==", "cpu": [ "x64" ], @@ -6943,9 +6966,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.0.tgz", - "integrity": "sha512-7xgQgSAThs0I14VAgmxpJnK6XFSZBxHMGoDXkLyYkEnu+8WRQMbCP93dkCUn2PIv+Q+JulRgc00PJ09uORSLXQ==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.6.tgz", + "integrity": "sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==", "cpu": [ "x64" ], @@ -6958,10 +6981,93 @@ "node": ">= 10" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.6.tgz", + "integrity": "sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.9", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.9", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.0.tgz", - "integrity": "sha512-qEcgTIPcWY5ZE7f6VxQ/JPrSFMcehzVIlZj7sGE3mVd5YWreAT+Fl1vSP8q2pjnWXn0avZG3Iw7a2hJQAm+fTQ==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.6.tgz", + "integrity": "sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==", "cpu": [ "arm64" ], @@ -6975,9 +7081,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.0.tgz", - "integrity": "sha512-bqT0AY8RXb8GMDy28JtngvqaOSB2YixbLPLvUo6I6lkvvUwA6Eqh2Tj60e2Lh7O/k083f8tYiB0WEK4wmTI7Jg==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.6.tgz", + "integrity": "sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==", "cpu": [ "x64" ], @@ -6991,24 +7097,23 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.0.tgz", - "integrity": "sha512-4uukMiU9gHui8KMPMdWic5SP1O/tmQ1NFSRNrQWmcop5evAVl/LZ6/LuWL3quEiecp2RBcRWwqJrG+mFXlRlew==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.6.tgz", + "integrity": "sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "^4.0.0", - "@tailwindcss/oxide": "^4.0.0", - "lightningcss": "^1.29.1", - "tailwindcss": "4.0.0" + "@tailwindcss/node": "4.1.6", + "@tailwindcss/oxide": "4.1.6", + "tailwindcss": "4.1.6" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "node_modules/@tanstack/query-core": { - "version": "5.68.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.68.0.tgz", - "integrity": "sha512-r8rFYYo8/sY/LNaOqX84h12w7EQev4abFXDWy4UoDVUJzJ5d9Fbmb8ayTi7ScG+V0ap44SF3vNs/45mkzDGyGw==", + "version": "5.76.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.76.0.tgz", + "integrity": "sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==", "license": "MIT", "funding": { "type": "github", @@ -7016,12 +7121,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.68.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.68.0.tgz", - "integrity": "sha512-mMOdGDKlwTP/WV72QqSNf4PAMeoBp/DqBHQ222wBfb51Looi8QUqnCnb9O98ZgvNISmy6fzxRGBJdZ+9IBvX2Q==", + "version": "5.76.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.76.0.tgz", + "integrity": "sha512-dZLYzVuUFZJkenxd8o01oyFimeLBmSkaUviPHuDzXe7LSLO4WTTx92jwJlNUXOOHzg6t0XknklZ15cjhYNSDjA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.68.0" + "@tanstack/query-core": "5.76.0" }, "funding": { "type": "github", @@ -7120,10 +7225,11 @@ "license": "MIT" }, "node_modules/@testing-library/react": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.2.0.tgz", - "integrity": "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" }, @@ -7461,9 +7567,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -7598,9 +7704,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.74", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", - "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", + "version": "18.19.100", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.100.tgz", + "integrity": "sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7621,9 +7727,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "version": "18.3.21", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.21.tgz", + "integrity": "sha512-gXLBtmlcRJeT09/sI4PxVwyrku6SaNUj/6cMubjE6T6XdY1fDmBL7r0nX0jbSZPU/Xr0KuwLLZh6aOYY5d91Xw==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -7631,9 +7737,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", "peerDependencies": { @@ -8166,17 +8272,17 @@ "license": "ISC" }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", + "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -8972,9 +9078,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8986,14 +9092,14 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -9133,6 +9239,7 @@ "version": "11.0.3", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "license": "Apache-2.0", "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", @@ -9146,6 +9253,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "license": "MIT", "dependencies": { "lodash-es": "^4.17.21" }, @@ -9153,6 +9261,15 @@ "chevrotain": "^11.0.0" } }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -9212,388 +9329,30 @@ "node": ">=6" } }, - "node_modules/cmdk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz", - "integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-dialog": "1.0.5", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/cmdk/node_modules/@radix-ui/primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dialog": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", - "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", - "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", - "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-id/node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-portal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", - "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-presence": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", - "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -9601,42 +9360,40 @@ } } }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state/node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "node_modules/cmdk/node_modules/@radix-ui/react-primitive": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", + "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/cmdk/node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "node_modules/cmdk/node_modules/@radix-ui/react-slot": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", + "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", "license": "MIT", "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -9894,6 +9651,15 @@ "node": ">=8.0.0" } }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -10467,6 +10233,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -10637,15 +10413,12 @@ } }, "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, "node_modules/detect-newline": { @@ -10808,9 +10581,9 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", - "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -11024,9 +10797,9 @@ } }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -11036,31 +10809,31 @@ "node": ">=18" }, "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/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, "node_modules/esbuild-register": { @@ -11121,6 +10894,17 @@ "source-map": "~0.6.1" } }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", @@ -11675,9 +11459,9 @@ "license": "MIT" }, "node_modules/eslint-plugin-react": { - "version": "7.37.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", - "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", "dependencies": { @@ -11691,7 +11475,7 @@ "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", @@ -11752,9 +11536,9 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.11.2.tgz", - "integrity": "sha512-0Z4DUklJrC+GHjCRXa7PYfPzWC15DaVnwaOYenpgXiCEijXPZkLKCms+rHhtoRcWccP7Z8DpOOaP1gc3P9oOwg==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.12.0.tgz", + "integrity": "sha512-Lg5I0+npTgiYgZ4KSvGWGDFZi3eOCNJPaWX0c9rTEEXC5wvooOClsP9ZtbI4hhFKyKgYR877KiJxbRTSJq9gWA==", "dev": true, "license": "MIT", "dependencies": { @@ -11770,14 +11554,14 @@ } }, "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/scope-manager": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", - "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.0.tgz", + "integrity": "sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0" + "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/visitor-keys": "8.32.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11788,9 +11572,9 @@ } }, "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/types": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", - "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.0.tgz", + "integrity": "sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==", "dev": true, "license": "MIT", "engines": { @@ -11802,20 +11586,20 @@ } }, "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", - "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.0.tgz", + "integrity": "sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0", + "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/visitor-keys": "8.32.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11825,20 +11609,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/utils": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", - "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.0.tgz", + "integrity": "sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.22.0", - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/typescript-estree": "8.22.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.0", + "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/typescript-estree": "8.32.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11849,17 +11633,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", - "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.0.tgz", + "integrity": "sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/types": "8.32.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -11910,9 +11694,9 @@ } }, "node_modules/eslint-plugin-storybook/node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -11923,9 +11707,9 @@ } }, "node_modules/eslint-plugin-storybook/node_modules/ts-api-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "license": "MIT", "engines": { @@ -12732,18 +12516,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -13386,9 +13170,9 @@ "license": "BSD-3-Clause" }, "node_modules/i18next": { - "version": "24.2.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.2.tgz", - "integrity": "sha512-NE6i86lBCKRYZa5TaUDkU5S4HFgLIEJRLr3Whf2psgaxBleQ2LC1YW1Vc+SCgkAW7VEzndT6al6+CzegSUHcTQ==", + "version": "24.2.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz", + "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==", "funding": [ { "type": "individual", @@ -13403,8 +13187,9 @@ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" } ], + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.2" + "@babel/runtime": "^7.26.10" }, "peerDependencies": { "typescript": "^5" @@ -14213,6 +13998,16 @@ "node": ">=10" } }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/istanbul-reports": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", @@ -15512,9 +15307,10 @@ "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==" }, "node_modules/langium": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz", - "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", + "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "license": "MIT", "dependencies": { "chevrotain": "~11.0.3", "chevrotain-allstar": "~0.3.0", @@ -15576,12 +15372,12 @@ } }, "node_modules/lightningcss": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz", - "integrity": "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", "license": "MPL-2.0", "dependencies": { - "detect-libc": "^1.0.3" + "detect-libc": "^2.0.3" }, "engines": { "node": ">= 12.0.0" @@ -15591,22 +15387,22 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-darwin-arm64": "1.29.1", - "lightningcss-darwin-x64": "1.29.1", - "lightningcss-freebsd-x64": "1.29.1", - "lightningcss-linux-arm-gnueabihf": "1.29.1", - "lightningcss-linux-arm64-gnu": "1.29.1", - "lightningcss-linux-arm64-musl": "1.29.1", - "lightningcss-linux-x64-gnu": "1.29.1", - "lightningcss-linux-x64-musl": "1.29.1", - "lightningcss-win32-arm64-msvc": "1.29.1", - "lightningcss-win32-x64-msvc": "1.29.1" + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.1.tgz", - "integrity": "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", "cpu": [ "arm64" ], @@ -15624,9 +15420,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.1.tgz", - "integrity": "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", "cpu": [ "x64" ], @@ -15644,9 +15440,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.1.tgz", - "integrity": "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", "cpu": [ "x64" ], @@ -15664,9 +15460,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.1.tgz", - "integrity": "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", "cpu": [ "arm" ], @@ -15684,9 +15480,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.1.tgz", - "integrity": "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", "cpu": [ "arm64" ], @@ -15704,9 +15500,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.1.tgz", - "integrity": "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", "cpu": [ "arm64" ], @@ -15724,9 +15520,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.1.tgz", - "integrity": "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", "cpu": [ "x64" ], @@ -15744,9 +15540,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.1.tgz", - "integrity": "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", "cpu": [ "x64" ], @@ -15764,9 +15560,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.1.tgz", - "integrity": "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", "cpu": [ "arm64" ], @@ -15784,9 +15580,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.1.tgz", - "integrity": "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", "cpu": [ "x64" ], @@ -15923,9 +15719,9 @@ } }, "node_modules/lucide-react": { - "version": "0.475.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.475.0.tgz", - "integrity": "sha512-NJzvVu1HwFVeZ+Gwq2q00KygM1aBhy/ZrhY9FsAgJtpB+E4R7uxRk9M2iKvHa6/vNxZydIB59htha4c2vvwvVg==", + "version": "0.510.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.510.0.tgz", + "integrity": "sha512-p8SQRAMVh7NhsAIETokSqDrc5CHnDLbV29mMnzaXx+Vc/hnqQzwI2r0FMWCcoTXnbw2KEjy48xwpGdEL+ck06Q==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -15945,7 +15741,6 @@ "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -16015,9 +15810,10 @@ } }, "node_modules/marked": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", - "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", + "version": "15.0.11", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.11.tgz", + "integrity": "sha512-1BEXAU2euRCG3xwgLVT1y0xbJEld1XOrmRJpUwRCcy7rxhSCwMrmEu9LXoPhHSCJG41V7YcQ2mjKRr5BA3ITIA==", + "license": "MIT", "bin": { "marked": "bin/marked.js" }, @@ -17345,30 +17141,44 @@ } }, "node_modules/mermaid": { - "version": "11.4.1", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz", - "integrity": "sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", + "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", + "license": "MIT", "dependencies": { - "@braintree/sanitize-url": "^7.0.1", - "@iconify/utils": "^2.1.32", - "@mermaid-js/parser": "^0.3.0", + "@braintree/sanitize-url": "^7.0.4", + "@iconify/utils": "^2.1.33", + "@mermaid-js/parser": "^0.4.0", "@types/d3": "^7.4.3", - "cytoscape": "^3.29.2", + "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", - "dayjs": "^1.11.10", - "dompurify": "^3.2.1", + "dayjs": "^1.11.13", + "dompurify": "^3.2.4", "katex": "^0.16.9", "khroma": "^2.1.0", "lodash-es": "^4.17.21", - "marked": "^13.0.2", + "marked": "^15.0.7", "roughjs": "^4.6.6", - "stylis": "^4.3.1", + "stylis": "^4.3.6", "ts-dedent": "^2.2.0", - "uuid": "^9.0.1" + "uuid": "^11.1.0" + } + }, + "node_modules/mermaid/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" } }, "node_modules/micromark": { @@ -18003,12 +17813,38 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mlly": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", @@ -18215,15 +18051,16 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -18309,18 +18146,18 @@ } }, "node_modules/oniguruma-parser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.0.tgz", - "integrity": "sha512-fD9o5ebCmEAA9dLysajdQvuKzLL7cj+w7DQjuO3Cb6IwafENfx6iL+RGkmyW82pVRsvgzixsWinHvgxTMJvdIA==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", "license": "MIT" }, "node_modules/oniguruma-to-es": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.1.tgz", - "integrity": "sha512-VtX1kepWO+7HG7IWV5v72JhiqofK7XsiHmtgnvurnNOTdIvE5mrdWYtsOrQyrXCv1L2Ckm08hywp+MFO7rC4Ug==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", + "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", "license": "MIT", "dependencies": { - "oniguruma-parser": "^0.12.0", + "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } @@ -18726,9 +18563,9 @@ } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -18760,14 +18597,15 @@ "license": "MIT" }, "node_modules/posthog-js": { - "version": "1.227.2", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.227.2.tgz", - "integrity": "sha512-McEerqeQHZpV+enlVqOXCcGUFtV3FZb4AmYkN8xU9mm0VRpa1feyEF7pFZJabKWLrqba0MrVpY6b6dse17HrOQ==", + "version": "1.242.1", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.242.1.tgz", + "integrity": "sha512-j2mzw0eukyuw/Qm3tNZ6pfaXmc7eglWj6ftmvR1Lz9GtMr85ndGNXJvIGO+6PBrQW2o0D1G0k/KV93ehta0hFA==", + "license": "SEE LICENSE IN LICENSE", "dependencies": { "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", - "web-vitals": "^4.2.0" + "web-vitals": "^4.2.4" }, "peerDependencies": { "@rrweb/types": "2.0.0-alpha.17", @@ -19042,16 +18880,18 @@ } }, "node_modules/react-i18next": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.1.tgz", - "integrity": "sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw==", + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.1.tgz", + "integrity": "sha512-C8RZ7N7H0L+flitiX6ASjq9p5puVJU1Z8VyL3OgM/QOMRf40BMZX+5TkpxzZVcTmOLPX5zlti4InEX5pFyiVeA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", "html-parse-stringify": "^3.0.1" }, "peerDependencies": { "i18next": ">= 23.2.3", - "react": ">= 16.8.0" + "react": ">= 16.8.0", + "typescript": "^5" }, "peerDependenciesMeta": { "react-dom": { @@ -19059,6 +18899,9 @@ }, "react-native": { "optional": true + }, + "typescript": { + "optional": true } } }, @@ -19070,12 +18913,13 @@ "license": "MIT" }, "node_modules/react-markdown": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.3.tgz", - "integrity": "sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz", + "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", @@ -19185,9 +19029,9 @@ } }, "node_modules/react-markdown/node_modules/micromark": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", - "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", "funding": [ { "type": "GitHub Sponsors", @@ -19236,9 +19080,9 @@ } }, "node_modules/react-markdown/node_modules/remark-rehype": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", - "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -19308,9 +19152,9 @@ } }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "license": "MIT", "engines": { @@ -19409,9 +19253,9 @@ } }, "node_modules/react-textarea-autosize": { - "version": "8.5.7", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.7.tgz", - "integrity": "sha512-2MqJ3p0Jh69yt9ktFIaZmORHXw4c4bxSIhCeWiFwmJ9EYKgLmuNII3e9c9b2UO+ijl4StnpZdqpxNIhTdHvqtQ==", + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.13", @@ -19461,16 +19305,13 @@ } }, "node_modules/react-virtuoso": { - "version": "4.12.3", - "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.3.tgz", - "integrity": "sha512-6X1p/sU7hecmjDZMAwN+r3go9EVjofKhwkUbVlL8lXhBZecPv9XVCkZ/kBPYOr0Mv0Vl5+Ziwgexg9Kh7+NNXQ==", + "version": "4.12.7", + "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.7.tgz", + "integrity": "sha512-njJp764he6Fi1p89PUW0k2kbyWu9w/y+MwdxmwK2kvdwwzVDbz2c2wMj5xdSruBFVgFTsI7Z85hxZR7aSHBrbQ==", "license": "MIT", - "engines": { - "node": ">=10" - }, "peerDependencies": { - "react": ">=16 || >=17 || >= 18", - "react-dom": ">=16 || >=17 || >= 18" + "react": ">=16 || >=17 || >= 18 || >= 19", + "react-dom": ">=16 || >=17 || >= 18 || >=19" } }, "node_modules/recast": { @@ -19490,6 +19331,16 @@ "node": ">= 4" } }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -19971,9 +19822,10 @@ } }, "node_modules/remove-markdown": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/remove-markdown/-/remove-markdown-0.6.0.tgz", - "integrity": "sha512-B9g8yo5Zp1wXfZ77M1RLpqI7xrBBERkp7+3/Btm9N/uZV5xhXZjzIxDbCKz7CSj141lWDuCnQuH12DKLUv4Ghw==" + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/remove-markdown/-/remove-markdown-0.6.2.tgz", + "integrity": "sha512-EijDXJZbzpGbQBd852ViUzcqgpMujthM+SAEHiWCMcZonRbZ+xViWKLJA/vrwbDwYdxrs1aFDjpBhcGrZoJRGA==", + "license": "MIT" }, "node_modules/require-directory": { "version": "2.1.1", @@ -20096,12 +19948,12 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz", - "integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==", + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", + "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -20111,25 +19963,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.32.1", - "@rollup/rollup-android-arm64": "4.32.1", - "@rollup/rollup-darwin-arm64": "4.32.1", - "@rollup/rollup-darwin-x64": "4.32.1", - "@rollup/rollup-freebsd-arm64": "4.32.1", - "@rollup/rollup-freebsd-x64": "4.32.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.32.1", - "@rollup/rollup-linux-arm-musleabihf": "4.32.1", - "@rollup/rollup-linux-arm64-gnu": "4.32.1", - "@rollup/rollup-linux-arm64-musl": "4.32.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.32.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.32.1", - "@rollup/rollup-linux-riscv64-gnu": "4.32.1", - "@rollup/rollup-linux-s390x-gnu": "4.32.1", - "@rollup/rollup-linux-x64-gnu": "4.32.1", - "@rollup/rollup-linux-x64-musl": "4.32.1", - "@rollup/rollup-win32-arm64-msvc": "4.32.1", - "@rollup/rollup-win32-ia32-msvc": "4.32.1", - "@rollup/rollup-win32-x64-msvc": "4.32.1", + "@rollup/rollup-android-arm-eabi": "4.40.2", + "@rollup/rollup-android-arm64": "4.40.2", + "@rollup/rollup-darwin-arm64": "4.40.2", + "@rollup/rollup-darwin-x64": "4.40.2", + "@rollup/rollup-freebsd-arm64": "4.40.2", + "@rollup/rollup-freebsd-x64": "4.40.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", + "@rollup/rollup-linux-arm-musleabihf": "4.40.2", + "@rollup/rollup-linux-arm64-gnu": "4.40.2", + "@rollup/rollup-linux-arm64-musl": "4.40.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", + "@rollup/rollup-linux-riscv64-gnu": "4.40.2", + "@rollup/rollup-linux-riscv64-musl": "4.40.2", + "@rollup/rollup-linux-s390x-gnu": "4.40.2", + "@rollup/rollup-linux-x64-gnu": "4.40.2", + "@rollup/rollup-linux-x64-musl": "4.40.2", + "@rollup/rollup-win32-arm64-msvc": "4.40.2", + "@rollup/rollup-win32-ia32-msvc": "4.40.2", + "@rollup/rollup-win32-x64-msvc": "4.40.2", "fsevents": "~2.3.2" } }, @@ -20392,17 +20245,17 @@ } }, "node_modules/shiki": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.3.0.tgz", - "integrity": "sha512-j0Z1tG5vlOFGW8JVj0Cpuatzvshes7VJy5ncDmmMaYcmnGW0Js1N81TOW98ivTFNZfKRn9uwEg/aIm638o368g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.4.0.tgz", + "integrity": "sha512-Ni80XHcqhOEXv5mmDAvf5p6PAJqbUc/RzFeaOqk+zP5DLvTPS3j0ckvA+MI87qoxTQ5RGJDVTbdl/ENLSyyAnQ==", "license": "MIT", "dependencies": { - "@shikijs/core": "3.3.0", - "@shikijs/engine-javascript": "3.3.0", - "@shikijs/engine-oniguruma": "3.3.0", - "@shikijs/langs": "3.3.0", - "@shikijs/themes": "3.3.0", - "@shikijs/types": "3.3.0", + "@shikijs/core": "3.4.0", + "@shikijs/engine-javascript": "3.4.0", + "@shikijs/engine-oniguruma": "3.4.0", + "@shikijs/langs": "3.4.0", + "@shikijs/themes": "3.4.0", + "@shikijs/types": "3.4.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } @@ -20508,12 +20361,12 @@ } }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, "node_modules/source-map-js": { @@ -20536,6 +20389,16 @@ "source-map": "^0.6.0" } }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/space-separated-tokens": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", @@ -20622,13 +20485,13 @@ } }, "node_modules/storybook": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.7.tgz", - "integrity": "sha512-9gktoFMQDSCINNGQH869d/sar9rVtAhr0HchcvDA6bssAqgQJvTphY4qC9lH54SxfTJm/7Sy+BKEngMK+dziJg==", + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.12.tgz", + "integrity": "sha512-Z/nWYEHBTLK1ZBtAWdhxC0l5zf7ioJ7G4+zYqtTdYeb67gTnxNj80gehf8o8QY9L2zA2+eyMRGLC2V5fI7Z3Tw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core": "8.6.7" + "@storybook/core": "8.6.12" }, "bin": { "getstorybook": "bin/index.cjs", @@ -20937,9 +20800,9 @@ } }, "node_modules/styled-components": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.14.tgz", - "integrity": "sha512-KtfwhU5jw7UoxdM0g6XU9VZQFV4do+KrM8idiVCH5h4v49W+3p3yMe0icYwJgZQZepa5DbH04Qv8P0/RdcLcgg==", + "version": "6.1.18", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.18.tgz", + "integrity": "sha512-Mvf3gJFzZCkhjY2Y/Fx9z1m3dxbza0uI9H1CbNZm/jSHCojzJhQ0R7bByrlFJINnMzz/gPulpoFFGymNwrsMcw==", "license": "MIT", "dependencies": { "@emotion/is-prop-valid": "1.2.2", @@ -20947,7 +20810,7 @@ "@types/stylis": "4.2.5", "css-to-react-native": "3.2.0", "csstype": "3.1.3", - "postcss": "8.4.38", + "postcss": "8.4.49", "shallowequal": "1.1.0", "stylis": "4.3.2", "tslib": "2.6.2" @@ -20965,9 +20828,9 @@ } }, "node_modules/styled-components/node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "funding": [ { "type": "opencollective", @@ -20985,8 +20848,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -21005,9 +20868,9 @@ "license": "0BSD" }, "node_modules/stylis": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.5.tgz", - "integrity": "sha512-K7npNOKGRYuhAFFzkzMGfxFDpN6gDwf8hcMiE+uveTVbBgm93HrNP3ZDUpKqzZ4pG7TP6fmb+EMAQPjq9FqqvA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", "license": "MIT" }, "node_modules/supports-color": { @@ -21060,9 +20923,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0.tgz", - "integrity": "sha512-ULRPI3A+e39T7pSaf1xoi58AqqJxVCLg8F/uM5A3FadUbnyDTgltVnXJvdkTjwCOGA6NazqHVcwPJC5h2vRYVQ==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.6.tgz", + "integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==", "license": "MIT" }, "node_modules/tailwindcss-animate": { @@ -21083,6 +20946,32 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -21126,6 +21015,48 @@ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -21230,9 +21161,9 @@ "license": "Unlicense" }, "node_modules/ts-jest": { - "version": "29.2.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz", - "integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==", + "version": "29.3.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.2.tgz", + "integrity": "sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug==", "dev": true, "license": "MIT", "dependencies": { @@ -21244,6 +21175,7 @@ "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.7.1", + "type-fest": "^4.39.1", "yargs-parser": "^21.1.1" }, "bin": { @@ -21291,6 +21223,19 @@ "node": ">=10" } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -21460,10 +21405,11 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21924,6 +21870,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -21990,14 +21937,17 @@ } }, "node_modules/vite": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz", - "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "license": "MIT", "dependencies": { - "esbuild": "^0.24.2", - "postcss": "^8.4.49", - "rollup": "^4.23.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -22060,6 +22010,32 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -22072,6 +22048,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -22080,6 +22057,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, @@ -22091,6 +22069,7 @@ "version": "3.17.5", "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" @@ -22099,17 +22078,20 @@ "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==" + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" }, "node_modules/vscode-languageserver-types": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" }, "node_modules/vscode-uri": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "license": "MIT" }, "node_modules/vscrui": { "version": "0.2.2", @@ -22497,9 +22479,9 @@ } }, "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", + "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/webview-ui/package.json b/webview-ui/package.json index 69adac66f32..50a7dbd43f8 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "lint": "ESLINT_USE_FLAT_CONFIG=false eslint src --ext .ts,.tsx", + "lint": "ESLINT_USE_FLAT_CONFIG=false eslint src --ext .ts,.tsx --max-warnings=0", "check-types": "tsc", "test": "jest -w=40%", "dev": "vite", @@ -39,13 +39,14 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.0.0", + "date-fns": "^4.1.0", "debounce": "^2.1.1", "fast-deep-equal": "^3.1.3", "fzf": "^0.5.2", "i18next": "^24.2.2", "i18next-http-backend": "^3.0.2", "knuth-shuffle-seeded": "^1.0.6", - "lucide-react": "^0.475.0", + "lucide-react": "^0.510.0", "mermaid": "^11.4.1", "posthog-js": "^1.227.2", "react": "^18.3.1", @@ -61,6 +62,7 @@ "remove-markdown": "^0.6.0", "shell-quote": "^1.8.2", "shiki": "^3.2.1", + "source-map": "^0.7.4", "styled-components": "^6.1.13", "tailwind-merge": "^2.6.0", "tailwindcss": "^4.0.0", @@ -90,7 +92,7 @@ "eslint-config-react-app": "^7.0.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-storybook": "^0.11.2", + "eslint-plugin-storybook": "^0.12.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -99,6 +101,6 @@ "storybook-dark-mode": "^4.0.2", "ts-jest": "^29.2.5", "typescript": "^5.4.5", - "vite": "6.0.11" + "vite": "6.3.5" } } diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx index b3a79cde7c7..0d93d9da0cf 100644 --- a/webview-ui/src/components/chat/Announcement.tsx +++ b/webview-ui/src/components/chat/Announcement.tsx @@ -1,113 +1,94 @@ -import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { memo } from "react" -import { useAppTranslation } from "@/i18n/TranslationContext" +import { useState, memo } from "react" import { Trans } from "react-i18next" +import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@src/components/ui" interface AnnouncementProps { hideAnnouncement: () => void } -/* -You must update the latestAnnouncementId in ClineProvider for new announcements to show to users. This new id will be compared with whats in state for the 'last announcement shown', and if it's different then the announcement will render. As soon as an announcement is shown, the id will be updated in state. This ensures that announcements are not shown more than once, even if the user doesn't close it themselves. -*/ -const Announcement = ({ hideAnnouncement }: AnnouncementProps) => { - const { t } = useAppTranslation() - const discordLink = ( - { - e.preventDefault() - window.postMessage( - { type: "action", action: "openExternal", data: { url: "https://discord.gg/roocode" } }, - "*", - ) - }}> - Discord - - ) +/** + * You must update the `latestAnnouncementId` in ClineProvider for new + * announcements to show to users. This new id will be compared with what's in + * state for the 'last announcement shown', and if it's different then the + * announcement will render. As soon as an announcement is shown, the id will be + * updated in state. This ensures that announcements are not shown more than + * once, even if the user doesn't close it themselves. + */ - const redditLink = ( - { - e.preventDefault() - window.postMessage( - { type: "action", action: "openExternal", data: { url: "https://reddit.com/r/RooCode" } }, - "*", - ) - }}> - Reddit - - ) +const Announcement = ({ hideAnnouncement }: AnnouncementProps) => { + const { t } = useAppTranslation() + const [open, setOpen] = useState(true) return ( -
-
- - - -

{t("chat:announcement.title")}

- -

{t("chat:announcement.description")}

- -

{t("chat:announcement.whatsNew")}

-
    -
  • - •{" "} - , - code: , - }} - /> -
  • -
  • - •{" "} - , - code: , - }} - /> -
  • -
  • - •{" "} - , - code: , - }} - /> -
  • -
+ { + setOpen(open) -

+ if (!open) { + hideAnnouncement() + } + }}> + + + {t("chat:announcement.title")} + {t("chat:announcement.description")} + +

+

{t("chat:announcement.whatsNew")}

+
    +
  • + •{" "} + , code: }} /> +
  • +
  • + •{" "} + , code: }} /> +
  • +
  • + •{" "} + , code: }} /> +
  • +
, redditLink: }} /> -

-
-
+
+ + ) } +const DiscordLink = () => ( + { + e.preventDefault() + window.postMessage( + { type: "action", action: "openExternal", data: { url: "https://discord.gg/roocode" } }, + "*", + ) + }}> + Discord + +) + +const RedditLink = () => ( + { + e.preventDefault() + window.postMessage( + { type: "action", action: "openExternal", data: { url: "https://reddit.com/r/RooCode" } }, + "*", + ) + }}> + Reddit + +) + export default memo(Announcement) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 2d2b2961938..98a210dc897 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -12,10 +12,12 @@ import { useCopyToClipboard } from "@src/utils/clipboard" import { useExtensionState } from "@src/context/ExtensionStateContext" import { findMatchingResourceOrTemplate } from "@src/utils/mcp" import { vscode } from "@src/utils/vscode" +import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric" import { Button } from "@src/components/ui" -import CodeAccordian, { removeLeadingNonAlphanumeric } from "../common/CodeAccordian" -import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock" +import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock" +import CodeAccordian from "../common/CodeAccordian" +import CodeBlock from "../common/CodeBlock" import MarkdownBlock from "../common/MarkdownBlock" import { ReasoningBlock } from "./ReasoningBlock" import Thumbnails from "../common/Thumbnails" @@ -288,10 +290,11 @@ export const ChatRowContent = ({
@@ -313,10 +316,11 @@ export const ChatRowContent = ({
@@ -334,10 +338,10 @@ export const ChatRowContent = ({
@@ -351,9 +355,9 @@ export const ChatRowContent = ({ {t("chat:fileOperations.wantsToCreate")}
@@ -372,47 +376,21 @@ export const ChatRowContent = ({ : t("chat:fileOperations.didRead")}
-
-
{ - vscode.postMessage({ type: "openFile", text: tool.content }) - }}> + + vscode.postMessage({ type: "openFile", text: tool.content })}> {tool.path?.startsWith(".") && .} - + {removeLeadingNonAlphanumeric(tool.path ?? "") + "\u200E"} {tool.reason}
-
-
+ style={{ fontSize: 13.5, margin: "1px 0" }} + /> + + ) case "fetchInstructions": @@ -423,8 +401,8 @@ export const ChatRowContent = ({ {t("chat:instructions.wantsToFetch")}
@@ -442,8 +420,8 @@ export const ChatRowContent = ({
@@ -511,8 +489,8 @@ export const ChatRowContent = ({
(message.text) return ( -
+
-
- {icon} - {title} -
- - + ) case "use_mcp_server": const useMcpServer = safeJsonParse(message.text) @@ -1045,6 +1019,7 @@ export const ChatRowContent = ({ )?.alwaysAllow || false, }} serverName={useMcpServer.serverName} + serverSource={server?.source} alwaysAllowMcp={alwaysAllowMcp} />
diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index ec02f3d337a..5a198f896b7 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -111,7 +111,7 @@ const CaretIcon = () => interface ChatTextAreaProps { inputValue: string setInputValue: (value: string) => void - textAreaDisabled: boolean + sendingDisabled: boolean selectApiConfigDisabled: boolean placeholderText: string selectedImages: string[] @@ -130,7 +130,7 @@ const ChatTextArea = forwardRef( { inputValue, setInputValue, - textAreaDisabled, + sendingDisabled, selectApiConfigDisabled, placeholderText, selectedImages, @@ -245,21 +245,19 @@ const ChatTextArea = forwardRef( }, [selectedType, searchQuery]) const handleEnhancePrompt = useCallback(() => { - if (!textAreaDisabled) { - const trimmedInput = inputValue.trim() - if (trimmedInput) { - setIsEnhancingPrompt(true) - const message = { - type: "enhancePrompt" as const, - text: trimmedInput, - } - vscode.postMessage(message) - } else { - const promptDescription = t("chat:enhancePromptDescription") - setInputValue(promptDescription) - } + if (sendingDisabled) { + return + } + + const trimmedInput = inputValue.trim() + + if (trimmedInput) { + setIsEnhancingPrompt(true) + vscode.postMessage({ type: "enhancePrompt" as const, text: trimmedInput }) + } else { + setInputValue(t("chat:enhancePromptDescription")) } - }, [inputValue, textAreaDisabled, setInputValue, t]) + }, [inputValue, sendingDisabled, setInputValue, t]) const queryItems = useMemo(() => { return [ @@ -441,9 +439,13 @@ const ChatTextArea = forwardRef( } const isComposing = event.nativeEvent?.isComposing ?? false + if (event.key === "Enter" && !event.shiftKey && !isComposing) { event.preventDefault() - onSend() + + if (!sendingDisabled) { + onSend() + } } if (event.key === "Backspace" && !isComposing) { @@ -452,29 +454,37 @@ const ChatTextArea = forwardRef( const charBeforeIsWhitespace = charBeforeCursor === " " || charBeforeCursor === "\n" || charBeforeCursor === "\r\n" + const charAfterIsWhitespace = charAfterCursor === " " || charAfterCursor === "\n" || charAfterCursor === "\r\n" - // checks if char before cusor is whitespace after a mention + + // Checks if char before cusor is whitespace after a mention. if ( charBeforeIsWhitespace && - inputValue.slice(0, cursorPosition - 1).match(new RegExp(mentionRegex.source + "$")) // "$" is added to ensure the match occurs at the end of the string + // "$" is added to ensure the match occurs at the end of the string. + inputValue.slice(0, cursorPosition - 1).match(new RegExp(mentionRegex.source + "$")) ) { const newCursorPosition = cursorPosition - 1 - // if mention is followed by another word, then instead of deleting the space separating them we just move the cursor to the end of the mention + // If mention is followed by another word, then instead + // of deleting the space separating them we just move + // the cursor to the end of the mention. if (!charAfterIsWhitespace) { event.preventDefault() textAreaRef.current?.setSelectionRange(newCursorPosition, newCursorPosition) setCursorPosition(newCursorPosition) } + setCursorPosition(newCursorPosition) setJustDeletedSpaceAfterMention(true) } else if (justDeletedSpaceAfterMention) { const { newText, newPosition } = removeMention(inputValue, cursorPosition) + if (newText !== inputValue) { event.preventDefault() setInputValue(newText) setIntendedCursorPosition(newPosition) // Store the new cursor position in state } + setJustDeletedSpaceAfterMention(false) setShowContextMenu(false) } else { @@ -483,6 +493,7 @@ const ChatTextArea = forwardRef( } }, [ + sendingDisabled, onSend, showContextMenu, searchQuery, @@ -505,63 +516,67 @@ const ChatTextArea = forwardRef( setIntendedCursorPosition(null) // Reset the state. } }, [inputValue, intendedCursorPosition]) - // Ref to store the search timeout + + // Ref to store the search timeout. const searchTimeoutRef = useRef(null) const handleInputChange = useCallback( (e: React.ChangeEvent) => { const newValue = e.target.value - const newCursorPosition = e.target.selectionStart setInputValue(newValue) + + const newCursorPosition = e.target.selectionStart setCursorPosition(newCursorPosition) - const showMenu = shouldShowContextMenu(newValue, newCursorPosition) + const showMenu = shouldShowContextMenu(newValue, newCursorPosition) setShowContextMenu(showMenu) + if (showMenu) { if (newValue.startsWith("/")) { - // Handle slash command + // Handle slash command. const query = newValue setSearchQuery(query) setSelectedMenuIndex(0) } else { - // Existing @ mention handling + // Existing @ mention handling. const lastAtIndex = newValue.lastIndexOf("@", newCursorPosition - 1) const query = newValue.slice(lastAtIndex + 1, newCursorPosition) setSearchQuery(query) - // Send file search request if query is not empty + // Send file search request if query is not empty. if (query.length > 0) { setSelectedMenuIndex(0) - // Don't clear results until we have new ones - // This prevents flickering - // Clear any existing timeout + // Don't clear results until we have new ones. This + // prevents flickering. + + // Clear any existing timeout. if (searchTimeoutRef.current) { clearTimeout(searchTimeoutRef.current) } - // Set a timeout to debounce the search requests + // Set a timeout to debounce the search requests. searchTimeoutRef.current = setTimeout(() => { - // Generate a request ID for this search + // Generate a request ID for this search. const reqId = Math.random().toString(36).substring(2, 9) setSearchRequestId(reqId) setSearchLoading(true) - // Send message to extension to search files + // Send message to extension to search files. vscode.postMessage({ type: "searchFiles", query: unescapeSpaces(query), requestId: reqId, }) - }, 200) // 200ms debounce + }, 200) // 200ms debounce. } else { - setSelectedMenuIndex(3) // Set to "File" option by default + setSelectedMenuIndex(3) // Set to "File" option by default. } } } else { setSearchQuery("") setSelectedMenuIndex(-1) - setFileSearchResults([]) // Clear file search results + setFileSearchResults([]) // Clear file search results. } }, [setInputValue, setSearchRequestId, setFileSearchResults, setSearchLoading], @@ -621,14 +636,18 @@ const ChatTextArea = forwardRef( if (!shouldDisableImages && imageItems.length > 0) { e.preventDefault() + const imagePromises = imageItems.map((item) => { return new Promise((resolve) => { const blob = item.getAsFile() + if (!blob) { resolve(null) return } + const reader = new FileReader() + reader.onloadend = () => { if (reader.error) { console.error(t("chat:errorReadingFile"), reader.error) @@ -638,11 +657,14 @@ const ChatTextArea = forwardRef( resolve(typeof result === "string" ? result : null) } } + reader.readAsDataURL(blob) }) }) + const imageDataArray = await Promise.all(imagePromises) const dataUrls = imageDataArray.filter((dataUrl): dataUrl is string => dataUrl !== null) + if (dataUrls.length > 0) { setSelectedImages((prevImages) => [...prevImages, ...dataUrls].slice(0, MAX_IMAGES_PER_MESSAGE)) } else { @@ -737,8 +759,10 @@ const ChatTextArea = forwardRef( } const files = Array.from(e.dataTransfer.files) - if (!textAreaDisabled && files.length > 0) { + + if (files.length > 0) { const acceptedTypes = ["png", "jpeg", "webp"] + const imageFiles = files.filter((file) => { const [type, subtype] = file.type.split("/") return type === "image" && acceptedTypes.includes(subtype) @@ -748,6 +772,7 @@ const ChatTextArea = forwardRef( const imagePromises = imageFiles.map((file) => { return new Promise((resolve) => { const reader = new FileReader() + reader.onloadend = () => { if (reader.error) { console.error(t("chat:errorReadingFile"), reader.error) @@ -757,20 +782,21 @@ const ChatTextArea = forwardRef( resolve(typeof result === "string" ? result : null) } } + reader.readAsDataURL(file) }) }) + const imageDataArray = await Promise.all(imagePromises) const dataUrls = imageDataArray.filter((dataUrl): dataUrl is string => dataUrl !== null) + if (dataUrls.length > 0) { setSelectedImages((prevImages) => [...prevImages, ...dataUrls].slice(0, MAX_IMAGES_PER_MESSAGE), ) + if (typeof vscode !== "undefined") { - vscode.postMessage({ - type: "draggedImages", - dataUrls: dataUrls, - }) + vscode.postMessage({ type: "draggedImages", dataUrls: dataUrls }) } } else { console.warn(t("chat:noValidImages")) @@ -785,7 +811,6 @@ const ChatTextArea = forwardRef( setInputValue, setCursorPosition, setIntendedCursorPosition, - textAreaDisabled, shouldDisableImages, setSelectedImages, t, @@ -832,11 +857,12 @@ const ChatTextArea = forwardRef( className={cn("chat-text-area", "relative", "flex", "flex-col", "outline-none")} onDrop={handleDrop} onDragOver={(e) => { - //Only allowed to drop images/files on shift key pressed + // Only allowed to drop images/files on shift key pressed. if (!e.shiftKey) { setIsDraggingOver(false) return } + e.preventDefault() setIsDraggingOver(true) e.dataTransfer.dropEffect = "copy" @@ -844,6 +870,7 @@ const ChatTextArea = forwardRef( onDragLeave={(e) => { e.preventDefault() const rect = e.currentTarget.getBoundingClientRect() + if ( e.clientX <= rect.left || e.clientX >= rect.right || @@ -922,7 +949,6 @@ const ChatTextArea = forwardRef( textAreaRef.current = el }} value={inputValue} - disabled={textAreaDisabled} onChange={(e) => { handleInputChange(e) updateHighlights() @@ -938,6 +964,7 @@ const ChatTextArea = forwardRef( if (textAreaBaseHeight === undefined || height < textAreaBaseHeight) { setTextAreaBaseHeight(height) } + onHeightChange?.(height) }} placeholder={placeholderText} @@ -947,8 +974,16 @@ const ChatTextArea = forwardRef( className={cn( "w-full", "text-vscode-input-foreground", - textAreaDisabled ? "cursor-not-allowed" : "cursor-text", + "font-vscode-font-family", + "text-vscode-editor-font-size", + "leading-vscode-editor-line-height", + "cursor-text", "py-1.5 px-2", + isFocused + ? "border border-vscode-focusBorder outline outline-vscode-focusBorder" + : isDraggingOver + ? "border-2 border-dashed border-vscode-focusBorder" + : "border border-transparent", isDraggingOver ? "border-2 border-dashed border-vscode-focusBorder" : "border-none outline-none focus:outline-none focus:ring-0", @@ -975,6 +1010,7 @@ const ChatTextArea = forwardRef( }} onScroll={() => updateHighlights()} /> + {isTtsPlaying && ( )} + {!inputValue && (
( "transition-opacity", "duration-200", "ease-in-out", - textAreaDisabled ? "opacity-35" : "opacity-70", + "opacity-70", )}> {placeholderBottomText}
@@ -1023,7 +1060,6 @@ const ChatTextArea = forwardRef(
- {/* Mode selector - fixed width */}
( />
- {/* API configuration selector - flexible width */}
pinnedApiConfigs && pinnedApiConfigs[config.id]) .map((config) => ({ value: config.id, label: config.name, - name: config.name, // Keep name for comparison with currentApiConfigName + name: config.name, // Keep name for comparison with currentApiConfigName. type: DropdownOptionType.ITEM, pinned: true, })) .sort((a, b) => a.label.localeCompare(b.label)), - // If we have pinned items and unpinned items, add a separator + // If we have pinned items and unpinned items, add a separator. ...(pinnedApiConfigs && Object.keys(pinnedApiConfigs).length > 0 && (listApiConfigMeta || []).some((config) => !pinnedApiConfigs[config.id]) @@ -1092,13 +1127,13 @@ const ChatTextArea = forwardRef( }, ] : []), - // Unpinned items sorted alphabetically + // Unpinned items sorted alphabetically. ...(listApiConfigMeta || []) .filter((config) => !pinnedApiConfigs || !pinnedApiConfigs[config.id]) .map((config) => ({ value: config.id, label: config.name, - name: config.name, // Keep name for comparison with currentApiConfigName + name: config.name, // Keep name for comparison with currentApiConfigName. type: DropdownOptionType.ITEM, pinned: false, })) @@ -1137,8 +1172,14 @@ const ChatTextArea = forwardRef( return (
-
{label}
-
+
+ {label} +
+
(
- {/* Right side - action buttons */}
( /> diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 1844d74fb26..e45a3d047a6 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -54,6 +54,7 @@ import { PlanningBar } from "./PlanningBar" import SystemPromptWarning from "./SystemPromptWarning" import { usePearAIModels } from "@/hooks/usePearAIModels" import { CheckpointWarning } from "./CheckpointWarning" +import { buildDocLink } from "@src/utils/docLinks" export interface ChatViewProps { isHidden: boolean @@ -128,7 +129,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction(null) - const [textAreaDisabled, setTextAreaDisabled] = useState(false) + const [sendingDisabled, setSendingDisabled] = useState(false) const [selectedImages, setSelectedImages] = useState([]) const [lastInputImages, setLastInputImages] = useState([]) @@ -161,9 +162,185 @@ const ChatViewComponent: React.ForwardRefRenderFunction { + // if last message is an ask, show user ask UI + // if user finished a task, then start a new task with a new conversation history since in this moment that the extension is waiting for user response, the user could close the extension and the conversation history would be lost. + // basically as long as a task is active, the conversation history will be persisted + if (lastMessage) { + switch (lastMessage.type) { + case "ask": + const isPartial = lastMessage.partial === true + switch (lastMessage.ask) { + case "api_req_failed": + playSound("progress_loop") + setSendingDisabled(true) + setClineAsk("api_req_failed") + setEnableButtons(true) + setPrimaryButtonText(t("chat:retry.title")) + setSecondaryButtonText(t("chat:startNewTask.title")) + break + case "mistake_limit_reached": + playSound("progress_loop") + setSendingDisabled(false) + setClineAsk("mistake_limit_reached") + setEnableButtons(true) + setPrimaryButtonText(t("chat:proceedAnyways.title")) + setSecondaryButtonText(t("chat:startNewTask.title")) + break + case "followup": + if (!isPartial) { + playSound("notification") + } + setSendingDisabled(isPartial) + setClineAsk("followup") + // setting enable buttons to `false` would trigger a focus grab when + // the text area is enabled which is undesirable. + // We have no buttons for this tool, so no problem having them "enabled" + // to workaround this issue. See #1358. + setEnableButtons(true) + setPrimaryButtonText(undefined) + setSecondaryButtonText(undefined) + break + case "tool": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setSendingDisabled(isPartial) + setClineAsk("tool") + setEnableButtons(!isPartial) + const tool = JSON.parse(lastMessage.text || "{}") as ClineSayTool + switch (tool.tool) { + case "editedExistingFile": + case "appliedDiff": + case "newFileCreated": + case "insertContent": + setPrimaryButtonText(t("chat:save.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + case "finishTask": + setPrimaryButtonText(t("chat:completeSubtaskAndReturn")) + setSecondaryButtonText(undefined) + break + default: + setPrimaryButtonText(t("chat:approve.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + } + break + case "browser_action_launch": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setSendingDisabled(isPartial) + setClineAsk("browser_action_launch") + setEnableButtons(!isPartial) + setPrimaryButtonText(t("chat:approve.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + case "command": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setSendingDisabled(isPartial) + setClineAsk("command") + setEnableButtons(!isPartial) + setPrimaryButtonText(t("chat:runCommand.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + case "command_output": + setSendingDisabled(false) + setClineAsk("command_output") + setEnableButtons(true) + setPrimaryButtonText(t("chat:proceedWhileRunning.title")) + setSecondaryButtonText(t("chat:killCommand.title")) + break + case "use_mcp_server": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setSendingDisabled(isPartial) + setClineAsk("use_mcp_server") + setEnableButtons(!isPartial) + setPrimaryButtonText(t("chat:approve.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + case "completion_result": + // extension waiting for feedback. but we can just present a new task button + if (!isPartial) { + playSound("celebration") + } + setSendingDisabled(isPartial) + setClineAsk("completion_result") + setEnableButtons(!isPartial) + setPrimaryButtonText(t("chat:startNewTask.title")) + setSecondaryButtonText(undefined) + break + case "resume_task": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setSendingDisabled(false) + setClineAsk("resume_task") + setEnableButtons(true) + setPrimaryButtonText(t("chat:resumeTask.title")) + setSecondaryButtonText(t("chat:terminate.title")) + setDidClickCancel(false) // special case where we reset the cancel button state + break + case "resume_completed_task": + if (!isPartial) { + playSound("celebration") + } + setSendingDisabled(false) + setClineAsk("resume_completed_task") + setEnableButtons(true) + setPrimaryButtonText(t("chat:startNewTask.title")) + setSecondaryButtonText(undefined) + setDidClickCancel(false) + break + } + break + case "say": + // Don't want to reset since there could be a "say" after + // an "ask" while ask is waiting for response. + switch (lastMessage.say) { + case "api_req_retry_delayed": + setSendingDisabled(true) + break + case "api_req_started": + if (secondLastMessage?.ask === "command_output") { + // If the last ask is a command_output, and we + // receive an api_req_started, then that means + // the command has finished and we don't need + // input from the user anymore (in every other + // case, the user has to interact with input + // field or buttons to continue, which does the + // following automatically). + setInputValue("") + setSendingDisabled(true) + setSelectedImages([]) + setClineAsk(undefined) + setEnableButtons(false) + } + break + case "api_req_finished": + case "error": + case "text": + case "browser_action": + case "browser_action_result": + case "command_output": + case "mcp_server_request_started": + case "mcp_server_response": + case "completion_result": + break + } + break + } + } + }, [lastMessage, secondLastMessage]) + useEffect(() => { if (messages.length === 0) { - setTextAreaDisabled(false) + setSendingDisabled(false) setClineAsk(undefined) setEnableButtons(false) setPrimaryButtonText(undefined) @@ -219,7 +396,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction vscode.postMessage({ type: "selectImages" }), []) const shouldDisableImages = - !model?.supportsImages || textAreaDisabled || selectedImages.length >= MAX_IMAGES_PER_MESSAGE + !model?.supportsImages || sendingDisabled || selectedImages.length >= MAX_IMAGES_PER_MESSAGE const handleMessage = useCallback( (e: MessageEvent) => { @@ -395,7 +572,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction { const timer = setTimeout(() => { - if (!isHidden && !textAreaDisabled && !enableButtons) { + if (!isHidden && !sendingDisabled && !enableButtons) { textAreaRef.current?.focus() } }, 50) @@ -475,7 +652,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction { clearTimeout(timer) } - }, [isHidden, textAreaDisabled, enableButtons]) + }, [isHidden, sendingDisabled, enableButtons]) const visibleMessages = useMemo(() => { return modifiedMessages.filter((message) => { @@ -598,7 +775,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction { - if (!autoApprovalEnabled || !message || message.type !== "ask") return false + if (!autoApprovalEnabled || !message || message.type !== "ask") { + return false + } if (message.ask === "browser_action_launch") { return alwaysAllowBrowser @@ -612,7 +791,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction { - if (isAutoApproved(lastMessage)) { + if (lastMessage?.ask && isAutoApproved(lastMessage)) { + // Note that `isAutoApproved` can only return true if + // lastMessage is an ask of type "browser_action_launch", + // "use_mcp_server", "command", or "tool". + // Add delay for write operations. - if (lastMessage?.ask === "tool" && isWriteToolAction(lastMessage)) { + if (lastMessage.ask === "tool" && isWriteToolAction(lastMessage)) { await new Promise((resolve) => setTimeout(resolve, writeDelayMs)) } - handlePrimaryButtonClick() + vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" }) + + // This is copied from `handlePrimaryButtonClick`, which we used + // to call from `autoApprove`. I'm not sure how many of these + // things are actually needed. + setInputValue("") + setSelectedImages([]) + setSendingDisabled(true) + setClineAsk(undefined) + setEnableButtons(false) } } autoApprove() @@ -1220,7 +1413,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction { if (enableButtons && primaryButtonText) { handlePrimaryButtonClick(inputValue, selectedImages) - } else if (!textAreaDisabled && (inputValue.trim() || selectedImages.length > 0)) { + } else if (!sendingDisabled && (inputValue.trim() || selectedImages.length > 0)) { handleSendMessage(inputValue, selectedImages) } }, @@ -1444,8 +1637,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction { +export const CommandExecution = ({ executionId, text, icon, title }: CommandExecutionProps) => { const { terminalShellIntegrationDisabled = false } = useExtensionState() + const { command, output: parsedOutput } = useMemo(() => parseCommandAndOutput(text), [text]) + // If we aren't opening the VSCode terminal for this command then we default // to expanding the command execution output. const [isExpanded, setIsExpanded] = useState(terminalShellIntegrationDisabled) - + const [streamingOutput, setStreamingOutput] = useState("") const [status, setStatus] = useState(null) - const [output, setOutput] = useState("") - const [command, setCommand] = useState(text) - const lines = useMemo( - () => [`$ ${command}`, ...output.split("\n").filter((line) => line.trim() !== "")], - [output, command], - ) + // The command's output can either come from the text associated with the + // task message (this is the case for completed commands) or from the + // streaming output (this is the case for running commands). + const output = streamingOutput || parsedOutput const onMessage = useCallback( (event: MessageEvent) => { - if (!executionId) { - return - } - const message: ExtensionMessage = event.data if (message.type === "commandExecutionStatus") { @@ -54,11 +52,10 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) = switch (data.status) { case "started": - setCommand(data.command) setStatus(data) break case "output": - setOutput((output) => output + data.output) + setStreamingOutput(data.output) break case "fallback": setIsExpanded(true) @@ -75,86 +72,89 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) = useEvent("message", onMessage) - useEffect(() => { - if (!status && text) { - const index = text.indexOf(COMMAND_OUTPUT_STRING) - - if (index === -1) { - setCommand(text) - } else { - setCommand(text.slice(0, index)) - setOutput(text.slice(index + COMMAND_OUTPUT_STRING.length)) - } - } - }, [status, text]) - return ( -
-
- {command} + <> +
- {status?.status === "started" && ( -
-
-
Running
- {status.pid &&
(PID: {status.pid})
} -
+
+
+ {status?.status === "started" && ( +
+
+
Running
+ {status.pid &&
(PID: {status.pid})
} + +
+ )} + {status?.status === "exited" && ( +
+
+
Exited ({status.exitCode})
+
+ )} + {output.length > 0 && ( + -
- )} - {status?.status === "exited" && ( -
-
-
Exited ({status.exitCode})
-
- )} - {lines.length > 0 && ( - - )} + )} +
-
- {lines.length > 0 && ( - {lines[i]}} - followOutput="auto" - /> - )} + +
+ +
-
+ ) } -type LineProps = HTMLAttributes +CommandExecution.displayName = "CommandExecution" -const Line = ({ className, ...props }: LineProps) => { - return ( -
- ) +const OutputContainerInternal = ({ isExpanded, output }: { isExpanded: boolean; output: string }) => ( +
+ {output.length > 0 && } +
+) + +const OutputContainer = memo(OutputContainerInternal) + +const parseCommandAndOutput = (text: string | undefined) => { + if (!text) { + return { command: "", output: "" } + } + + const index = text.indexOf(COMMAND_OUTPUT_STRING) + + if (index === -1) { + return { command: text, output: "" } + } + + return { + command: text.slice(0, index), + output: text.slice(index + COMMAND_OUTPUT_STRING.length), + } } - -CommandExecution.displayName = "CommandExecution" diff --git a/webview-ui/src/components/chat/CommandExecutionError.tsx b/webview-ui/src/components/chat/CommandExecutionError.tsx index 3c8c6273c19..f1606686876 100644 --- a/webview-ui/src/components/chat/CommandExecutionError.tsx +++ b/webview-ui/src/components/chat/CommandExecutionError.tsx @@ -1,6 +1,7 @@ import { useCallback } from "react" import { useTranslation, Trans } from "react-i18next" import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" +import { buildDocLink } from "../../utils/docLinks" export const CommandExecutionError = () => { const { t } = useTranslation() @@ -28,7 +29,7 @@ export const CommandExecutionError = () => { />
{t("chat:shellIntegration.troubleshooting")} diff --git a/webview-ui/src/components/chat/ContextMenu.tsx b/webview-ui/src/components/chat/ContextMenu.tsx index 27d53803a85..2983c023f25 100644 --- a/webview-ui/src/components/chat/ContextMenu.tsx +++ b/webview-ui/src/components/chat/ContextMenu.tsx @@ -1,13 +1,15 @@ import React, { useEffect, useMemo, useRef, useState } from "react" import { getIconForFilePath, getIconUrlByName, getIconForDirectoryPath } from "vscode-material-icons" + +import { ModeConfig } from "@roo/shared/modes" + import { ContextMenuOptionType, ContextMenuQueryItem, getContextMenuOptions, SearchResult, } from "@src/utils/context-mentions" -import { removeLeadingNonAlphanumeric } from "../common/CodeAccordian" -import { ModeConfig } from "@roo/shared/modes" +import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric" interface ContextMenuProps { onSelect: (type: ContextMenuOptionType, value?: string) => void diff --git a/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx b/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx index 0f8ad2cbc64..204821c9025 100644 --- a/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx @@ -46,7 +46,7 @@ describe("ChatTextArea", () => { inputValue: "", setInputValue: jest.fn(), onSend: jest.fn(), - textAreaDisabled: false, + sendingDisabled: false, selectApiConfigDisabled: false, onSelectImages: jest.fn(), shouldDisableImages: false, @@ -72,12 +72,12 @@ describe("ChatTextArea", () => { }) describe("enhance prompt button", () => { - it("should be disabled when textAreaDisabled is true", () => { + it("should be disabled when sendingDisabled is true", () => { ;(useExtensionState as jest.Mock).mockReturnValue({ filePaths: [], openedTabs: [], }) - render() + render() const enhanceButton = getEnhancePromptButton() expect(enhanceButton).toHaveClass("cursor-not-allowed") }) @@ -415,13 +415,13 @@ describe("ChatTextArea", () => { const getApiConfigDropdown = () => { return screen.getByTitle("chat:selectApiConfig") } - it("should be enabled independently of textAreaDisabled", () => { - render() + it("should be enabled independently of sendingDisabled", () => { + render() const apiConfigDropdown = getApiConfigDropdown() expect(apiConfigDropdown).not.toHaveAttribute("disabled") }) it("should be disabled when selectApiConfigDisabled is true", () => { - render() + render() const apiConfigDropdown = getApiConfigDropdown() expect(apiConfigDropdown).toHaveAttribute("disabled") }) diff --git a/webview-ui/src/components/chat/__tests__/ChatView.test.tsx b/webview-ui/src/components/chat/__tests__/ChatView.test.tsx index 05540f35d36..cb801806871 100644 --- a/webview-ui/src/components/chat/__tests__/ChatView.test.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatView.test.tsx @@ -59,7 +59,7 @@ jest.mock("../AutoApproveMenu", () => ({ interface ChatTextAreaProps { onSend: (value: string) => void inputValue?: string - textAreaDisabled?: boolean + sendingDisabled?: boolean placeholderText?: string selectedImages?: string[] shouldDisableImages?: boolean diff --git a/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx b/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx index 625bad1169d..f9fb71b0660 100644 --- a/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx +++ b/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx @@ -4,7 +4,7 @@ import React from "react" import { render, screen } from "@testing-library/react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { ApiConfiguration } from "@roo/shared/api" +import { ProviderSettings } from "@roo/shared/api" import TaskHeader, { TaskHeaderProps } from "../TaskHeader" @@ -27,7 +27,7 @@ jest.mock("@src/context/ExtensionStateContext", () => ({ apiProvider: "anthropic", apiKey: "test-api-key", // Add relevant fields apiModelId: "claude-3-opus-20240229", // Add relevant fields - } as ApiConfiguration, // Optional: Add type assertion if ApiConfiguration is imported + } as ProviderSettings, // Optional: Add type assertion if ProviderSettings is imported currentTaskItem: null, }), })) diff --git a/webview-ui/src/components/common/CodeAccordian.tsx b/webview-ui/src/components/common/CodeAccordian.tsx index 92f1b49b91b..4b92fe416e9 100644 --- a/webview-ui/src/components/common/CodeAccordian.tsx +++ b/webview-ui/src/components/common/CodeAccordian.tsx @@ -1,110 +1,59 @@ import { memo, useMemo } from "react" -import { getLanguageFromPath } from "@src/utils/getLanguageFromPath" -import CodeBlock, { CODE_BLOCK_BG_COLOR } from "./CodeBlock" -import { ToolProgressStatus } from "@roo/shared/ExtensionMessage" import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" +import { type ToolProgressStatus } from "@roo/shared/ExtensionMessage" +import { getLanguageFromPath } from "@src/utils/getLanguageFromPath" +import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric" + +import { ToolUseBlock, ToolUseBlockHeader } from "./ToolUseBlock" +import CodeBlock from "./CodeBlock" + interface CodeAccordianProps { + path?: string code?: string - diff?: string language?: string | undefined - path?: string - isFeedback?: boolean - isConsoleLogs?: boolean + progressStatus?: ToolProgressStatus + isLoading?: boolean isExpanded: boolean + isFeedback?: boolean onToggleExpand: () => void - isLoading?: boolean - progressStatus?: ToolProgressStatus -} - -/* -We need to remove certain leading characters from the path in order for our leading ellipses trick to work. -However, we want to preserve all language characters (including CJK, Cyrillic, etc.) and only remove specific -punctuation that might interfere with the ellipsis display. -*/ -export const removeLeadingNonAlphanumeric = (path: string): string => { - // Only remove specific punctuation characters that might interfere with ellipsis display - // Keep all language characters (including CJK, Cyrillic, etc.) and numbers - return path.replace(/^[/\\:*?"<>|]+/, "") } const CodeAccordian = ({ - code, - diff, - language, path, - isFeedback, - isConsoleLogs, + code = "", + language, + progressStatus, + isLoading, isExpanded, + isFeedback, onToggleExpand, - isLoading, - progressStatus, }: CodeAccordianProps) => { - const inferredLanguage = useMemo( - () => code && (language ?? (path ? getLanguageFromPath(path) : undefined)), - [path, language, code], - ) + const inferredLanguage = useMemo(() => language ?? (path ? getLanguageFromPath(path) : undefined), [path, language]) + const source = useMemo(() => code.trim(), [code]) + const hasHeader = Boolean(path || isFeedback) return ( -
- {(path || isFeedback || isConsoleLogs) && ( -
+ + {hasHeader && ( + {isLoading && } - {isFeedback || isConsoleLogs ? ( -
- - + {isFeedback ? ( +
+ + {isFeedback ? "User Edits" : "Console Logs"}
) : ( <> {path?.startsWith(".") && .} - + {removeLeadingNonAlphanumeric(path ?? "") + "\u200E"} )} -
+
{progressStatus && progressStatus.text && ( <> {progressStatus.icon && } @@ -114,24 +63,17 @@ const CodeAccordian = ({ )} -
+ )} - {(!(path || isFeedback || isConsoleLogs) || isExpanded) && ( -
- + {(!hasHeader || isExpanded) && ( +
+
)} -
+ ) } -// memo does shallow comparison of props, so if you need it to re-render when a nested object changes, you need to pass a custom comparison function +// Memo does shallow comparison of props, so if you need it to re-render when a +// nested object changes, you need to pass a custom comparison function. export default memo(CodeAccordian) diff --git a/webview-ui/src/components/common/CodeBlock.tsx b/webview-ui/src/components/common/CodeBlock.tsx index afd87c248fc..151a133c25d 100644 --- a/webview-ui/src/components/common/CodeBlock.tsx +++ b/webview-ui/src/components/common/CodeBlock.tsx @@ -4,8 +4,12 @@ import { useCopyToClipboard } from "@src/utils/clipboard" import { getHighlighter, isLanguageLoaded, normalizeLanguage, ExtendedLanguage } from "@src/utils/highlighter" import { bundledLanguages } from "shiki" import type { ShikiTransformer } from "shiki" +import { ChevronDown, ChevronUp, WrapText, AlignJustify, Copy, Check } from "lucide-react" +import { useAppTranslation } from "@src/i18n/TranslationContext" + export const CODE_BLOCK_BG_COLOR = "var(--vscode-editor-background, --vscode-sideBar-background, rgb(30 30 30))" export const WRAPPER_ALPHA = "cc" // 80% opacity + // Configuration constants export const WINDOW_SHADE_SETTINGS = { transitionDelayS: 0.2, @@ -34,13 +38,6 @@ interface CodeBlockProps { onLanguageChange?: (language: string) => void } -const ButtonIcon = styled.span` - display: inline-block; - width: 1.2em; - text-align: center; - vertical-align: middle; -` - const CodeBlockButton = styled.button` background: transparent; border: none; @@ -50,16 +47,23 @@ const CodeBlockButton = styled.button` margin: 0 0px; display: flex; align-items: center; + justify-content: center; opacity: 0.4; border-radius: 3px; pointer-events: var(--copy-button-events, none); margin-left: 4px; height: 24px; + width: 24px; &:hover { background: var(--vscode-toolbar-hoverBackground); opacity: 1; } + + /* Style for Lucide icons to ensure consistent sizing and positioning */ + svg { + display: block; + } ` const CodeBlockButtonWrapper = styled.div` @@ -93,7 +97,6 @@ const CodeBlockButtonWrapper = styled.div` const CodeBlockContainer = styled.div` position: relative; overflow: hidden; - border-bottom: 4px solid var(--vscode-sideBar-background); background-color: ${CODE_BLOCK_BG_COLOR}; ${CodeBlockButtonWrapper} { @@ -120,7 +123,6 @@ export const StyledPre = styled.div<{ windowshade === "true" ? `${collapsedHeight || WINDOW_SHADE_SETTINGS.collapsedHeight}px` : "none"}; overflow-y: auto; padding: 10px; - // transition: max-height ${WINDOW_SHADE_SETTINGS.transitionDelayS} ease-out; border-radius: 5px; ${({ preStyle }) => preStyle && { ...preStyle }} @@ -135,7 +137,7 @@ export const StyledPre = styled.div<{ pre, code { - /* Undefined wordwrap defaults to true (pre-wrap) behavior */ + /* Undefined wordwrap defaults to true (pre-wrap) behavior. */ white-space: ${({ wordwrap }) => (wordwrap === "false" ? "pre" : "pre-wrap")}; word-break: ${({ wordwrap }) => (wordwrap === "false" ? "normal" : "normal")}; overflow-wrap: ${({ wordwrap }) => (wordwrap === "false" ? "normal" : "break-word")}; @@ -229,25 +231,30 @@ const CodeBlock = memo( const preRef = useRef(null) const copyButtonWrapperRef = useRef(null) const { showCopyFeedback, copyWithFeedback } = useCopyToClipboard() + const { t } = useAppTranslation() - // Update current language when prop changes, but only if user hasn't made a selection + // Update current language when prop changes, but only if user hasn't + // made a selection. useEffect(() => { const normalizedLang = normalizeLanguage(language) + if (normalizedLang !== currentLanguage && !userChangedLanguageRef.current) { setCurrentLanguage(normalizedLang) } }, [language, currentLanguage]) - // Syntax highlighting with cached Shiki instance + // Syntax highlighting with cached Shiki instance. useEffect(() => { const fallback = `
${source || ""}
` + const highlight = async () => { - // Show plain text if language needs to be loaded + // Show plain text if language needs to be loaded. if (currentLanguage && !isLanguageLoaded(currentLanguage)) { setHighlightedCode(fallback) } const highlighter = await getHighlighter(currentLanguage) + const html = await highlighter.codeToHtml(source || "", { lang: currentLanguage || "txt", theme: document.body.className.toLowerCase().includes("light") ? "github-light" : "github-dark", @@ -270,6 +277,7 @@ const CodeBlock = memo( }, ] as ShikiTransformer[], }) + setHighlightedCode(html) } @@ -282,13 +290,15 @@ const CodeBlock = memo( // Check if content height exceeds collapsed height whenever content changes useEffect(() => { const codeBlock = codeBlockRef.current + if (codeBlock) { const actualHeight = codeBlock.scrollHeight setShowCollapseButton(actualHeight >= WINDOW_SHADE_SETTINGS.collapsedHeight) } }, [highlightedCode]) - // Ref to track if user was scrolled up *before* the source update potentially changes scrollHeight + // Ref to track if user was scrolled up *before* the source update + // potentially changes scrollHeight const wasScrolledUpRef = useRef(false) // Ref to track if outer container was near bottom @@ -328,13 +338,14 @@ const CodeBlock = memo( } scrollContainer.addEventListener("scroll", handleOuterScroll, { passive: true }) + // Initial check handleOuterScroll() return () => { scrollContainer.removeEventListener("scroll", handleOuterScroll) } - }, []) // Empty dependency array: runs once on mount + }, []) // Store whether we should scroll after highlighting completes const shouldScrollAfterHighlightRef = useRef(false) @@ -352,16 +363,24 @@ const CodeBlock = memo( const updateCodeBlockButtonPosition = useCallback((forceHide = false) => { const codeBlock = codeBlockRef.current const copyWrapper = copyButtonWrapperRef.current - if (!codeBlock) return + + if (!codeBlock) { + return + } const rectCodeBlock = codeBlock.getBoundingClientRect() const scrollContainer = document.querySelector('[data-virtuoso-scroller="true"]') - if (!scrollContainer) return + + if (!scrollContainer) { + return + } // Get wrapper height dynamically let wrapperHeight + if (copyWrapper) { const copyRect = copyWrapper.getBoundingClientRect() + // If height is 0 due to styling, estimate from children if (copyRect.height > 0) { wrapperHeight = copyRect.height @@ -604,16 +623,15 @@ const CodeBlock = memo( return ( - updateCodeBlockButtonPosition(true)} - onMouseUp={() => updateCodeBlockButtonPosition(false)}> -
- + highlightedCode={highlightedCode} + updateCodeBlockButtonPosition={updateCodeBlockButtonPosition} + /> {!isSelecting && ( { e.currentTarget.focus() @@ -697,19 +712,17 @@ const CodeBlock = memo( WINDOW_SHADE_SETTINGS.transitionDelayS * 1000 + 50, ) }} - title={`${windowShade ? "Expand" : "Collapse"} code block`}> - {windowShade ? "⌄" : "⌃"} + title={t(`chat:codeblock.tooltips.${windowShade ? "expand" : "collapse"}`)}> + {windowShade ? : } )} setWordWrap(!wordWrap)} - title={`${wordWrap ? "Disable" : "Enable"} word wrap`}> - - {wordWrap ? "⟼" : "⤸"} - + title={t(`chat:codeblock.tooltips.${wordWrap ? "disable_wrap" : "enable_wrap"}`)}> + {wordWrap ? : } - - + + {showCopyFeedback ? : } )} @@ -718,4 +731,39 @@ const CodeBlock = memo( }, ) +// Memoized content component to prevent unnecessary re-renders of highlighted code +const MemoizedCodeContent = memo(({ html }: { html: string }) =>
) + +// Memoized StyledPre component +const MemoizedStyledPre = memo( + ({ + preRef, + preStyle, + wordWrap, + windowShade, + collapsedHeight, + highlightedCode, + updateCodeBlockButtonPosition, + }: { + preRef: React.RefObject + preStyle?: React.CSSProperties + wordWrap: boolean + windowShade: boolean + collapsedHeight?: number + highlightedCode: string + updateCodeBlockButtonPosition: (forceHide?: boolean) => void + }) => ( + updateCodeBlockButtonPosition(true)} + onMouseUp={() => updateCodeBlockButtonPosition(false)}> + + + ), +) + export default CodeBlock diff --git a/webview-ui/src/components/common/MarkdownBlock.tsx b/webview-ui/src/components/common/MarkdownBlock.tsx index be7d5459802..a9c5ada19a9 100644 --- a/webview-ui/src/components/common/MarkdownBlock.tsx +++ b/webview-ui/src/components/common/MarkdownBlock.tsx @@ -3,6 +3,7 @@ import { useRemark } from "react-remark" import styled from "styled-components" import { visit } from "unist-util-visit" +import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" import CodeBlock from "./CodeBlock" @@ -108,11 +109,14 @@ const StyledMarkdown = styled.div` } a { - text-decoration: none; - } - a { + color: var(--vscode-textLink-foreground); + text-decoration-line: underline; + text-decoration-style: dotted; + text-decoration-color: var(--vscode-textLink-foreground); &:hover { - text-decoration: underline; + color: var(--vscode-textLink-activeForeground); + text-decoration-style: solid; + text-decoration-color: var(--vscode-textLink-activeForeground); } } ` @@ -137,6 +141,48 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => { rehypePlugins: [], rehypeReactOptions: { components: { + a: ({ href, children }: any) => { + return ( + { + // Only process file:// protocol or local file paths + const isLocalPath = + href.startsWith("file://") || href.startsWith("/") || !href.includes("://") + + if (!isLocalPath) { + return + } + + e.preventDefault() + + // Handle absolute vs project-relative paths + let filePath = href.replace("file://", "") + + // Extract line number if present + const match = filePath.match(/(.*):(\d+)(-\d+)?$/) + let values = undefined + if (match) { + filePath = match[1] + values = { line: parseInt(match[2]) } + } + + // Add ./ prefix if needed + if (!filePath.startsWith("/") && !filePath.startsWith("./")) { + filePath = "./" + filePath + } + + vscode.postMessage({ + type: "openFile", + text: filePath, + values, + }) + }}> + {children} + + ) + }, pre: ({ node: _, children }: any) => { // Check for Mermaid diagrams first if (Array.isArray(children) && children.length === 1 && React.isValidElement(children[0])) { diff --git a/webview-ui/src/components/common/Tab.tsx b/webview-ui/src/components/common/Tab.tsx index 48794320fec..99c2b7b4b0b 100644 --- a/webview-ui/src/components/common/Tab.tsx +++ b/webview-ui/src/components/common/Tab.tsx @@ -1,4 +1,4 @@ -import { HTMLAttributes, useCallback } from "react" +import React, { HTMLAttributes, useCallback, forwardRef } from "react" import { useExtensionState } from "@/context/ExtensionStateContext" import { cn } from "@/lib/utils" @@ -6,7 +6,7 @@ import { cn } from "@/lib/utils" type TabProps = HTMLAttributes export const Tab = ({ className, children, ...props }: TabProps) => ( -
+
{children}
) @@ -45,3 +45,47 @@ export const TabContent = ({ className, children, ...props }: TabProps) => {
) } + +export const TabList = forwardRef< + HTMLDivElement, + HTMLAttributes & { + value: string + onValueChange: (value: string) => void + } +>(({ children, className, value, onValueChange, ...props }, ref) => { + return ( +
+ {React.Children.map(children, (child) => { + if (React.isValidElement(child)) { + return React.cloneElement(child as React.ReactElement, { + isSelected: child.props.value === value, + onSelect: () => onValueChange(child.props.value), + }) + } + return child + })} +
+ ) +}) + +export const TabTrigger = forwardRef< + HTMLButtonElement, + React.ButtonHTMLAttributes & { + value: string + isSelected?: boolean + onSelect?: () => void + } +>(({ children, className, value: _value, isSelected, onSelect, ...props }, ref) => { + return ( + + ) +}) diff --git a/webview-ui/src/components/common/ToolUseBlock.tsx b/webview-ui/src/components/common/ToolUseBlock.tsx new file mode 100644 index 00000000000..6fb2b3a5215 --- /dev/null +++ b/webview-ui/src/components/common/ToolUseBlock.tsx @@ -0,0 +1,17 @@ +import { cn } from "@/lib/utils" + +import { CODE_BLOCK_BG_COLOR } from "./CodeBlock" + +export const ToolUseBlock = ({ className, ...props }: React.HTMLAttributes) => ( +
+) + +export const ToolUseBlockHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+) diff --git a/webview-ui/src/components/common/__tests__/CodeBlock.test.tsx b/webview-ui/src/components/common/__tests__/CodeBlock.test.tsx index e7347067c29..28576c3c7d6 100644 --- a/webview-ui/src/components/common/__tests__/CodeBlock.test.tsx +++ b/webview-ui/src/components/common/__tests__/CodeBlock.test.tsx @@ -2,6 +2,23 @@ import { render, screen, fireEvent, act } from "@testing-library/react" import "@testing-library/jest-dom" import CodeBlock from "../CodeBlock" +// Mock the translation context +jest.mock("../../../i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string) => { + // Return fixed English strings for tests + const translations: { [key: string]: string } = { + "chat:codeblock.tooltips.copy_code": "Copy code", + "chat:codeblock.tooltips.expand": "Expand code block", + "chat:codeblock.tooltips.collapse": "Collapse code block", + "chat:codeblock.tooltips.enable_wrap": "Enable word wrap", + "chat:codeblock.tooltips.disable_wrap": "Disable word wrap", + } + return translations[key] || key + }, + }), +})) + // Mock shiki module jest.mock("shiki", () => ({ bundledLanguages: { @@ -11,6 +28,22 @@ jest.mock("shiki", () => ({ }, })) +// Mock all lucide-react icons with a proxy to handle any icon requested +jest.mock("lucide-react", () => { + return new Proxy( + {}, + { + get: function (obj, prop) { + // Return a component factory for any icon that's requested + if (prop === "__esModule") { + return true + } + return () =>
{String(prop)}
+ }, + }, + ) +}) + // Mock the highlighter utility jest.mock("../../../utils/highlighter", () => { const mockHighlighter = { diff --git a/webview-ui/src/components/mcp/McpErrorRow.tsx b/webview-ui/src/components/mcp/McpErrorRow.tsx new file mode 100644 index 00000000000..3b1ea8f7ce7 --- /dev/null +++ b/webview-ui/src/components/mcp/McpErrorRow.tsx @@ -0,0 +1,32 @@ +import { useMemo } from "react" +import { formatRelative } from "date-fns" + +import type { McpErrorEntry } from "@roo/shared/mcp" + +type McpErrorRowProps = { + error: McpErrorEntry +} + +export const McpErrorRow = ({ error }: McpErrorRowProps) => { + const color = useMemo(() => { + switch (error.level) { + case "error": + return "var(--vscode-testing-iconFailed)" + case "warn": + return "var(--vscode-charts-yellow)" + case "info": + return "var(--vscode-testing-iconPassed)" + } + }, [error.level]) + + return ( +
+
+ {error.message} +
+
+ {formatRelative(error.timestamp, new Date())} +
+
+ ) +} diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index b21a479730a..7b6a89799b7 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -1,6 +1,7 @@ -import { useState } from "react" -import { Button } from "@/components/ui/button" +import React, { useState } from "react" +import { Trans } from "react-i18next" import { + VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodePanels, @@ -10,16 +11,26 @@ import { import { McpServer } from "@roo/shared/mcp" -import { vscode } from "@/utils/vscode" -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui" - +import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { Trans } from "react-i18next" +import { + Button, + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@src/components/ui" +import { buildDocLink } from "@src/utils/docLinks" + import { Tab, TabContent, TabHeader } from "../common/Tab" + import McpToolRow from "./McpToolRow" import McpResourceRow from "./McpResourceRow" import McpEnabledToggle from "./McpEnabledToggle" +import { McpErrorRow } from "./McpErrorRow" type McpViewProps = { onDone: () => void @@ -33,6 +44,7 @@ const McpView = ({ onDone }: McpViewProps) => { enableMcpServerCreation, setEnableMcpServerCreation, } = useExtensionState() + const { t } = useAppTranslation() return ( @@ -51,13 +63,10 @@ const McpView = ({ onDone }: McpViewProps) => { marginTop: "5px", }}> - - Model Context Protocol - - community-made servers + Learn More
@@ -75,14 +84,25 @@ const McpView = ({ onDone }: McpViewProps) => { }}> {t("mcp:enableServerCreation.title")} -

- {t("mcp:enableServerCreation.description")} -

+ + + Learn about server creation + + new + +

{t("mcp:enableServerCreation.hint")}

+
{/* Server List */} @@ -119,6 +139,21 @@ const McpView = ({ onDone }: McpViewProps) => { {t("mcp:editProjectMCP")}
+
+ + {t("mcp:learnMoreEditingSettings")} + +
)} @@ -158,7 +193,7 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM } const handleRowClick = () => { - if (!server.error) { + if (server.status === "connected") { setIsExpanded(!isExpanded) } } @@ -199,12 +234,12 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM alignItems: "center", padding: "8px", background: "var(--vscode-textCodeBlock-background)", - cursor: server.error ? "default" : "pointer", - borderRadius: isExpanded || server.error ? "4px 4px 0 0" : "4px", + cursor: server.status === "connected" ? "pointer" : "default", + borderRadius: isExpanded || server.status === "connected" ? "4px" : "4px 4px 0 0", opacity: server.disabled ? 0.6 : 1, }} onClick={handleRowClick}> - {!server.error && ( + {server.status === "connected" && (
- {server.error ? ( -
-
- {server.error} -
- -
- ) : ( + {server.status === "connected" ? ( isExpanded && (
+ + {t("mcp:tabs.errors")} ({server.errorHistory?.length || 0}) + {server.tools && server.tools.length > 0 ? ( @@ -391,6 +401,23 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
)} + + + {server.errorHistory && server.errorHistory.length > 0 ? ( +
+ {[...server.errorHistory] + .sort((a, b) => b.timestamp - a.timestamp) + .map((error, index) => ( + + ))} +
+ ) : ( +
+ {t("mcp:emptyState.noErrors")} +
+ )} +
{/* Network Timeout */} @@ -434,6 +461,38 @@ const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowM
) + ) : ( +
+
+ {server.error && + server.error.split("\n").map((item, index) => ( + + {index > 0 &&
} + {item} +
+ ))} +
+ + {server.status === "connecting" ? "Retrying..." : "Retry Connection"} + +
)} {/* Delete Confirmation Dialog */} diff --git a/webview-ui/src/components/prompts/PromptsView.tsx b/webview-ui/src/components/prompts/PromptsView.tsx index 5d17dd04a60..6cc59fb832b 100644 --- a/webview-ui/src/components/prompts/PromptsView.tsx +++ b/webview-ui/src/components/prompts/PromptsView.tsx @@ -1,13 +1,11 @@ -import React, { useState, useEffect, useMemo, useCallback } from "react" +import React, { useState, useEffect, useMemo, useCallback, useRef } from "react" import { Button } from "@/components/ui/button" import { - VSCodeTextArea, - VSCodeDropdown, - VSCodeOption, - VSCodeTextField, VSCodeCheckbox, VSCodeRadioGroup, VSCodeRadio, + VSCodeTextArea, + VSCodeLink, } from "@vscode/webview-ui-toolkit/react" import { useExtensionState } from "@src/context/ExtensionStateContext" @@ -15,6 +13,7 @@ import { Mode, PromptComponent, getRoleDefinition, + getWhenToUse, getCustomInstructions, getAllModes, ModeConfig, @@ -26,10 +25,28 @@ import { supportPrompt, SupportPromptType } from "@roo/shared/support-prompt" import { TOOL_GROUPS, ToolGroup } from "@roo/shared/tools" import { vscode } from "@src/utils/vscode" import { Tab, TabContent, TabHeader } from "../common/Tab" -import i18next from "i18next" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Trans } from "react-i18next" import { AGENT_MODES_FILE_NAME, AGENT_RULES_DIR } from "@roo/shared/constants" +import { buildDocLink } from "@src/utils/docLinks" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + Popover, + PopoverContent, + PopoverTrigger, + Command, + CommandInput, + CommandList, + CommandEmpty, + CommandItem, + CommandGroup, + Input, +} from "../ui" +import { ChevronsUpDown, X } from "lucide-react" // Get all available groups that should show in prompts view const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) => !TOOL_GROUPS[group].alwaysAvailable) @@ -79,9 +96,14 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { const [isToolsEditMode, setIsToolsEditMode] = useState(false) const [showConfigMenu, setShowConfigMenu] = useState(false) const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false) - const [activeSupportTab, setActiveSupportTab] = useState("ENHANCE") + const [activeSupportOption, setActiveSupportOption] = useState("ENHANCE") const [isSystemPromptDisclosureOpen, setIsSystemPromptDisclosureOpen] = useState(false) + // State for mode selection popover and search + const [open, setOpen] = useState(false) + const [searchValue, setSearchValue] = useState("") + const searchInputRef = useRef(null) + // Direct update functions const updateAgentPrompt = useCallback( (mode: Mode, promptData: PromptComponent) => { @@ -92,6 +114,9 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { if (updatedPrompt.roleDefinition === getRoleDefinition(mode)) { delete updatedPrompt.roleDefinition } + if (updatedPrompt.whenToUse === getWhenToUse(mode)) { + delete updatedPrompt.whenToUse + } vscode.postMessage({ type: "updatePrompt", @@ -145,9 +170,24 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { // Exit tools edit mode when switching modes setIsToolsEditMode(false) }, - [visualMode, switchMode, setIsToolsEditMode], + [visualMode, switchMode], ) + // Handler for popover open state change + const onOpenChange = useCallback((open: boolean) => { + setOpen(open) + // Reset search when closing the popover + if (!open) { + setTimeout(() => setSearchValue(""), 100) + } + }, []) + + // Handler for clearing search input + const onClearSearch = useCallback(() => { + setSearchValue("") + searchInputRef.current?.focus() + }, []) + // Helper function to get current mode's config const getCurrentMode = useCallback((): ModeConfig | undefined => { const findMode = (m: ModeConfig): boolean => m.slug === visualMode @@ -166,6 +206,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { const [newModeName, setNewModeName] = useState("") const [newModeSlug, setNewModeSlug] = useState("") const [newModeRoleDefinition, setNewModeRoleDefinition] = useState("") + const [newModeWhenToUse, setNewModeWhenToUse] = useState("") const [newModeCustomInstructions, setNewModeCustomInstructions] = useState("") const [newModeGroups, setNewModeGroups] = useState(availableGroups) const [newModeSource, setNewModeSource] = useState("global") @@ -183,6 +224,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { setNewModeSlug("") setNewModeGroups(availableGroups) setNewModeRoleDefinition("") + setNewModeWhenToUse("") setNewModeCustomInstructions("") setNewModeSource("global") // Reset error states @@ -229,6 +271,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { slug: newModeSlug, name: newModeName, roleDefinition: newModeRoleDefinition.trim(), + whenToUse: newModeWhenToUse.trim() || undefined, customInstructions: newModeCustomInstructions.trim() || undefined, groups: newModeGroups, source, @@ -270,6 +313,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { newModeName, newModeSlug, newModeRoleDefinition, + newModeWhenToUse, // Add whenToUse dependency newModeCustomInstructions, newModeGroups, newModeSource, @@ -367,7 +411,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { }) } - const handleAgentReset = (modeSlug: string, type: "roleDefinition" | "customInstructions") => { + const handleAgentReset = (modeSlug: string, type: "roleDefinition" | "whenToUse" | "customInstructions") => { // Only reset for built-in modes const existingPrompt = customModePrompts?.[modeSlug] as PromptComponent const updatedPrompt = { ...existingPrompt } @@ -478,48 +522,155 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
- {t("prompts:modes.createModeHelpText")} + + + +
-
- {modes.map((modeConfig) => { - const isActive = visualMode === modeConfig.slug - return ( - - ) - })} +
+ + + + + + +
+ + {searchValue.length > 0 && ( +
+ +
+ )} +
+ + + {searchValue && ( +
+ {t("prompts:modes.noMatchFound")} +
+ )} +
+ + {modes + .filter((modeConfig) => + searchValue + ? modeConfig.name + .toLowerCase() + .includes(searchValue.toLowerCase()) + : true, + ) + .map((modeConfig) => ( + { + handleModeSwitch(modeConfig) + setOpen(false) + }} + data-testid={`mode-option-${modeConfig.slug}`}> +
+ + {modeConfig.name} + + + {modeConfig.slug} + +
+
+ ))} +
+
+
+
+
+
+ {/* API Configuration - Moved Here */} +
+
{t("prompts:apiConfiguration.title")}
+
+ +
+ {t("prompts:apiConfiguration.select")} +
+
-
+
{/* Only show name and delete for custom modes */} {visualMode && findModeBySlug(visualMode, customModes) && (
{t("prompts:createModeDialog.name.label")}
- ) => { - const target = - (e as CustomEvent)?.detail?.target || - ((e as any).target as HTMLInputElement) + onChange={(e) => { const customMode = findModeBySlug(visualMode, customModes) if (customMode) { updateCustomMode(visualMode, { ...customMode, - name: target.value, + name: e.target.value, source: customMode.source || "global", }) } @@ -542,7 +693,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
)} -
+
{t("prompts:roleDefinition.title")}
{!findModeBySlug(visualMode, customModes) && ( @@ -576,7 +727,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { })()} onChange={(e) => { const value = - (e as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value || ((e as any).target as HTMLTextAreaElement).value const customMode = findModeBySlug(visualMode, customModes) if (customMode) { @@ -593,41 +744,68 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { }) } }} + className="resize-y w-full" rows={4} - resize="vertical" - style={{ width: "100%" }} data-testid={`${getCurrentMode()?.slug || "code"}-prompt-textarea`} />
- {/* Mode settings */} - <> -
-
- {t("prompts:apiConfiguration.title")} -
-
- { - const value = e.detail?.target?.value || e.target?.value - vscode.postMessage({ - type: "loadApiConfiguration", - text: value, - }) + + {/* When to Use section */} +
+
+
{t("prompts:whenToUse.title")}
+ {!findModeBySlug(visualMode, customModes) && ( +
+ title={t("prompts:whenToUse.resetToDefault")} + data-testid="when-to-use-reset"> + + + )} +
+
+ {t("prompts:whenToUse.description")}
+ { + const customMode = findModeBySlug(visualMode, customModes) + const prompt = customModePrompts?.[visualMode] as PromptComponent + return customMode?.whenToUse ?? prompt?.whenToUse ?? getWhenToUse(visualMode) + })()} + onChange={(e) => { + const value = + (e as unknown as CustomEvent)?.detail?.target?.value || + ((e as any).target as HTMLTextAreaElement).value + const customMode = findModeBySlug(visualMode, customModes) + if (customMode) { + // For custom modes, update the JSON file + updateCustomMode(visualMode, { + ...customMode, + whenToUse: value.trim() || undefined, + source: customMode.source || "global", + }) + } else { + // For built-in modes, update the prompts + updateAgentPrompt(visualMode, { + whenToUse: value.trim() || undefined, + }) + } + }} + className="resize-y w-full" + rows={3} + data-testid={`${getCurrentMode()?.slug || "code"}-when-to-use-textarea`} + /> +
+ {/* Mode settings */} + <> {/* Show tools for all modes */}
@@ -722,15 +900,9 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { {/* Role definition for both built-in and custom modes */} -
-
-
{t("prompts:customInstructions.title")}
+
+
+
{t("prompts:customInstructions.title")}
{!findModeBySlug(visualMode, customModes) && ( )}
-
+
{t("prompts:customInstructions.description", { modeName: getCurrentMode()?.name || "Code", })} @@ -769,7 +936,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { })()} onChange={(e) => { const value = - (e as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value || ((e as any).target as HTMLTextAreaElement).value const customMode = findModeBySlug(visualMode, customModes) if (customMode) { @@ -789,16 +956,10 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { } }} rows={4} - resize="vertical" - style={{ width: "100%" }} + className="w-full resize-y" data-testid={`${getCurrentMode()?.slug || "code"}-custom-instructions-textarea`} /> -
+
{ components={{ span: ( { const currentMode = getCurrentMode() if (!currentMode) return @@ -835,13 +992,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
-
-
+
+
{/* Custom System Prompt Disclosure */} -
+
@@ -919,18 +1080,23 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
-

- {t("prompts:globalCustomInstructions.title")} -

+

{t("prompts:globalCustomInstructions.title")}

- {t("prompts:globalCustomInstructions.description", { language: i18next.language })} + + +
{ const value = - (e as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value || ((e as any).target as HTMLTextAreaElement).value setCustomInstructions(value || undefined) vscode.postMessage({ @@ -939,21 +1105,16 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { }) }} rows={4} - resize="vertical" - className="w-full" + className="w-full resize-y" data-testid="global-custom-instructions-textarea" /> -
+
vscode.postMessage({ type: "openFile", @@ -971,156 +1132,113 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
-
-

- {t("prompts:supportPrompts.title")} -

-
- {Object.keys(supportPrompt.default).map((type) => ( - - ))} +
+

{t("prompts:supportPrompts.title")}

+
+
{/* Support prompt description */} -
- {t(`prompts:supportPrompts.types.${activeSupportTab}.description`)} +
+ {t(`prompts:supportPrompts.types.${activeSupportOption}.description`)}
- {/* Show active tab content */} -
-
-
{t("prompts:supportPrompts.prompt")}
+
+
+
{t("prompts:supportPrompts.prompt")}
{ const value = - (e as CustomEvent)?.detail?.target?.value || + (e as unknown as CustomEvent)?.detail?.target?.value || ((e as any).target as HTMLTextAreaElement).value const trimmedValue = value.trim() - updateSupportPrompt(activeSupportTab, trimmedValue || undefined) + updateSupportPrompt(activeSupportOption, trimmedValue || undefined) }} rows={6} - resize="vertical" - style={{ width: "100%" }} + className="resize-y w-full" /> - {activeSupportTab === "ENHANCE" && ( + {activeSupportOption === "ENHANCE" && ( <>
-
-
-
-
+
+
+
+
{t("prompts:supportPrompts.enhance.apiConfiguration")}
-
+
{t("prompts:supportPrompts.enhance.apiConfigDescription")}
- { - const value = e.detail?.target?.value || e.target?.value - setEnhancementApiConfigId(value) +
-
+
setTestPrompt((e.target as HTMLTextAreaElement).value)} placeholder={t("prompts:supportPrompts.enhance.testPromptPlaceholder")} rows={3} - resize="vertical" - style={{ width: "100%" }} + className="w-full resize-y" data-testid="test-prompt-textarea" /> -
+
-

{t("prompts:createModeDialog.title")}

-
-
- {t("prompts:createModeDialog.name.label")} -
- {t("prompts:createModeDialog.title")} +
+
{t("prompts:createModeDialog.name.label")}
+ ) => { - const target = - (e as CustomEvent)?.detail?.target || - ((e as any).target as HTMLInputElement) - handleNameChange(target.value) + onChange={(e) => { + handleNameChange(e.target.value) }} - style={{ width: "100%" }} + className="w-full" /> {nameError && (
{nameError}
)}
-
-
- {t("prompts:createModeDialog.slug.label")} -
- +
{t("prompts:createModeDialog.slug.label")}
+ ) => { - const target = - (e as CustomEvent)?.detail?.target || - ((e as any).target as HTMLInputElement) - setNewModeSlug(target.value) + onChange={(e) => { + setNewModeSlug(e.target.value) }} - style={{ width: "100%" }} + className="w-full" /> -
+
{t("prompts:createModeDialog.slug.description")}
{slugError && (
{slugError}
)}
-
-
- {t("prompts:createModeDialog.saveLocation.label")} -
+
+
{t("prompts:createModeDialog.saveLocation.label")}
{t("prompts:createModeDialog.saveLocation.description")}
@@ -1234,12 +1310,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { }}> {t("prompts:createModeDialog.saveLocation.global.label")} -
+
{t("prompts:createModeDialog.saveLocation.global.description")}
@@ -1267,14 +1338,10 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { { - const value = - (e as CustomEvent)?.detail?.target?.value || - ((e as any).target as HTMLTextAreaElement).value - setNewModeRoleDefinition(value) + setNewModeRoleDefinition((e.target as HTMLTextAreaElement).value) }} rows={4} - resize="vertical" - style={{ width: "100%" }} + className="w-full resize-y" /> {roleDefinitionError && (
@@ -1282,24 +1349,27 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
)}
-
-
- {t("prompts:createModeDialog.tools.label")} + +
+
{t("prompts:createModeDialog.whenToUse.label")}
+
+ {t("prompts:createModeDialog.whenToUse.description")}
-
+ { + setNewModeWhenToUse((e.target as HTMLTextAreaElement).value) + }} + rows={3} + className="w-full resize-y" + /> +
+
+
{t("prompts:createModeDialog.tools.label")}
+
{t("prompts:createModeDialog.tools.description")}
-
+
{availableGroups.map((group) => ( {
{groupsError}
)}
-
-
+
+
{t("prompts:createModeDialog.customInstructions.label")}
-
+
{t("prompts:createModeDialog.customInstructions.description")}
{ - const value = - (e as CustomEvent)?.detail?.target?.value || - ((e as any).target as HTMLTextAreaElement).value - setNewModeCustomInstructions(value) + setNewModeCustomInstructions((e.target as HTMLTextAreaElement).value) }} rows={4} - resize="vertical" - style={{ width: "100%" }} + className="w-full resize-y" />
-
+
@@ -1371,71 +1424,27 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { )} {isDialogOpen && ( -
-
-
+
+
+
-

+

{selectedPromptTitle || - t("prompts:systemPrompt.title", { modeName: getCurrentMode()?.name || "Code" })} + t("prompts:systemPrompt.title", { + modeName: getCurrentMode()?.name || "Code", + })}

-
+							
 								{selectedPromptContent}
 							
-
+
diff --git a/webview-ui/src/components/prompts/__tests__/PromptsView.test.tsx b/webview-ui/src/components/prompts/__tests__/PromptsView.test.tsx index 72695a6920d..2015ffd0fba 100644 --- a/webview-ui/src/components/prompts/__tests__/PromptsView.test.tsx +++ b/webview-ui/src/components/prompts/__tests__/PromptsView.test.tsx @@ -1,3 +1,5 @@ +// npx jest src/components/prompts/__tests__/PromptsView.test.tsx + import { render, screen, fireEvent, waitFor } from "@testing-library/react" import PromptsView from "../PromptsView" import { ExtensionStateContext } from "@src/context/ExtensionStateContext" @@ -10,6 +12,22 @@ jest.mock("@src/utils/vscode", () => ({ }, })) +// Mock all lucide-react icons with a proxy to handle any icon requested +jest.mock("lucide-react", () => { + return new Proxy( + {}, + { + get: function (obj, prop) { + // Return a component factory for any icon that's requested + if (prop === "__esModule") { + return true + } + return () =>
{String(prop)}
+ }, + }, + ) +}) + const mockExtensionState = { customModePrompts: {}, listApiConfigMeta: [ @@ -19,6 +37,9 @@ const mockExtensionState = { enhancementApiConfigId: "", setEnhancementApiConfigId: jest.fn(), mode: "code", + customModes: [], + customSupportPrompts: [], + currentApiConfigName: "", customInstructions: "Initial instructions", setCustomInstructions: jest.fn(), } @@ -32,69 +53,67 @@ const renderPromptsView = (props = {}) => { ) } +class MockResizeObserver { + observe() {} + unobserve() {} + disconnect() {} +} + +global.ResizeObserver = MockResizeObserver + +Element.prototype.scrollIntoView = jest.fn() + describe("PromptsView", () => { beforeEach(() => { jest.clearAllMocks() }) - it("renders all mode tabs", () => { - renderPromptsView() - expect(screen.getByTestId("code-tab")).toBeInTheDocument() - expect(screen.getByTestId("ask-tab")).toBeInTheDocument() - expect(screen.getByTestId("architect-tab")).toBeInTheDocument() + it("displays the current mode name in the select trigger", () => { + renderPromptsView({ mode: "code" }) + const selectTrigger = screen.getByTestId("mode-select-trigger") + expect(selectTrigger).toHaveTextContent("Code") }) - it("defaults to current mode as active tab", () => { - renderPromptsView({ mode: "ask" }) - - const codeTab = screen.getByTestId("code-tab") - const askTab = screen.getByTestId("ask-tab") - const architectTab = screen.getByTestId("architect-tab") - - expect(askTab).toHaveAttribute("data-active", "true") - expect(codeTab).toHaveAttribute("data-active", "false") - expect(architectTab).toHaveAttribute("data-active", "false") + it("opens the mode selection popover when the trigger is clicked", async () => { + renderPromptsView() + const selectTrigger = screen.getByTestId("mode-select-trigger") + fireEvent.click(selectTrigger) + await waitFor(() => { + expect(selectTrigger).toHaveAttribute("aria-expanded", "true") + }) }) - it("switches between tabs correctly", async () => { - const { rerender } = render( - - - , - ) - - const codeTab = screen.getByTestId("code-tab") - const askTab = screen.getByTestId("ask-tab") - const architectTab = screen.getByTestId("architect-tab") + it("filters mode options based on search input", async () => { + renderPromptsView() + const selectTrigger = screen.getByTestId("mode-select-trigger") + fireEvent.click(selectTrigger) - // Initial state matches current mode (code) - expect(codeTab).toHaveAttribute("data-active", "true") - expect(askTab).toHaveAttribute("data-active", "false") - expect(architectTab).toHaveAttribute("data-active", "false") + const searchInput = screen.getByTestId("mode-search-input") + fireEvent.change(searchInput, { target: { value: "ask" } }) - // Click Ask tab and update context - fireEvent.click(askTab) - rerender( - - - , - ) + await waitFor(() => { + expect(screen.getByTestId("mode-option-ask")).toBeInTheDocument() + expect(screen.queryByTestId("mode-option-code")).not.toBeInTheDocument() + expect(screen.queryByTestId("mode-option-architect")).not.toBeInTheDocument() + }) + }) - expect(askTab).toHaveAttribute("data-active", "true") - expect(codeTab).toHaveAttribute("data-active", "false") - expect(architectTab).toHaveAttribute("data-active", "false") + it("selects a mode from the dropdown and sends update message", async () => { + renderPromptsView() + const selectTrigger = screen.getByTestId("mode-select-trigger") + fireEvent.click(selectTrigger) - // Click Architect tab and update context - fireEvent.click(architectTab) - rerender( - - - , - ) + const askOption = await waitFor(() => screen.getByTestId("mode-option-ask")) + fireEvent.click(askOption) - expect(architectTab).toHaveAttribute("data-active", "true") - expect(askTab).toHaveAttribute("data-active", "false") - expect(codeTab).toHaveAttribute("data-active", "false") + expect(mockExtensionState.setEnhancementApiConfigId).not.toHaveBeenCalled() // Ensure this is not called by mode switch + expect(vscode.postMessage).toHaveBeenCalledWith({ + type: "mode", + text: "ask", + }) + await waitFor(() => { + expect(selectTrigger).toHaveAttribute("aria-expanded", "false") + }) }) it("handles prompt changes correctly", async () => { @@ -159,21 +178,19 @@ describe("PromptsView", () => { it("handles API configuration selection", async () => { renderPromptsView() - // Click the ENHANCE tab first to show the API config dropdown - const enhanceTab = screen.getByTestId("ENHANCE-tab") - fireEvent.click(enhanceTab) + const trigger = screen.getByTestId("support-prompt-select-trigger") + fireEvent.click(trigger) - // Wait for the ENHANCE tab click to take effect - const dropdown = await waitFor(() => screen.getByTestId("api-config-dropdown")) - fireEvent.change(dropdown, { - target: { value: "config1" }, - }) + const enhanceOption = await waitFor(() => screen.getByTestId("ENHANCE-option")) + fireEvent.click(enhanceOption) - expect(mockExtensionState.setEnhancementApiConfigId).toHaveBeenCalledWith("config1") - expect(vscode.postMessage).toHaveBeenCalledWith({ - type: "enhancementApiConfigId", - text: "config1", - }) + const apiConfig = await waitFor(() => screen.getByTestId("api-config-select")) + fireEvent.click(apiConfig) + + const config1 = await waitFor(() => screen.getByTestId("config1-option")) + fireEvent.click(config1) + + expect(mockExtensionState.setEnhancementApiConfigId).toHaveBeenCalledWith("config1") // Ensure this is not called by mode switch }) it("handles clearing custom instructions correctly", async () => { diff --git a/webview-ui/src/components/settings/About.tsx b/webview-ui/src/components/settings/About.tsx index da57dc73a6f..698330581ff 100644 --- a/webview-ui/src/components/settings/About.tsx +++ b/webview-ui/src/components/settings/About.tsx @@ -38,11 +38,14 @@ export const About = ({ version, telemetrySetting, setTelemetrySetting, classNam {t("settings:footer.settings.export")} - - diff --git a/webview-ui/src/components/settings/ApiConfigManager.tsx b/webview-ui/src/components/settings/ApiConfigManager.tsx index 841de5d8a28..05b14bc5867 100644 --- a/webview-ui/src/components/settings/ApiConfigManager.tsx +++ b/webview-ui/src/components/settings/ApiConfigManager.tsx @@ -2,7 +2,7 @@ import { memo, useEffect, useRef, useState } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { ChevronsUpDown, Check, X } from "lucide-react" -import { ApiConfigMeta } from "@roo/shared/ExtensionMessage" +import { ProviderSettingsEntry } from "@roo/shared/ExtensionMessage" import { useAppTranslation } from "@/i18n/TranslationContext" import { cn } from "@/lib/utils" @@ -25,7 +25,7 @@ import { interface ApiConfigManagerProps { currentApiConfigName?: string - listApiConfigMeta?: ApiConfigMeta[] + listApiConfigMeta?: ProviderSettingsEntry[] onSelectConfig: (configName: string) => void onDeleteConfig: (configName: string) => void onRenameConfig: (oldName: string, newName: string) => void diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 4fcb2ae5924..ec277243052 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -1,66 +1,63 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from "react" -import { useDebounce, useEvent } from "react-use" -import { Trans } from "react-i18next" -import { LanguageModelChatSelector } from "vscode" -import { Checkbox } from "vscrui" -import { - VSCodeButton, - VSCodeLink, - VSCodeRadio, - VSCodeRadioGroup, - VSCodeTextField, -} from "@vscode/webview-ui-toolkit/react" -import { ExternalLinkIcon } from "@radix-ui/react-icons" +import { convertHeadersToObject } from "./utils/headers" +import { useDebounce } from "react-use" +import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { ReasoningEffort as ReasoningEffortType } from "@roo/schemas" import { - ApiConfiguration, - ModelInfo, - azureOpenAiDefaultApiVersion, - glamaDefaultModelId, - mistralDefaultModelId, - openAiModelInfoSaneDefaults, + type ProviderName, + type ProviderSettings, openRouterDefaultModelId, - unboundDefaultModelId, requestyDefaultModelId, - ApiProvider, + glamaDefaultModelId, + unboundDefaultModelId, + litellmDefaultModelId, } from "@roo/shared/api" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" import { vscode } from "@src/utils/vscode" -import { validateApiConfiguration, validateModelId, validateBedrockArn } from "@src/utils/validate" +import { validateApiConfiguration, validateModelId } from "@src/utils/validate" import { useAppTranslation } from "@src/i18n/TranslationContext" import { useRouterModels } from "@src/components/ui/hooks/useRouterModels" import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel" -import { - useOpenRouterModelProviders, - OPENROUTER_DEFAULT_PROVIDER_NAME, -} from "@src/components/ui/hooks/useOpenRouterModelProviders" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Button } from "@src/components/ui" -import { getRequestyAuthUrl, getOpenRouterAuthUrl, getGlamaAuthUrl } from "@src/oauth/urls" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" -import { VSCodeButtonLink } from "../common/VSCodeButtonLink" - -import { MODELS_BY_PROVIDER, PROVIDERS, VERTEX_REGIONS, REASONING_MODELS, AWS_REGIONS } from "./constants" +import { + Anthropic, + Bedrock, + Chutes, + DeepSeek, + Gemini, + Glama, + Groq, + LMStudio, + LiteLLM, + Mistral, + Ollama, + OpenAI, + OpenAICompatible, + OpenRouter, + Requesty, + Unbound, + Vertex, + VSCodeLM, + XAI, +} from "./providers" + +import { MODELS_BY_PROVIDER, PROVIDERS, REASONING_MODELS } from "./constants" +import { inputEventTransform, noTransform } from "./transforms" import { ModelInfoView } from "./ModelInfoView" -import { ModelPicker } from "./ModelPicker" import { ApiErrorMessage } from "./ApiErrorMessage" import { ThinkingBudget } from "./ThinkingBudget" -import { R1FormatSetting } from "./R1FormatSetting" -import { usePearAIModels } from "../../hooks/usePearAIModels" -import { allModels, pearaiDefaultModelId, pearaiDefaultModelInfo } from "../../../../src/shared/pearaiApi" -import { OpenRouterBalanceDisplay } from "./OpenRouterBalanceDisplay" -import { RequestyBalanceDisplay } from "./RequestyBalanceDisplay" import { ReasoningEffort } from "./ReasoningEffort" -import { PromptCachingControl } from "./PromptCachingControl" import { DiffSettingsControl } from "./DiffSettingsControl" import { TemperatureControl } from "./TemperatureControl" import { RateLimitSecondsControl } from "./RateLimitSecondsControl" +import { BedrockCustomArn } from "./providers/BedrockCustomArn" +import { buildDocLink } from "@src/utils/docLinks" export interface ApiOptionsProps { uriScheme: string | undefined - apiConfiguration: ApiConfiguration - setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: K, value: ProviderSettings[K]) => void fromWelcomeView?: boolean errorMessage: string | undefined setErrorMessage: React.Dispatch> @@ -76,91 +73,29 @@ const ApiOptions = ({ }: ApiOptionsProps) => { const { t } = useAppTranslation() - const [ollamaModels, setOllamaModels] = useState([]) - const [lmStudioModels, setLmStudioModels] = useState([]) - const [vsCodeLmModels, setVsCodeLmModels] = useState([]) - - const [openAiModels, setOpenAiModels] = useState | null>(null) - const pearaiModels = usePearAIModels(apiConfiguration).models - const [customHeaders, setCustomHeaders] = useState<[string, string][]>(() => { const headers = apiConfiguration?.openAiHeaders || {} return Object.entries(headers) }) - // Effect to synchronize internal customHeaders state with prop changes useEffect(() => { const propHeaders = apiConfiguration?.openAiHeaders || {} - if (JSON.stringify(customHeaders) !== JSON.stringify(Object.entries(propHeaders))) setCustomHeaders(Object.entries(propHeaders)) - }, [apiConfiguration?.openAiHeaders, customHeaders]) - - const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) - const [openAiNativeBaseUrlSelected, setOpenAiNativeBaseUrlSelected] = useState( - !!apiConfiguration?.openAiNativeBaseUrl, - ) - const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion) - const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl) - const [openAiLegacyFormatSelected, setOpenAiLegacyFormatSelected] = useState(!!apiConfiguration?.openAiLegacyFormat) - const [googleGeminiBaseUrlSelected, setGoogleGeminiBaseUrlSelected] = useState( - !!apiConfiguration?.googleGeminiBaseUrl, - ) - - const handleAddCustomHeader = useCallback(() => { - // Only update the local state to show the new row in the UI - setCustomHeaders((prev) => [...prev, ["", ""]]) - // Do not update the main configuration yet, wait for user input - }, []) - - const handleUpdateHeaderKey = useCallback((index: number, newKey: string) => { - setCustomHeaders((prev) => { - const updated = [...prev] - if (updated[index]) { - updated[index] = [newKey, updated[index][1]] - } - return updated - }) - }, []) - - const handleUpdateHeaderValue = useCallback((index: number, newValue: string) => { - setCustomHeaders((prev) => { - const updated = [...prev] - if (updated[index]) { - updated[index] = [updated[index][0], newValue] - } - return updated - }) - }, []) - - const handleRemoveCustomHeader = useCallback((index: number) => { - setCustomHeaders((prev) => prev.filter((_, i) => i !== index)) - }, []) - - // Helper to convert array of tuples to object (filtering out empty keys) - const convertHeadersToObject = (headers: [string, string][]): Record => { - const result: Record = {} - // Process each header tuple - for (const [key, value] of headers) { - const trimmedKey = key.trim() - - // Skip empty keys - if (!trimmedKey) continue - - // For duplicates, the last one in the array wins - // This matches how HTTP headers work in general - result[trimmedKey] = value.trim() + if (JSON.stringify(customHeaders) !== JSON.stringify(Object.entries(propHeaders))) { + setCustomHeaders(Object.entries(propHeaders)) } + }, [apiConfiguration?.openAiHeaders, customHeaders]) - return result - } + // Helper to convert array of tuples to object (filtering out empty keys). - // Debounced effect to update the main configuration when local customHeaders state stabilizes + // Debounced effect to update the main configuration when local + // customHeaders state stabilizes. useDebounce( () => { const currentConfigHeaders = apiConfiguration?.openAiHeaders || {} const newHeadersObject = convertHeadersToObject(customHeaders) - // Only update if the processed object is different from the current config + // Only update if the processed object is different from the current config. if (JSON.stringify(currentConfigHeaders) !== JSON.stringify(newHeadersObject)) { setApiConfigurationField("openAiHeaders", newHeadersObject) } @@ -170,14 +105,11 @@ const ApiOptions = ({ ) const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) - const noTransform = (value: T) => value - - const inputEventTransform = (event: E) => (event as { target: HTMLInputElement })?.target?.value as any const handleInputChange = useCallback( - ( + ( field: K, - transform: (event: E) => ApiConfiguration[K] = inputEventTransform, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, ) => (event: E | Event) => { setApiConfigurationField(field, transform(event as E)) @@ -191,9 +123,9 @@ const ApiOptions = ({ info: selectedModelInfo, } = useSelectedModel(apiConfiguration) - const { data: routerModels } = useRouterModels() + const { data: routerModels, refetch: refetchRouterModels } = useRouterModels() - // Update apiConfiguration.aiModelId whenever selectedModelId changes. + // Update `apiModelId` whenever `selectedModelId` changes. useEffect(() => { if (selectedModelId) { setApiConfigurationField("apiModelId", selectedModelId) @@ -205,8 +137,9 @@ const ApiOptions = ({ useDebounce( () => { if (selectedProvider === "openai") { - // Use our custom headers state to build the headers object + // Use our custom headers state to build the headers object. const headerObject = convertHeadersToObject(customHeaders) + vscode.postMessage({ type: "requestOpenAiModels", values: { @@ -222,6 +155,8 @@ const ApiOptions = ({ vscode.postMessage({ type: "requestLmStudioModels", text: apiConfiguration?.lmStudioBaseUrl }) } else if (selectedProvider === "vscode-lm") { vscode.postMessage({ type: "requestVsCodeLmModels" }) + } else if (selectedProvider === "litellm") { + vscode.postMessage({ type: "requestRouterModels" }) } }, 250, @@ -232,6 +167,8 @@ const ApiOptions = ({ apiConfiguration?.openAiApiKey, apiConfiguration?.ollamaBaseUrl, apiConfiguration?.lmStudioBaseUrl, + apiConfiguration?.litellmBaseUrl, + apiConfiguration?.litellmApiKey, customHeaders, ], ) @@ -239,99 +176,25 @@ const ApiOptions = ({ useEffect(() => { const apiValidationResult = validateApiConfiguration(apiConfiguration) || validateModelId(apiConfiguration, routerModels) + setErrorMessage(apiValidationResult) }, [apiConfiguration, routerModels, setErrorMessage]) - const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, { - enabled: - selectedProvider === "openrouter" && - !!apiConfiguration?.openRouterModelId && - routerModels?.openrouter && - Object.keys(routerModels.openrouter).length > 1 && - apiConfiguration.openRouterModelId in routerModels.openrouter, - }) - - const onMessage = useCallback((event: MessageEvent) => { - const message: ExtensionMessage = event.data - - switch (message.type) { - case "openAiModels": { - const updatedModels = message.openAiModels ?? [] - setOpenAiModels(Object.fromEntries(updatedModels.map((item) => [item, openAiModelInfoSaneDefaults]))) - break - } - case "ollamaModels": - { - const newModels = message.ollamaModels ?? [] - setOllamaModels(newModels) - } - break - case "lmStudioModels": - { - const newModels = message.lmStudioModels ?? [] - setLmStudioModels(newModels) - } - break - case "vsCodeLmModels": - { - const newModels = message.vsCodeLmModels ?? [] - setVsCodeLmModels(newModels) - } - break - } - }, []) - - useEvent("message", onMessage) - - const selectedProviderModelOptions = useMemo(() => { - if (selectedProvider === "pearai") { - return Object.keys(pearaiModels).map((modelId) => ({ - value: modelId, - label: modelId, - })) - } - return MODELS_BY_PROVIDER[selectedProvider] - ? Object.keys(MODELS_BY_PROVIDER[selectedProvider]).map((modelId) => ({ - value: modelId, - label: modelId, - })) - : [] - }, [selectedProvider, pearaiModels]) - - // Base URL for provider documentation - const DOC_BASE_URL = "https://docs.roocode.com/providers" - - // Custom URL path mappings for providers with different slugs - const providerUrlSlugs: Record = { - "openai-native": "openai", - openai: "openai-compatible", - } - - // Helper function to get provider display name from PROVIDERS constant - const getProviderDisplayName = (providerKey: string): string | undefined => { - const provider = PROVIDERS.find((p) => p.value === providerKey) - return provider?.label - } - - // Helper function to get the documentation URL and name for the currently selected provider - const getSelectedProviderDocUrl = (): { url: string; name: string } | undefined => { - const displayName = getProviderDisplayName(selectedProvider) - - if (!displayName) { - return undefined - } - - // Get the URL slug - use custom mapping if available, otherwise use the provider key - const urlSlug = providerUrlSlugs[selectedProvider] || selectedProvider - - return { - url: `${DOC_BASE_URL}/${urlSlug}`, - name: displayName, - } - } + const selectedProviderModels = useMemo( + () => { + const providerModels = MODELS_BY_PROVIDER[selectedProvider as keyof typeof MODELS_BY_PROVIDER]; + return providerModels + ? Object.keys(providerModels).map((modelId) => ({ + value: modelId, + label: modelId, + })) + : []; + }, + [selectedProvider], + ) - const onApiProviderChange = useCallback( - (value: ApiProvider) => { + const onProviderChange = useCallback( + (value: ProviderName) => { // It would be much easier to have a single attribute that stores // the modelId, but we have a separate attribute for each of // OpenRouter, Glama, Unbound, and Requesty. @@ -360,6 +223,11 @@ const ApiOptions = ({ setApiConfigurationField("requestyModelId", requestyDefaultModelId) } break + case "litellm": + if (!apiConfiguration.litellmModelId) { + setApiConfigurationField("litellmModelId", litellmDefaultModelId) + } + break } setApiConfigurationField("apiProvider", value) @@ -370,28 +238,45 @@ const ApiOptions = ({ apiConfiguration.glamaModelId, apiConfiguration.unboundModelId, apiConfiguration.requestyModelId, + apiConfiguration.litellmModelId, ], ) + const docs = useMemo(() => { + const provider = PROVIDERS.find(({ value }) => value === selectedProvider) + const name = provider?.label + + if (!name) { + return undefined + } + + // Get the URL slug - use custom mapping if available, otherwise use the provider key. + const slugs: Record = { + "openai-native": "openai", + openai: "openai-compatible", + } + + const slug = slugs[selectedProvider] || selectedProvider + return { + url: buildDocLink(`providers/${slug}`, "provider_docs"), + name, + } + }, [selectedProvider]) + return (
- {getSelectedProviderDocUrl() && ( + {docs && (
- - {t("settings:providers.providerDocumentation", { - provider: getSelectedProviderDocUrl()!.name, - })} + + {t("settings:providers.providerDocumentation", { provider: docs.name })}
)}
- onProviderChange(value as ProviderName)}> @@ -405,1271 +290,114 @@ const ApiOptions = ({
- {selectedProvider === "pearai" && ( -
- {!apiConfiguration?.pearaiApiKey ? ( - <> - { - vscode.postMessage({ - type: "openPearAIAuth", - }) - }}> - Login to PearAI - -

- Connect your PearAI account to use servers. -

- - ) : ( -

- User already logged in to PearAI. Click 'Done' to proceed! -

- )} -
- )} {errorMessage && } {selectedProvider === "openrouter" && ( - <> - -
- - {apiConfiguration?.openRouterApiKey && ( - - )} -
-
-
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.openRouterApiKey && ( - - {t("settings:providers.getOpenRouterApiKey")} - - )} - {!fromWelcomeView && ( - <> -
- { - setOpenRouterBaseUrlSelected(checked) - - if (!checked) { - setApiConfigurationField("openRouterBaseUrl", "") - } - }}> - {t("settings:providers.useCustomBaseUrl")} - - {openRouterBaseUrlSelected && ( - - )} -
- - , - }} - /> - - - )} - + )} - {selectedProvider === "anthropic" && ( - <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.apiKey && ( - - {t("settings:providers.getAnthropicApiKey")} - - )} -
- { - setAnthropicBaseUrlSelected(checked) - - if (!checked) { - setApiConfigurationField("anthropicBaseUrl", "") - setApiConfigurationField("anthropicUseAuthToken", false) // added - } - }}> - {t("settings:providers.useCustomBaseUrl")} - - {anthropicBaseUrlSelected && ( - <> - - - {/* added */} - - {t("settings:providers.anthropicUseAuthToken")} - - - )} -
- + {selectedProvider === "requesty" && ( + )} {selectedProvider === "glama" && ( - <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.glamaApiKey && ( - - {t("settings:providers.getGlamaApiKey")} - - )} - + )} - {selectedProvider === "requesty" && ( - <> - -
- - {apiConfiguration?.requestyApiKey && ( - - )} -
-
-
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.requestyApiKey && ( - - {t("settings:providers.getRequestyApiKey")} - - )} - + {selectedProvider === "unbound" && ( + )} - {selectedProvider === "openai-native" && ( - <> - { - setOpenAiNativeBaseUrlSelected(checked) + {selectedProvider === "anthropic" && ( + + )} - if (!checked) { - setApiConfigurationField("openAiNativeBaseUrl", "") - } - }}> - {t("settings:providers.useCustomBaseUrl")} - - {openAiNativeBaseUrlSelected && ( - <> - - - )} - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.openAiNativeApiKey && ( - - {t("settings:providers.getOpenAiApiKey")} - - )} - + {selectedProvider === "openai-native" && ( + )} {selectedProvider === "mistral" && ( - <> - - {t("settings:providers.mistralApiKey")} - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.mistralApiKey && ( - - {t("settings:providers.getMistralApiKey")} - - )} - {(apiConfiguration?.apiModelId?.startsWith("codestral-") || - (!apiConfiguration?.apiModelId && mistralDefaultModelId.startsWith("codestral-"))) && ( - <> - - - -
- {t("settings:providers.codestralBaseUrlDesc")} -
- - )} - + )} {selectedProvider === "bedrock" && ( - <> - (e.target as HTMLInputElement).value === "profile", - )}> - {t("settings:providers.awsCredentials")} - {t("settings:providers.awsProfile")} - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {apiConfiguration?.awsUseProfile ? ( - - - - ) : ( - <> - - - - - - - - - - - )} -
- - -
- - {t("settings:providers.awsCrossRegion")} - - {selectedModelInfo?.supportsPromptCache && ( - -
- {t("settings:providers.enablePromptCaching")} - -
-
- )} -
-
- {t("settings:providers.cacheUsageNote")} -
-
- + )} {selectedProvider === "vertex" && ( - <> -
-
{t("settings:providers.googleCloudSetup.title")}
-
- - {t("settings:providers.googleCloudSetup.step1")} - -
-
- - {t("settings:providers.googleCloudSetup.step2")} - -
-
- - {t("settings:providers.googleCloudSetup.step3")} - -
-
- - - - - - - - - -
- - -
- + )} {selectedProvider === "gemini" && ( - <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.geminiApiKey && ( - - {t("settings:providers.getGeminiApiKey")} - - )} -
- { - setGoogleGeminiBaseUrlSelected(checked) - - if (!checked) { - setApiConfigurationField("googleGeminiBaseUrl", "") - } - }}> - {t("settings:providers.useCustomBaseUrl")} - - {googleGeminiBaseUrlSelected && ( - - )} -
- + )} {selectedProvider === "openai" && ( - <> - - - - - - - - -
- { - setOpenAiLegacyFormatSelected(checked) - setApiConfigurationField("openAiLegacyFormat", checked) - }}> - {t("settings:providers.useLegacyFormat")} - -
- - {t("settings:modelInfo.enableStreaming")} - - - {t("settings:modelInfo.useAzure")} - -
- { - setAzureApiVersionSelected(checked) - - if (!checked) { - setApiConfigurationField("azureApiVersion", "") - } - }}> - {t("settings:modelInfo.azureApiVersion")} - - {azureApiVersionSelected && ( - - )} -
- - {/* Custom Headers UI */} -
-
- - - - -
- {!customHeaders.length ? ( -
- {t("settings:providers.noCustomHeaders")} -
- ) : ( - customHeaders.map(([key, value], index) => ( -
- handleUpdateHeaderKey(index, e.target.value)} - /> - handleUpdateHeaderValue(index, e.target.value)} - /> - handleRemoveCustomHeader(index)}> - - -
- )) - )} -
- -
- { - setApiConfigurationField("enableReasoningEffort", checked) - - if (!checked) { - const { reasoningEffort: _, ...openAiCustomModelInfo } = - apiConfiguration.openAiCustomModelInfo || openAiModelInfoSaneDefaults - - setApiConfigurationField("openAiCustomModelInfo", openAiCustomModelInfo) - } - }}> - {t("settings:providers.setReasoningLevel")} - - {!!apiConfiguration.enableReasoningEffort && ( - { - if (field === "reasoningEffort") { - const openAiCustomModelInfo = - apiConfiguration.openAiCustomModelInfo || openAiModelInfoSaneDefaults - - setApiConfigurationField("openAiCustomModelInfo", { - ...openAiCustomModelInfo, - reasoningEffort: value as ReasoningEffortType, - }) - } - }} - /> - )} -
-
-
- {t("settings:providers.customModel.capabilities")} -
- -
- { - const value = apiConfiguration?.openAiCustomModelInfo?.maxTokens - - if (!value) { - return "var(--vscode-input-border)" - } - - return value > 0 - ? "var(--vscode-charts-green)" - : "var(--vscode-errorForeground)" - })(), - }} - title={t("settings:providers.customModel.maxTokens.description")} - onInput={handleInputChange("openAiCustomModelInfo", (e) => { - const value = parseInt((e.target as HTMLInputElement).value) - - return { - ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), - maxTokens: isNaN(value) ? undefined : value, - } - })} - placeholder={t("settings:placeholders.numbers.maxTokens")} - className="w-full"> - - -
- {t("settings:providers.customModel.maxTokens.description")} -
-
- -
- { - const value = apiConfiguration?.openAiCustomModelInfo?.contextWindow - - if (!value) { - return "var(--vscode-input-border)" - } - - return value > 0 - ? "var(--vscode-charts-green)" - : "var(--vscode-errorForeground)" - })(), - }} - title={t("settings:providers.customModel.contextWindow.description")} - onInput={handleInputChange("openAiCustomModelInfo", (e) => { - const value = (e.target as HTMLInputElement).value - const parsed = parseInt(value) - - return { - ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), - contextWindow: isNaN(parsed) - ? openAiModelInfoSaneDefaults.contextWindow - : parsed, - } - })} - placeholder={t("settings:placeholders.numbers.contextWindow")} - className="w-full"> - - -
- {t("settings:providers.customModel.contextWindow.description")} -
-
- -
-
- { - return { - ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), - supportsImages: checked, - } - })}> - - {t("settings:providers.customModel.imageSupport.label")} - - - -
-
- {t("settings:providers.customModel.imageSupport.description")} -
-
- -
-
- { - return { - ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), - supportsComputerUse: checked, - } - })}> - - {t("settings:providers.customModel.computerUse.label")} - - - -
-
- {t("settings:providers.customModel.computerUse.description")} -
-
- -
-
- { - return { - ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), - supportsPromptCache: checked, - } - })}> - - {t("settings:providers.customModel.promptCache.label")} - - - -
-
- {t("settings:providers.customModel.promptCache.description")} -
-
- -
- { - const value = apiConfiguration?.openAiCustomModelInfo?.inputPrice - - if (!value && value !== 0) { - return "var(--vscode-input-border)" - } - - return value >= 0 - ? "var(--vscode-charts-green)" - : "var(--vscode-errorForeground)" - })(), - }} - onChange={handleInputChange("openAiCustomModelInfo", (e) => { - const value = (e.target as HTMLInputElement).value - const parsed = parseFloat(value) - - return { - ...(apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults), - inputPrice: isNaN(parsed) ? openAiModelInfoSaneDefaults.inputPrice : parsed, - } - })} - placeholder={t("settings:placeholders.numbers.inputPrice")} - className="w-full"> -
- - -
-
-
- -
- { - const value = apiConfiguration?.openAiCustomModelInfo?.outputPrice - - if (!value && value !== 0) { - return "var(--vscode-input-border)" - } - - return value >= 0 - ? "var(--vscode-charts-green)" - : "var(--vscode-errorForeground)" - })(), - }} - onChange={handleInputChange("openAiCustomModelInfo", (e) => { - const value = (e.target as HTMLInputElement).value - const parsed = parseFloat(value) - - return { - ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), - outputPrice: isNaN(parsed) ? openAiModelInfoSaneDefaults.outputPrice : parsed, - } - })} - placeholder={t("settings:placeholders.numbers.outputPrice")} - className="w-full"> -
- - -
-
-
- - {apiConfiguration?.openAiCustomModelInfo?.supportsPromptCache && ( - <> -
- { - const value = apiConfiguration?.openAiCustomModelInfo?.cacheReadsPrice - - if (!value && value !== 0) { - return "var(--vscode-input-border)" - } - - return value >= 0 - ? "var(--vscode-charts-green)" - : "var(--vscode-errorForeground)" - })(), - }} - onChange={handleInputChange("openAiCustomModelInfo", (e) => { - const value = (e.target as HTMLInputElement).value - const parsed = parseFloat(value) - - return { - ...(apiConfiguration?.openAiCustomModelInfo ?? - openAiModelInfoSaneDefaults), - cacheReadsPrice: isNaN(parsed) ? 0 : parsed, - } - })} - placeholder={t("settings:placeholders.numbers.inputPrice")} - className="w-full"> -
- - {t("settings:providers.customModel.pricing.cacheReads.label")} - - -
-
-
-
- { - const value = apiConfiguration?.openAiCustomModelInfo?.cacheWritesPrice - - if (!value && value !== 0) { - return "var(--vscode-input-border)" - } - - return value >= 0 - ? "var(--vscode-charts-green)" - : "var(--vscode-errorForeground)" - })(), - }} - onChange={handleInputChange("openAiCustomModelInfo", (e) => { - const value = (e.target as HTMLInputElement).value - const parsed = parseFloat(value) - - return { - ...(apiConfiguration?.openAiCustomModelInfo ?? - openAiModelInfoSaneDefaults), - cacheWritesPrice: isNaN(parsed) ? 0 : parsed, - } - })} - placeholder={t("settings:placeholders.numbers.cacheWritePrice")} - className="w-full"> -
- - -
-
-
- - )} - - -
- + )} {selectedProvider === "lmstudio" && ( - <> - - - - - - - {lmStudioModels.length > 0 && ( - - {lmStudioModels.map((model) => ( - - {model} - - ))} - - )} - { - setApiConfigurationField("lmStudioSpeculativeDecodingEnabled", checked) - }}> - {t("settings:providers.lmStudio.speculativeDecoding")} - - {apiConfiguration?.lmStudioSpeculativeDecodingEnabled && ( - <> -
- - - -
- {t("settings:providers.lmStudio.draftModelDesc")} -
-
- {lmStudioModels.length > 0 && ( - <> -
- {t("settings:providers.lmStudio.selectDraftModel")} -
- - {lmStudioModels.map((model) => ( - - {model} - - ))} - - {lmStudioModels.length === 0 && ( -
- {t("settings:providers.lmStudio.noModelsFound")} -
- )} - - )} - - )} -
- , - b: , - span: ( - - Note: - - ), - }} - /> -
- + )} {selectedProvider === "deepseek" && ( - <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.deepSeekApiKey && ( - - {t("settings:providers.getDeepSeekApiKey")} - - )} - + )} {selectedProvider === "vscode-lm" && ( - <> -
- - {vsCodeLmModels.length > 0 ? ( - - ) : ( -
- {t("settings:providers.vscodeLmDescription")} -
- )} -
-
{t("settings:providers.vscodeLmWarning")}
- + )} {selectedProvider === "ollama" && ( - <> - - - - - - - {ollamaModels.length > 0 && ( - - {ollamaModels.map((model) => ( - - {model} - - ))} - - )} -
- {t("settings:providers.ollama.description")} - - {t("settings:providers.ollama.warning")} - -
- + )} {selectedProvider === "xai" && ( - <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.xaiApiKey && ( - - {t("settings:providers.getXaiApiKey")} - - )} - + )} - {selectedProvider === "unbound" && ( - <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.unboundApiKey && ( - - {t("settings:providers.getUnboundApiKey")} - - )} - + {selectedProvider === "groq" && ( + + )} + + {selectedProvider === "chutes" && ( + + )} + + {selectedProvider === "litellm" && ( + )} {selectedProvider === "human-relay" && ( @@ -1683,99 +411,10 @@ const ApiOptions = ({ )} - {/* Model Pickers */} - - {selectedProvider === "openrouter" && ( - - )} - - {selectedProvider === "openrouter" && - openRouterModelProviders && - Object.keys(openRouterModelProviders).length > 0 && ( -
-
- - - - -
- -
- {t("settings:providers.openRouter.providerRouting.description")}{" "} - - {t("settings:providers.openRouter.providerRouting.learnMore")}. - -
-
- )} - - {selectedProvider === "glama" && ( - - )} - - {selectedProvider === "unbound" && ( - - )} - - {selectedProvider === "requesty" && ( - - )} - - {selectedProviderModelOptions.length > 0 && ( + {selectedProviderModels.length > 0 && ( <>
- - {selectedModelId === "gemini-2.5-pro-preview-03-25" + {selectedModelId === "gemini-2.5-pro-preview-03-25" || + selectedModelId === "gemini-2.5-pro-preview-05-06" ? t("settings:modelInfo.gemini.billingEstimate") : t("settings:modelInfo.gemini.freeRequests", { count: selectedModelId && selectedModelId.includes("flash") ? 15 : 2, diff --git a/webview-ui/src/components/settings/ModelPicker.tsx b/webview-ui/src/components/settings/ModelPicker.tsx index 442656a5a62..48dd999e384 100644 --- a/webview-ui/src/components/settings/ModelPicker.tsx +++ b/webview-ui/src/components/settings/ModelPicker.tsx @@ -26,7 +26,7 @@ import { ModelInfoView } from "./ModelInfoView" type ModelIdKey = keyof Pick< ProviderSettings, - "glamaModelId" | "openRouterModelId" | "unboundModelId" | "requestyModelId" | "openAiModelId" + "glamaModelId" | "openRouterModelId" | "unboundModelId" | "requestyModelId" | "openAiModelId" | "litellmModelId" > interface ModelPickerProps { diff --git a/webview-ui/src/components/settings/PromptCachingControl.tsx b/webview-ui/src/components/settings/PromptCachingControl.tsx deleted file mode 100644 index ad68d8d8a9b..00000000000 --- a/webview-ui/src/components/settings/PromptCachingControl.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" - -import { ApiConfiguration } from "@roo/shared/api" - -import { useAppTranslation } from "@src/i18n/TranslationContext" - -interface PromptCachingControlProps { - apiConfiguration: ApiConfiguration - setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void -} - -export const PromptCachingControl = ({ apiConfiguration, setApiConfigurationField }: PromptCachingControlProps) => { - const { t } = useAppTranslation() - - return ( - <> -
- setApiConfigurationField("promptCachingEnabled", e.target.checked)}> - - -
- {t("settings:promptCaching.description")} -
-
- - ) -} diff --git a/webview-ui/src/components/settings/ReasoningEffort.tsx b/webview-ui/src/components/settings/ReasoningEffort.tsx index 8e3c399ae92..53d21a2c498 100644 --- a/webview-ui/src/components/settings/ReasoningEffort.tsx +++ b/webview-ui/src/components/settings/ReasoningEffort.tsx @@ -2,12 +2,12 @@ import { useAppTranslation } from "@/i18n/TranslationContext" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui" -import { ApiConfiguration } from "@roo/shared/api" +import { ProviderSettings } from "@roo/shared/api" import { reasoningEfforts, ReasoningEffort as ReasoningEffortType } from "@roo/schemas" interface ReasoningEffortProps { - apiConfiguration: ApiConfiguration - setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: K, value: ProviderSettings[K]) => void } export const ReasoningEffort = ({ apiConfiguration, setApiConfigurationField }: ReasoningEffortProps) => { diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 30e2fe5c2b3..9cca41bcd43 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -1,4 +1,14 @@ -import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react" +import React, { + forwardRef, + memo, + useCallback, + useEffect, + useImperativeHandle, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react" import { useAppTranslation } from "@/i18n/TranslationContext" import { CheckCheck, @@ -14,11 +24,10 @@ import { Info, LucideIcon, } from "lucide-react" -import { CaretSortIcon } from "@radix-ui/react-icons" import { ExperimentId } from "@roo/shared/experiments" import { TelemetrySetting } from "@roo/shared/TelemetrySetting" -import { ApiConfiguration } from "@roo/shared/api" +import { ProviderSettings } from "@roo/shared/api" import { vscode } from "@/utils/vscode" import { ExtensionStateContextType, useExtensionState } from "@/context/ExtensionStateContext" @@ -32,13 +41,13 @@ import { AlertDialogHeader, AlertDialogFooter, Button, - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, } from "@/components/ui" -import { Tab, TabContent, TabHeader } from "../common/Tab" +import { Tab, TabContent, TabHeader, TabList, TabTrigger } from "../common/Tab" import { SetCachedStateField, SetExperimentEnabled } from "./types" import { SectionHeader } from "./SectionHeader" import ApiConfigManager from "./ApiConfigManager" @@ -53,6 +62,14 @@ import { ExperimentalSettings } from "./ExperimentalSettings" import { LanguageSettings } from "./LanguageSettings" import { About } from "./About" import { Section } from "./Section" +import { cn } from "@/lib/utils" + +export const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden" +export const settingsTabList = + "w-48 data-[compact=true]:w-12 flex-shrink-0 flex flex-col overflow-y-auto overflow-x-hidden border-r border-vscode-sideBar-background" +export const settingsTabTrigger = + "whitespace-nowrap overflow-hidden min-w-0 h-12 px-4 py-3 box-border flex items-center border-l-2 border-transparent text-vscode-foreground opacity-70 hover:bg-vscode-list-hoverBackground data-[compact=true]:w-12 data-[compact=true]:p-4" +export const settingsTabTriggerActive = "opacity-100 border-vscode-focusBorder bg-vscode-list-activeSelectionBackground" export interface SettingsViewRef { checkUnsaveChanges: (then: () => void) => void @@ -87,6 +104,11 @@ const SettingsView = forwardRef(({ onDone, t const [isDiscardDialogShow, setDiscardDialogShow] = useState(false) const [isChangeDetected, setChangeDetected] = useState(false) const [errorMessage, setErrorMessage] = useState(undefined) + const [activeTab, setActiveTab] = useState( + targetSection && sectionNames.includes(targetSection as SectionName) + ? (targetSection as SectionName) + : "providers", + ) const prevApiConfigName = useRef(currentApiConfigName) const confirmDialogHandler = useRef<() => void>() @@ -125,7 +147,7 @@ const SettingsView = forwardRef(({ onDone, t telemetrySetting, terminalOutputLineLimit, terminalShellIntegrationTimeout, - terminalShellIntegrationDisabled, + terminalShellIntegrationDisabled, // Added from upstream terminalCommandDelay, terminalPowershellCounter, terminalZshClearEolMark, @@ -139,7 +161,6 @@ const SettingsView = forwardRef(({ onDone, t terminalCompressProgressBar, } = cachedState - // Make sure apiConfiguration is initialized and managed by SettingsView. const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) useEffect(() => { // Update only when currentApiConfigName is changed. @@ -173,7 +194,7 @@ const SettingsView = forwardRef(({ onDone, t }, []) const setApiConfigurationField = useCallback( - (field: K, value: ApiConfiguration[K]) => { + (field: K, value: ProviderSettings[K]) => { setCachedState((prevState) => { if (prevState.apiConfiguration?.[field] === value) { return prevState @@ -278,81 +299,116 @@ const SettingsView = forwardRef(({ onDone, t useImperativeHandle(ref, () => ({ checkUnsaveChanges }), [checkUnsaveChanges]) - const onConfirmDialogResult = useCallback((confirm: boolean) => { - if (confirm) { - confirmDialogHandler.current?.() + const onConfirmDialogResult = useCallback( + (confirm: boolean) => { + if (confirm) { + // Discard changes: Reset state and flag + setCachedState(extensionState) // Revert to original state + setChangeDetected(false) // Reset change flag + confirmDialogHandler.current?.() // Execute the pending action (e.g., tab switch) + } + // If confirm is false (Cancel), do nothing, dialog closes automatically + }, + [extensionState], // Depend on extensionState to get the latest original state + ) + + // Handle tab changes with unsaved changes check + const handleTabChange = useCallback( + (newTab: SectionName) => { + // Directly switch tab without checking for unsaved changes + setActiveTab(newTab) + }, + [], // No dependency on isChangeDetected needed anymore + ) + + // Store direct DOM element refs for each tab + const tabRefs = useRef>( + Object.fromEntries(sectionNames.map((name) => [name, null])) as Record, + ) + + // Track whether we're in compact mode + const [isCompactMode, setIsCompactMode] = useState(false) + const containerRef = useRef(null) + + // Setup resize observer to detect when we should switch to compact mode + useEffect(() => { + if (!containerRef.current) return + + const observer = new ResizeObserver((entries) => { + for (const entry of entries) { + // If container width is less than 500px, switch to compact mode + setIsCompactMode(entry.contentRect.width < 500) + } + }) + + observer.observe(containerRef.current) + + return () => { + observer?.disconnect() } }, []) - const providersRef = useRef(null) - const autoApproveRef = useRef(null) - const browserRef = useRef(null) - const checkpointsRef = useRef(null) - const notificationsRef = useRef(null) - const contextManagementRef = useRef(null) - const terminalRef = useRef(null) - const experimentalRef = useRef(null) - const languageRef = useRef(null) - const aboutRef = useRef(null) - - const sections: { id: SectionName; icon: LucideIcon; ref: React.RefObject }[] = useMemo( + const sections: { id: SectionName; icon: LucideIcon }[] = useMemo( () => [ - { id: "providers", icon: Webhook, ref: providersRef }, - { id: "autoApprove", icon: CheckCheck, ref: autoApproveRef }, - { id: "browser", icon: SquareMousePointer, ref: browserRef }, - { id: "checkpoints", icon: GitBranch, ref: checkpointsRef }, - { id: "notifications", icon: Bell, ref: notificationsRef }, - { id: "contextManagement", icon: Database, ref: contextManagementRef }, - { id: "terminal", icon: SquareTerminal, ref: terminalRef }, - { id: "experimental", icon: FlaskConical, ref: experimentalRef }, - { id: "language", icon: Globe, ref: languageRef }, - { id: "about", icon: Info, ref: aboutRef }, - ], - [ - providersRef, - autoApproveRef, - browserRef, - checkpointsRef, - notificationsRef, - contextManagementRef, - terminalRef, - experimentalRef, + { id: "providers", icon: Webhook }, + { id: "autoApprove", icon: CheckCheck }, + { id: "browser", icon: SquareMousePointer }, + { id: "checkpoints", icon: GitBranch }, + { id: "notifications", icon: Bell }, + { id: "contextManagement", icon: Database }, + { id: "terminal", icon: SquareTerminal }, + { id: "experimental", icon: FlaskConical }, + { id: "language", icon: Globe }, + { id: "about", icon: Info }, ], + [], // No dependencies needed now ) - const scrollToSection = (ref: React.RefObject) => ref.current?.scrollIntoView() + // Update target section logic to set active tab + useEffect(() => { + if (targetSection && sectionNames.includes(targetSection as SectionName)) { + setActiveTab(targetSection as SectionName) + } + }, [targetSection]) + + // Function to scroll the active tab into view for vertical layout + const scrollToActiveTab = useCallback(() => { + const activeTabElement = tabRefs.current[activeTab] + + if (activeTabElement) { + activeTabElement.scrollIntoView({ + behavior: "auto", + block: "nearest", + }) + } + }, [activeTab]) - // Scroll to target section when specified + // Effect to scroll when the active tab changes useEffect(() => { - if (targetSection) { - const sectionObj = sections.find((section) => section.id === targetSection) - if (sectionObj && sectionObj.ref.current) { - // Use setTimeout to ensure the scroll happens after render - setTimeout(() => scrollToSection(sectionObj.ref), 500) + scrollToActiveTab() + }, [activeTab, scrollToActiveTab]) + + // Effect to scroll when the webview becomes visible + useLayoutEffect(() => { + const handleMessage = (event: MessageEvent) => { + const message = event.data + if (message.type === "action" && message.action === "didBecomeVisible") { + scrollToActiveTab() } } - }, [targetSection, sections]) + + window.addEventListener("message", handleMessage) + + return () => { + window.removeEventListener("message", handleMessage) + } + }, [scrollToActiveTab]) return (

{t("settings:header.title")}

- - - - - - {sections.map(({ id, icon: Icon, ref }) => ( - scrollToSection(ref)}> - - {t(`settings:sections.${id}`)} - - ))} - -
- -
- -
- -
{t("settings:sections.providers")}
+ {/* Vertical tabs layout */} +
+ {/* Tab sidebar */} + handleTabChange(value as SectionName)} + className={cn(settingsTabList)} + data-compact={isCompactMode} + data-testid="settings-tab-list"> + {sections.map(({ id, icon: Icon }) => { + const isSelected = id === activeTab + const onSelect = () => handleTabChange(id) + + // Base TabTrigger component definition + // We pass isSelected manually for styling, but onSelect is handled conditionally + const triggerComponent = ( + (tabRefs.current[id] = element)} + value={id} + isSelected={isSelected} // Pass manually for styling state + className={cn( + isSelected // Use manual isSelected for styling + ? `${settingsTabTrigger} ${settingsTabTriggerActive}` + : settingsTabTrigger, + "focus:ring-0", // Remove the focus ring styling + )} + data-testid={`tab-${id}`} + data-compact={isCompactMode}> +
+ + {t(`settings:sections.${id}`)} +
+
+ ) + + if (isCompactMode) { + // Wrap in Tooltip and manually add onClick to the trigger + return ( + + + + {/* Clone to avoid ref issues if triggerComponent itself had a key */} + {React.cloneElement(triggerComponent)} + + +

{t(`settings:sections.${id}`)}

+
+
+
+ ) + } else { + // Render trigger directly; TabList will inject onSelect via cloning + // Ensure the element passed to TabList has the key + return React.cloneElement(triggerComponent, { key: id }) + } + })} +
+ + {/* Content area */} + + {/* Providers Section */} + {activeTab === "providers" && ( +
+ +
+ +
{t("settings:sections.providers")}
+
+
+ +
+ + checkUnsaveChanges(() => + vscode.postMessage({ type: "loadApiConfiguration", text: configName }), + ) + } + onDeleteConfig={(configName: string) => + vscode.postMessage({ type: "deleteApiConfiguration", text: configName }) + } + onRenameConfig={(oldName: string, newName: string) => { + vscode.postMessage({ + type: "renameApiConfiguration", + values: { oldName, newName }, + apiConfiguration, + }) + prevApiConfigName.current = newName + }} + onUpsertConfig={(configName: string) => + vscode.postMessage({ + type: "upsertApiConfiguration", + text: configName, + apiConfiguration, + }) + } + /> + +
- - -
- - checkUnsaveChanges(() => - vscode.postMessage({ type: "loadApiConfiguration", text: configName }), - ) - } - onDeleteConfig={(configName: string) => - vscode.postMessage({ type: "deleteApiConfiguration", text: configName }) - } - onRenameConfig={(oldName: string, newName: string) => { - vscode.postMessage({ - type: "renameApiConfiguration", - values: { oldName, newName }, - apiConfiguration, - }) - prevApiConfigName.current = newName - }} - onUpsertConfig={(configName: string) => - vscode.postMessage({ - type: "upsertApiConfiguration", - text: configName, - apiConfiguration, - }) - } + )} + + {/* Auto-Approve Section */} + {activeTab === "autoApprove" && ( + - -
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
+ )} -
- -
- + {/* Checkpoints Section */} + {activeTab === "checkpoints" && ( + + )} + + {/* Notifications Section */} + {activeTab === "notifications" && ( + + )} + + {/* Context Management Section */} + {activeTab === "contextManagement" && ( + + )} + + {/* Terminal Section */} + {activeTab === "terminal" && ( + + )} + + {/* Experimental Section */} + {activeTab === "experimental" && ( + + )} + + {/* Language Section */} + {activeTab === "language" && ( + + )} + + {/* About Section */} + {activeTab === "about" && ( + + )} + +
diff --git a/webview-ui/src/components/settings/TerminalSettings.tsx b/webview-ui/src/components/settings/TerminalSettings.tsx index 452314ac39e..59c434fbb20 100644 --- a/webview-ui/src/components/settings/TerminalSettings.tsx +++ b/webview-ui/src/components/settings/TerminalSettings.tsx @@ -1,7 +1,13 @@ -import { HTMLAttributes } from "react" +import { HTMLAttributes, useState, useCallback } from "react" import { useAppTranslation } from "@/i18n/TranslationContext" +import { vscode } from "@/utils/vscode" import { SquareTerminal } from "lucide-react" -import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" +import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react" +import { Trans } from "react-i18next" +import { buildDocLink } from "@src/utils/docLinks" +import { useEvent, useMount } from "react-use" + +import { ExtensionMessage } from "@roo/shared/ExtensionMessage" import { cn } from "@/lib/utils" import { Slider } from "@/components/ui" @@ -52,8 +58,32 @@ export const TerminalSettings = ({ }: TerminalSettingsProps) => { const { t } = useAppTranslation() + const [inheritEnv, setInheritEnv] = useState(true) + + useMount(() => vscode.postMessage({ type: "getVSCodeSetting", setting: "terminal.integrated.inheritEnv" })) + + const onMessage = useCallback((event: MessageEvent) => { + const message: ExtensionMessage = event.data + + switch (message.type) { + case "vsCodeSetting": + switch (message.setting) { + case "terminal.integrated.inheritEnv": + setInheritEnv(message.value ?? true) + break + default: + break + } + break + default: + break + } + }, []) + + useEvent("message", onMessage) + return ( -
+
@@ -62,149 +92,316 @@ export const TerminalSettings = ({
-
- -
- setCachedStateField("terminalOutputLineLimit", value)} - data-testid="terminal-output-limit-slider" - /> - {terminalOutputLineLimit ?? 500} + {/* Basic Settings */} +
+
+
+ +
{t("settings:terminal.basic.label")}
+
-
- {t("settings:terminal.outputLineLimit.description")} +
+
+ +
+ setCachedStateField("terminalOutputLineLimit", value)} + data-testid="terminal-output-limit-slider" + /> + {terminalOutputLineLimit ?? 500} +
+
+ + + {" "} + + +
+
+
+ + setCachedStateField("terminalCompressProgressBar", e.target.checked) + } + data-testid="terminal-compress-progress-bar-checkbox"> + {t("settings:terminal.compressProgressBar.label")} + +
+ + + {" "} + + +
+
-
- setCachedStateField("terminalCompressProgressBar", e.target.checked)} - data-testid="terminal-compress-progress-bar-checkbox"> - {t("settings:terminal.compressProgressBar.label")} - -
- {t("settings:terminal.compressProgressBar.description")} + {/* Advanced Settings */} +
+
+
+ +
{t("settings:terminal.advanced.label")}
+
+
+ {t("settings:terminal.advanced.description")} +
-
+
+
+ { + setInheritEnv(e.target.checked) + vscode.postMessage({ + type: "updateVSCodeSetting", + setting: "terminal.integrated.inheritEnv", + value: e.target.checked, + }) + }} + data-testid="terminal-inherit-env-checkbox"> + {t("settings:terminal.inheritEnv.label")} + +
+ + + {" "} + + +
+
-
- -
- - setCachedStateField( - "terminalShellIntegrationTimeout", - Math.min(60_000, Math.max(5_000, value)), - ) - } - /> - {(terminalShellIntegrationTimeout ?? 5000) / 1000}s -
-
- {t("settings:terminal.shellIntegrationTimeout.description")} -
-
+
+ + setCachedStateField("terminalShellIntegrationDisabled", e.target.checked) + }> + + {t("settings:terminal.shellIntegrationDisabled.label")} + + +
+ + + {" "} + + +
+
-
- - setCachedStateField("terminalShellIntegrationDisabled", e.target.checked) - }> - {t("settings:terminal.shellIntegrationDisabled.label")} - -
- {t("settings:terminal.shellIntegrationDisabled.description")} -
-
+ {!terminalShellIntegrationDisabled && ( + <> +
+ +
+ + setCachedStateField( + "terminalShellIntegrationTimeout", + Math.min(60000, Math.max(1000, value)), + ) + } + /> + + {(terminalShellIntegrationTimeout ?? 5000) / 1000}s + +
+
+ + + {" "} + + +
+
-
- -
- - setCachedStateField("terminalCommandDelay", Math.min(1000, Math.max(0, value))) - } - /> - {terminalCommandDelay ?? 50}ms -
-
- {t("settings:terminal.commandDelay.description")} -
-
+
+ +
+ + setCachedStateField( + "terminalCommandDelay", + Math.min(1000, Math.max(0, value)), + ) + } + /> + {terminalCommandDelay ?? 50}ms +
+
+ + + {" "} + + +
+
-
- setCachedStateField("terminalPowershellCounter", e.target.checked)} - data-testid="terminal-powershell-counter-checkbox"> - {t("settings:terminal.powershellCounter.label")} - -
- {t("settings:terminal.powershellCounter.description")} -
-
+
+ + setCachedStateField("terminalPowershellCounter", e.target.checked) + } + data-testid="terminal-powershell-counter-checkbox"> + + {t("settings:terminal.powershellCounter.label")} + + +
+ + + {" "} + + +
+
-
- setCachedStateField("terminalZshClearEolMark", e.target.checked)} - data-testid="terminal-zsh-clear-eol-mark-checkbox"> - {t("settings:terminal.zshClearEolMark.label")} - -
- {t("settings:terminal.zshClearEolMark.description")} -
-
+
+ + setCachedStateField("terminalZshClearEolMark", e.target.checked) + } + data-testid="terminal-zsh-clear-eol-mark-checkbox"> + + {t("settings:terminal.zshClearEolMark.label")} + + +
+ + + {" "} + + +
+
-
- setCachedStateField("terminalZshOhMy", e.target.checked)} - data-testid="terminal-zsh-oh-my-checkbox"> - {t("settings:terminal.zshOhMy.label")} - -
- {t("settings:terminal.zshOhMy.description")} -
-
+
+ setCachedStateField("terminalZshOhMy", e.target.checked)} + data-testid="terminal-zsh-oh-my-checkbox"> + {t("settings:terminal.zshOhMy.label")} + +
+ + + {" "} + + +
+
-
- setCachedStateField("terminalZshP10k", e.target.checked)} - data-testid="terminal-zsh-p10k-checkbox"> - {t("settings:terminal.zshP10k.label")} - -
- {t("settings:terminal.zshP10k.description")} -
-
+
+ setCachedStateField("terminalZshP10k", e.target.checked)} + data-testid="terminal-zsh-p10k-checkbox"> + {t("settings:terminal.zshP10k.label")} + +
+ + + {" "} + + +
+
-
- setCachedStateField("terminalZdotdir", e.target.checked)} - data-testid="terminal-zdotdir-checkbox"> - {t("settings:terminal.zdotdir.label")} - -
- {t("settings:terminal.zdotdir.description")} +
+ setCachedStateField("terminalZdotdir", e.target.checked)} + data-testid="terminal-zdotdir-checkbox"> + {t("settings:terminal.zdotdir.label")} + +
+ + + {" "} + + +
+
+ + )}
diff --git a/webview-ui/src/components/settings/ThinkingBudget.tsx b/webview-ui/src/components/settings/ThinkingBudget.tsx index 1ee2f310e0a..6c4352f22b9 100644 --- a/webview-ui/src/components/settings/ThinkingBudget.tsx +++ b/webview-ui/src/components/settings/ThinkingBudget.tsx @@ -3,14 +3,14 @@ import { useAppTranslation } from "@/i18n/TranslationContext" import { Slider } from "@/components/ui" -import { ApiConfiguration, ModelInfo } from "@roo/shared/api" +import { ProviderSettings, ModelInfo } from "@roo/shared/api" const DEFAULT_MAX_OUTPUT_TOKENS = 16_384 const DEFAULT_MAX_THINKING_TOKENS = 8_192 interface ThinkingBudgetProps { - apiConfiguration: ApiConfiguration - setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: K, value: ProviderSettings[K]) => void modelInfo?: ModelInfo } diff --git a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx index 115c623a27d..b9b1d1d21e1 100644 --- a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx @@ -3,7 +3,7 @@ import { render, screen, fireEvent } from "@testing-library/react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { ApiConfiguration } from "@roo/shared/api" +import { ProviderSettings } from "@roo/shared/api" import { ExtensionStateContextProvider } from "@/context/ExtensionStateContext" import { openAiModelInfoSaneDefaults } from "@roo/shared/api" @@ -148,7 +148,7 @@ jest.mock("../DiffSettingsControl", () => ({ })) jest.mock("@src/components/ui/hooks/useSelectedModel", () => ({ - useSelectedModel: jest.fn((apiConfiguration: ApiConfiguration) => { + useSelectedModel: jest.fn((apiConfiguration: ProviderSettings) => { if (apiConfiguration.apiModelId?.includes("thinking")) { return { provider: apiConfiguration.apiProvider, diff --git a/webview-ui/src/components/settings/__tests__/AutoApproveToggle.test.tsx b/webview-ui/src/components/settings/__tests__/AutoApproveToggle.test.tsx new file mode 100644 index 00000000000..ce6c978ff28 --- /dev/null +++ b/webview-ui/src/components/settings/__tests__/AutoApproveToggle.test.tsx @@ -0,0 +1,91 @@ +import { render, screen, fireEvent } from "@testing-library/react" +import "@testing-library/jest-dom" + +import { AutoApproveToggle, autoApproveSettingsConfig } from "../AutoApproveToggle" +import { TranslationProvider } from "@/i18n/__mocks__/TranslationContext" + +jest.mock("@/i18n/TranslationContext", () => { + const actual = jest.requireActual("@/i18n/TranslationContext") + return { + ...actual, + useAppTranslation: () => ({ + t: (key: string) => key, + }), + } +}) + +describe("AutoApproveToggle", () => { + const mockOnToggle = jest.fn() + const initialProps = { + alwaysAllowReadOnly: true, + alwaysAllowWrite: false, + alwaysAllowBrowser: false, + alwaysApproveResubmit: true, + alwaysAllowMcp: false, + alwaysAllowModeSwitch: true, + alwaysAllowSubtasks: false, + alwaysAllowExecute: true, + onToggle: mockOnToggle, + } + + beforeEach(() => { + mockOnToggle.mockClear() + }) + + test("renders all toggle buttons with correct initial ARIA attributes", () => { + render( + + + , + ) + + Object.values(autoApproveSettingsConfig).forEach((config) => { + const button = screen.getByTestId(config.testId) + expect(button).toBeInTheDocument() + expect(button).toHaveAttribute("aria-label", config.labelKey) + expect(button).toHaveAttribute("aria-pressed", String(initialProps[config.key])) + }) + }) + + test("calls onToggle with the correct key and value when a button is clicked", () => { + render( + + + , + ) + + const writeToggleButton = screen.getByTestId(autoApproveSettingsConfig.alwaysAllowWrite.testId) + fireEvent.click(writeToggleButton) + + expect(mockOnToggle).toHaveBeenCalledTimes(1) + expect(mockOnToggle).toHaveBeenCalledWith("alwaysAllowWrite", true) + + const readOnlyButton = screen.getByTestId(autoApproveSettingsConfig.alwaysAllowReadOnly.testId) + fireEvent.click(readOnlyButton) + expect(mockOnToggle).toHaveBeenCalledTimes(2) + expect(mockOnToggle).toHaveBeenCalledWith("alwaysAllowReadOnly", false) + }) + + test("updates aria-pressed attribute after toggle", () => { + const { rerender } = render( + + + , + ) + + const writeToggleButton = screen.getByTestId(autoApproveSettingsConfig.alwaysAllowWrite.testId) + expect(writeToggleButton).toHaveAttribute("aria-pressed", "false") + + const updatedProps = { ...initialProps, alwaysAllowWrite: true } + rerender( + + + , + ) + + expect(screen.getByTestId(autoApproveSettingsConfig.alwaysAllowWrite.testId)).toHaveAttribute( + "aria-pressed", + "true", + ) + }) +}) diff --git a/webview-ui/src/components/settings/__tests__/SettingsView.test.tsx b/webview-ui/src/components/settings/__tests__/SettingsView.test.tsx index 81f3dea1fd6..072296a7542 100644 --- a/webview-ui/src/components/settings/__tests__/SettingsView.test.tsx +++ b/webview-ui/src/components/settings/__tests__/SettingsView.test.tsx @@ -1,5 +1,4 @@ -// npx jest src/components/settings/__tests__/SettingsView.test.ts - +import React from "react" import { render, screen, fireEvent } from "@testing-library/react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" @@ -81,6 +80,47 @@ jest.mock("@vscode/webview-ui-toolkit/react", () => ({ VSCodeRadioGroup: ({ children, onChange }: any) =>
{children}
, })) +// Mock Tab components +jest.mock("../../../components/common/Tab", () => ({ + ...jest.requireActual("../../../components/common/Tab"), + Tab: ({ children }: any) =>
{children}
, + TabHeader: ({ children }: any) =>
{children}
, + TabContent: ({ children }: any) =>
{children}
, + TabList: ({ children, value, onValueChange, "data-testid": dataTestId }: any) => { + // Store onValueChange in a global variable so TabTrigger can access it + ;(window as any).__onValueChange = onValueChange + return ( +
+ {children} +
+ ) + }, + TabTrigger: ({ children, value, "data-testid": dataTestId, onClick, isSelected }: any) => { + // This function simulates clicking on a tab and making its content visible + const handleClick = () => { + if (onClick) onClick() + // Access onValueChange from the global variable + const onValueChange = (window as any).__onValueChange + if (onValueChange) onValueChange(value) + // Make all tab contents invisible + document.querySelectorAll("[data-tab-content]").forEach((el) => { + ;(el as HTMLElement).style.display = "none" + }) + // Make this tab's content visible + const tabContent = document.querySelector(`[data-tab-content="${value}"]`) + if (tabContent) { + ;(tabContent as HTMLElement).style.display = "block" + } + } + + return ( + + ) + }, +})) + // Mock Slider component jest.mock("@/components/ui", () => ({ ...jest.requireActual("@/components/ui"), @@ -129,7 +169,7 @@ const renderSettingsView = () => { const onDone = jest.fn() const queryClient = new QueryClient() - render( + const result = render( @@ -140,7 +180,20 @@ const renderSettingsView = () => { // Hydrate initial state. mockPostMessage({}) - return { onDone } + // Helper function to activate a tab and ensure its content is visible + const activateTab = (tabId: string) => { + // Skip trying to find and click the tab, just directly render with the target section + // This bypasses the actual tab clicking mechanism but ensures the content is shown + result.rerender( + + + + + , + ) + } + + return { onDone, activateTab } } describe("SettingsView - Sound Settings", () => { @@ -149,7 +202,11 @@ describe("SettingsView - Sound Settings", () => { }) it("initializes with tts disabled by default", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") const ttsCheckbox = screen.getByTestId("tts-enabled-checkbox") expect(ttsCheckbox).not.toBeChecked() @@ -159,7 +216,11 @@ describe("SettingsView - Sound Settings", () => { }) it("initializes with sound disabled by default", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") const soundCheckbox = screen.getByTestId("sound-enabled-checkbox") expect(soundCheckbox).not.toBeChecked() @@ -169,7 +230,11 @@ describe("SettingsView - Sound Settings", () => { }) it("toggles tts setting and sends message to VSCode", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") const ttsCheckbox = screen.getByTestId("tts-enabled-checkbox") @@ -190,7 +255,11 @@ describe("SettingsView - Sound Settings", () => { }) it("toggles sound setting and sends message to VSCode", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") const soundCheckbox = screen.getByTestId("sound-enabled-checkbox") @@ -211,7 +280,11 @@ describe("SettingsView - Sound Settings", () => { }) it("shows tts slider when sound is enabled", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") // Enable tts const ttsCheckbox = screen.getByTestId("tts-enabled-checkbox") @@ -224,7 +297,11 @@ describe("SettingsView - Sound Settings", () => { }) it("shows volume slider when sound is enabled", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") // Enable sound const soundCheckbox = screen.getByTestId("sound-enabled-checkbox") @@ -237,7 +314,11 @@ describe("SettingsView - Sound Settings", () => { }) it("updates speed and sends message to VSCode when slider changes", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") // Enable tts const ttsCheckbox = screen.getByTestId("tts-enabled-checkbox") @@ -259,7 +340,11 @@ describe("SettingsView - Sound Settings", () => { }) it("updates volume and sends message to VSCode when slider changes", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") // Enable sound const soundCheckbox = screen.getByTestId("sound-enabled-checkbox") @@ -269,9 +354,9 @@ describe("SettingsView - Sound Settings", () => { const volumeSlider = screen.getByTestId("sound-volume-slider") fireEvent.change(volumeSlider, { target: { value: "0.75" } }) - // Click Save to save settings - const saveButton = screen.getByTestId("save-button") - fireEvent.click(saveButton) + // Click Save to save settings - use getAllByTestId to handle multiple elements + const saveButtons = screen.getAllByTestId("save-button") + fireEvent.click(saveButtons[0]) // Verify message sent to VSCode expect(vscode.postMessage).toHaveBeenCalledWith({ @@ -299,7 +384,11 @@ describe("SettingsView - Allowed Commands", () => { }) it("shows allowed commands section when alwaysAllowExecute is enabled", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the autoApprove tab + activateTab("autoApprove") // Enable always allow execute const executeCheckbox = screen.getByTestId("always-allow-execute-toggle") @@ -310,7 +399,11 @@ describe("SettingsView - Allowed Commands", () => { }) it("adds new command to the list", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the autoApprove tab + activateTab("autoApprove") // Enable always allow execute const executeCheckbox = screen.getByTestId("always-allow-execute-toggle") @@ -334,7 +427,11 @@ describe("SettingsView - Allowed Commands", () => { }) it("removes command from the list", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the autoApprove tab + activateTab("autoApprove") // Enable always allow execute const executeCheckbox = screen.getByTestId("always-allow-execute-toggle") @@ -360,8 +457,71 @@ describe("SettingsView - Allowed Commands", () => { }) }) + describe("SettingsView - Tab Navigation", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("renders with providers tab active by default", () => { + renderSettingsView() + + // Check that the tab list is rendered + const tabList = screen.getByTestId("settings-tab-list") + expect(tabList).toBeInTheDocument() + + // Check that providers content is visible + expect(screen.getByTestId("api-config-management")).toBeInTheDocument() + }) + + it("shows unsaved changes dialog when clicking Done with unsaved changes", () => { + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the notifications tab + activateTab("notifications") + + // Make a change to create unsaved changes + const soundCheckbox = screen.getByTestId("sound-enabled-checkbox") + fireEvent.click(soundCheckbox) + + // Click the Done button + const doneButton = screen.getByText("settings:common.done") + fireEvent.click(doneButton) + + // Check that unsaved changes dialog is shown + expect(screen.getByText("settings:unsavedChangesDialog.title")).toBeInTheDocument() + }) + + it("renders with targetSection prop", () => { + // Render with a specific target section + render( + + + + + , + ) + + // Hydrate initial state + mockPostMessage({}) + + // Verify browser-related content is visible and API config is not + expect(screen.queryByTestId("api-config-management")).not.toBeInTheDocument() + }) + }) +}) + +describe("SettingsView - Duplicate Commands", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + it("prevents duplicate commands", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the autoApprove tab + activateTab("autoApprove") // Enable always allow execute const executeCheckbox = screen.getByTestId("always-allow-execute-toggle") @@ -385,7 +545,11 @@ describe("SettingsView - Allowed Commands", () => { }) it("saves allowed commands when clicking Save", () => { - renderSettingsView() + // Render once and get the activateTab helper + const { activateTab } = renderSettingsView() + + // Activate the autoApprove tab + activateTab("autoApprove") // Enable always allow execute const executeCheckbox = screen.getByTestId("always-allow-execute-toggle") @@ -397,9 +561,9 @@ describe("SettingsView - Allowed Commands", () => { const addButton = screen.getByTestId("add-command-button") fireEvent.click(addButton) - // Click Save - const saveButton = screen.getByTestId("save-button") - fireEvent.click(saveButton) + // Click Save - use getAllByTestId to handle multiple elements + const saveButtons = screen.getAllByTestId("save-button") + fireEvent.click(saveButtons[0]) // Verify VSCode messages were sent expect(vscode.postMessage).toHaveBeenCalledWith( diff --git a/webview-ui/src/components/settings/constants.ts b/webview-ui/src/components/settings/constants.ts index 6144431249a..440d651a1ba 100644 --- a/webview-ui/src/components/settings/constants.ts +++ b/webview-ui/src/components/settings/constants.ts @@ -1,5 +1,5 @@ import { - ApiProvider, + ProviderName, ModelInfo, anthropicModels, bedrockModels, @@ -9,13 +9,15 @@ import { openAiNativeModels, vertexModels, xaiModels, + groqModels, + chutesModels, } from "@roo/shared/api" export { REASONING_MODELS, PROMPT_CACHING_MODELS } from "@roo/shared/api" export { AWS_REGIONS } from "@roo/shared/aws_regions" -export const MODELS_BY_PROVIDER: Partial>> = { +export const MODELS_BY_PROVIDER: Partial>> = { anthropic: anthropicModels, bedrock: bedrockModels, deepseek: deepSeekModels, @@ -24,6 +26,8 @@ export const MODELS_BY_PROVIDER: Partial a.label.localeCompare(b.label)) export const VERTEX_REGIONS = [ diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx new file mode 100644 index 00000000000..4d5957b562a --- /dev/null +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -0,0 +1,84 @@ +import { useCallback, useState } from "react" +import { Checkbox } from "vscrui" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform, noTransform } from "../transforms" + +type AnthropicProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: AnthropicProps) => { + const { t } = useAppTranslation() + + const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.apiKey && ( + + {t("settings:providers.getAnthropicApiKey")} + + )} +
+ { + setAnthropicBaseUrlSelected(checked) + + if (!checked) { + setApiConfigurationField("anthropicBaseUrl", "") + setApiConfigurationField("anthropicUseAuthToken", false) + } + }}> + {t("settings:providers.useCustomBaseUrl")} + + {anthropicBaseUrlSelected && ( + <> + + + {t("settings:providers.anthropicUseAuthToken")} + + + )} +
+ + ) +} diff --git a/webview-ui/src/components/settings/providers/Bedrock.tsx b/webview-ui/src/components/settings/providers/Bedrock.tsx new file mode 100644 index 00000000000..2ddc5797ca2 --- /dev/null +++ b/webview-ui/src/components/settings/providers/Bedrock.tsx @@ -0,0 +1,126 @@ +import { useCallback } from "react" +import { Checkbox } from "vscrui" +import { VSCodeTextField, VSCodeRadio, VSCodeRadioGroup } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings, ModelInfo } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" + +import { AWS_REGIONS } from "../constants" +import { inputEventTransform, noTransform } from "../transforms" + +type BedrockProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void + selectedModelInfo?: ModelInfo +} + +export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedModelInfo }: BedrockProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + (e.target as HTMLInputElement).value === "profile", + )}> + {t("settings:providers.awsCredentials")} + {t("settings:providers.awsProfile")} + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {apiConfiguration?.awsUseProfile ? ( + + + + ) : ( + <> + + + + + + + + + + + )} +
+ + +
+ + {t("settings:providers.awsCrossRegion")} + + {selectedModelInfo?.supportsPromptCache && ( + +
+ {t("settings:providers.enablePromptCaching")} + +
+
+ )} +
+
+ {t("settings:providers.cacheUsageNote")} +
+
+ + ) +} diff --git a/webview-ui/src/components/settings/providers/BedrockCustomArn.tsx b/webview-ui/src/components/settings/providers/BedrockCustomArn.tsx new file mode 100644 index 00000000000..1caf95b8c76 --- /dev/null +++ b/webview-ui/src/components/settings/providers/BedrockCustomArn.tsx @@ -0,0 +1,53 @@ +import { useMemo } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { validateBedrockArn } from "@src/utils/validate" +import { useAppTranslation } from "@src/i18n/TranslationContext" + +type BedrockCustomArnProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const BedrockCustomArn = ({ apiConfiguration, setApiConfigurationField }: BedrockCustomArnProps) => { + const { t } = useAppTranslation() + + const validation = useMemo(() => { + const { awsCustomArn, awsRegion } = apiConfiguration + return awsCustomArn ? validateBedrockArn(awsCustomArn, awsRegion) : { isValid: true, errorMessage: undefined } + }, [apiConfiguration]) + + return ( + <> + setApiConfigurationField("awsCustomArn", (e.target as HTMLInputElement).value)} + placeholder={t("settings:placeholders.customArn")} + className="w-full"> + + +
+ {t("settings:providers.awsCustomArnUse")} +
    +
  • + arn:aws:bedrock:eu-west-1:123456789012:inference-profile/eu.anthropic.claude-3-7-sonnet-20250219-v1:0 +
  • +
  • arn:aws:bedrock:us-west-2:123456789012:provisioned-model/my-provisioned-model
  • +
  • arn:aws:bedrock:us-east-1:123456789012:default-prompt-router/anthropic.claude:1
  • +
+ {t("settings:providers.awsCustomArnDesc")} +
+ {!validation.isValid ? ( +
+ {validation.errorMessage || t("settings:providers.invalidArnFormat")} +
+ ) : ( + validation.errorMessage && ( +
{validation.errorMessage}
+ ) + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/Chutes.tsx b/webview-ui/src/components/settings/providers/Chutes.tsx new file mode 100644 index 00000000000..10ca2c202f5 --- /dev/null +++ b/webview-ui/src/components/settings/providers/Chutes.tsx @@ -0,0 +1,50 @@ +import { useCallback } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" + +type ChutesProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const Chutes = ({ apiConfiguration, setApiConfigurationField }: ChutesProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.chutesApiKey && ( + + {t("settings:providers.getChutesApiKey")} + + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/DeepSeek.tsx b/webview-ui/src/components/settings/providers/DeepSeek.tsx new file mode 100644 index 00000000000..ffb48aec092 --- /dev/null +++ b/webview-ui/src/components/settings/providers/DeepSeek.tsx @@ -0,0 +1,50 @@ +import { useCallback } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" + +type DeepSeekProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const DeepSeek = ({ apiConfiguration, setApiConfigurationField }: DeepSeekProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.deepSeekApiKey && ( + + {t("settings:providers.getDeepSeekApiKey")} + + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/Gemini.tsx b/webview-ui/src/components/settings/providers/Gemini.tsx new file mode 100644 index 00000000000..0a9af31568a --- /dev/null +++ b/webview-ui/src/components/settings/providers/Gemini.tsx @@ -0,0 +1,77 @@ +import { useCallback, useState } from "react" +import { Checkbox } from "vscrui" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" + +type GeminiProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const Gemini = ({ apiConfiguration, setApiConfigurationField }: GeminiProps) => { + const { t } = useAppTranslation() + + const [googleGeminiBaseUrlSelected, setGoogleGeminiBaseUrlSelected] = useState( + !!apiConfiguration?.googleGeminiBaseUrl, + ) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.geminiApiKey && ( + + {t("settings:providers.getGeminiApiKey")} + + )} +
+ { + setGoogleGeminiBaseUrlSelected(checked) + + if (!checked) { + setApiConfigurationField("googleGeminiBaseUrl", "") + } + }}> + {t("settings:providers.useCustomBaseUrl")} + + {googleGeminiBaseUrlSelected && ( + + )} +
+ + ) +} diff --git a/webview-ui/src/components/settings/providers/Glama.tsx b/webview-ui/src/components/settings/providers/Glama.tsx new file mode 100644 index 00000000000..7a7f5dcc495 --- /dev/null +++ b/webview-ui/src/components/settings/providers/Glama.tsx @@ -0,0 +1,63 @@ +import { useCallback } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings, RouterModels, glamaDefaultModelId } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { getGlamaAuthUrl } from "@src/oauth/urls" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" +import { ModelPicker } from "../ModelPicker" + +type GlamaProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void + routerModels?: RouterModels + uriScheme?: string +} + +export const Glama = ({ apiConfiguration, setApiConfigurationField, routerModels, uriScheme }: GlamaProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.glamaApiKey && ( + + {t("settings:providers.getGlamaApiKey")} + + )} + + + ) +} diff --git a/webview-ui/src/components/settings/providers/Groq.tsx b/webview-ui/src/components/settings/providers/Groq.tsx new file mode 100644 index 00000000000..eaf29a45723 --- /dev/null +++ b/webview-ui/src/components/settings/providers/Groq.tsx @@ -0,0 +1,50 @@ +import { useCallback } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" + +type GroqProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const Groq = ({ apiConfiguration, setApiConfigurationField }: GroqProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.groqApiKey && ( + + {t("settings:providers.getGroqApiKey")} + + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/LMStudio.tsx b/webview-ui/src/components/settings/providers/LMStudio.tsx new file mode 100644 index 00000000000..df58f903bbf --- /dev/null +++ b/webview-ui/src/components/settings/providers/LMStudio.tsx @@ -0,0 +1,152 @@ +import { useCallback, useState } from "react" +import { useEvent } from "react-use" +import { Trans } from "react-i18next" +import { Checkbox } from "vscrui" +import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { ExtensionMessage } from "@roo/shared/ExtensionMessage" + +import { inputEventTransform } from "../transforms" + +type LMStudioProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const LMStudio = ({ apiConfiguration, setApiConfigurationField }: LMStudioProps) => { + const { t } = useAppTranslation() + + const [lmStudioModels, setLmStudioModels] = useState([]) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + const onMessage = useCallback((event: MessageEvent) => { + const message: ExtensionMessage = event.data + + switch (message.type) { + case "lmStudioModels": + { + const newModels = message.lmStudioModels ?? [] + setLmStudioModels(newModels) + } + break + } + }, []) + + useEvent("message", onMessage) + + return ( + <> + + + + + + + {lmStudioModels.length > 0 && ( + + {lmStudioModels.map((model) => ( + + {model} + + ))} + + )} + { + setApiConfigurationField("lmStudioSpeculativeDecodingEnabled", checked) + }}> + {t("settings:providers.lmStudio.speculativeDecoding")} + + {apiConfiguration?.lmStudioSpeculativeDecodingEnabled && ( + <> +
+ + + +
+ {t("settings:providers.lmStudio.draftModelDesc")} +
+
+ {lmStudioModels.length > 0 && ( + <> +
{t("settings:providers.lmStudio.selectDraftModel")}
+ + {lmStudioModels.map((model) => ( + + {model} + + ))} + + {lmStudioModels.length === 0 && ( +
+ {t("settings:providers.lmStudio.noModelsFound")} +
+ )} + + )} + + )} +
+ , + b: , + span: ( + + Note: + + ), + }} + /> +
+ + ) +} diff --git a/webview-ui/src/components/settings/providers/LiteLLM.tsx b/webview-ui/src/components/settings/providers/LiteLLM.tsx new file mode 100644 index 00000000000..8f98d97e5dc --- /dev/null +++ b/webview-ui/src/components/settings/providers/LiteLLM.tsx @@ -0,0 +1,65 @@ +import { useCallback } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings, RouterModels, litellmDefaultModelId } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" + +import { inputEventTransform } from "../transforms" +import { ModelPicker } from "../ModelPicker" + +type LiteLLMProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void + routerModels?: RouterModels +} + +export const LiteLLM = ({ apiConfiguration, setApiConfigurationField, routerModels }: LiteLLMProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + + + + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ + + + ) +} diff --git a/webview-ui/src/components/settings/providers/Mistral.tsx b/webview-ui/src/components/settings/providers/Mistral.tsx new file mode 100644 index 00000000000..16691660436 --- /dev/null +++ b/webview-ui/src/components/settings/providers/Mistral.tsx @@ -0,0 +1,67 @@ +import { useCallback } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings, RouterModels, mistralDefaultModelId } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" + +type MistralProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void + routerModels?: RouterModels +} + +export const Mistral = ({ apiConfiguration, setApiConfigurationField }: MistralProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + {t("settings:providers.mistralApiKey")} + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.mistralApiKey && ( + + {t("settings:providers.getMistralApiKey")} + + )} + {(apiConfiguration?.apiModelId?.startsWith("codestral-") || + (!apiConfiguration?.apiModelId && mistralDefaultModelId.startsWith("codestral-"))) && ( + <> + + + +
+ {t("settings:providers.codestralBaseUrlDesc")} +
+ + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/Ollama.tsx b/webview-ui/src/components/settings/providers/Ollama.tsx new file mode 100644 index 00000000000..bda3c441cc0 --- /dev/null +++ b/webview-ui/src/components/settings/providers/Ollama.tsx @@ -0,0 +1,86 @@ +import { useState, useCallback } from "react" +import { useEvent } from "react-use" +import { VSCodeTextField, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" +import { ExtensionMessage } from "@roo/shared/ExtensionMessage" + +import { useAppTranslation } from "@src/i18n/TranslationContext" + +import { inputEventTransform } from "../transforms" + +type OllamaProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const Ollama = ({ apiConfiguration, setApiConfigurationField }: OllamaProps) => { + const { t } = useAppTranslation() + + const [ollamaModels, setOllamaModels] = useState([]) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + const onMessage = useCallback((event: MessageEvent) => { + const message: ExtensionMessage = event.data + + switch (message.type) { + case "ollamaModels": + { + const newModels = message.ollamaModels ?? [] + setOllamaModels(newModels) + } + break + } + }, []) + + useEvent("message", onMessage) + + return ( + <> + + + + + + + {ollamaModels.length > 0 && ( + + {ollamaModels.map((model) => ( + + {model} + + ))} + + )} +
+ {t("settings:providers.ollama.description")} + {t("settings:providers.ollama.warning")} +
+ + ) +} diff --git a/webview-ui/src/components/settings/providers/OpenAI.tsx b/webview-ui/src/components/settings/providers/OpenAI.tsx new file mode 100644 index 00000000000..771d77ef6ab --- /dev/null +++ b/webview-ui/src/components/settings/providers/OpenAI.tsx @@ -0,0 +1,77 @@ +import { useCallback, useState } from "react" +import { Checkbox } from "vscrui" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" + +type OpenAIProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const OpenAI = ({ apiConfiguration, setApiConfigurationField }: OpenAIProps) => { + const { t } = useAppTranslation() + + const [openAiNativeBaseUrlSelected, setOpenAiNativeBaseUrlSelected] = useState( + !!apiConfiguration?.openAiNativeBaseUrl, + ) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + { + setOpenAiNativeBaseUrlSelected(checked) + + if (!checked) { + setApiConfigurationField("openAiNativeBaseUrl", "") + } + }}> + {t("settings:providers.useCustomBaseUrl")} + + {openAiNativeBaseUrlSelected && ( + <> + + + )} + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.openAiNativeApiKey && ( + + {t("settings:providers.getOpenAiApiKey")} + + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx new file mode 100644 index 00000000000..774a5681ce2 --- /dev/null +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -0,0 +1,591 @@ +import { useState, useCallback, useEffect } from "react" +import { useEvent } from "react-use" +import { Checkbox } from "vscrui" +import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { convertHeadersToObject } from "../utils/headers" + +import { ModelInfo, ReasoningEffort as ReasoningEffortType } from "@roo/schemas" +import { ProviderSettings, azureOpenAiDefaultApiVersion, openAiModelInfoSaneDefaults } from "@roo/shared/api" +import { ExtensionMessage } from "@roo/shared/ExtensionMessage" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { Button } from "@src/components/ui" + +import { inputEventTransform, noTransform } from "../transforms" +import { ModelPicker } from "../ModelPicker" +import { R1FormatSetting } from "../R1FormatSetting" +import { ReasoningEffort } from "../ReasoningEffort" + +type OpenAICompatibleProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const OpenAICompatible = ({ apiConfiguration, setApiConfigurationField }: OpenAICompatibleProps) => { + const { t } = useAppTranslation() + + const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion) + const [openAiLegacyFormatSelected, setOpenAiLegacyFormatSelected] = useState(!!apiConfiguration?.openAiLegacyFormat) + + const [openAiModels, setOpenAiModels] = useState | null>(null) + + const [customHeaders, setCustomHeaders] = useState<[string, string][]>(() => { + const headers = apiConfiguration?.openAiHeaders || {} + return Object.entries(headers) + }) + + const handleAddCustomHeader = useCallback(() => { + // Only update the local state to show the new row in the UI. + setCustomHeaders((prev) => [...prev, ["", ""]]) + // Do not update the main configuration yet, wait for user input. + }, []) + + const handleUpdateHeaderKey = useCallback((index: number, newKey: string) => { + setCustomHeaders((prev) => { + const updated = [...prev] + + if (updated[index]) { + updated[index] = [newKey, updated[index][1]] + } + + return updated + }) + }, []) + + const handleUpdateHeaderValue = useCallback((index: number, newValue: string) => { + setCustomHeaders((prev) => { + const updated = [...prev] + + if (updated[index]) { + updated[index] = [updated[index][0], newValue] + } + + return updated + }) + }, []) + + const handleRemoveCustomHeader = useCallback((index: number) => { + setCustomHeaders((prev) => prev.filter((_, i) => i !== index)) + }, []) + + // Helper to convert array of tuples to object + + // Add effect to update the parent component's state when local headers change + useEffect(() => { + const timer = setTimeout(() => { + const headerObject = convertHeadersToObject(customHeaders) + setApiConfigurationField("openAiHeaders", headerObject) + }, 300) + + return () => clearTimeout(timer) + }, [customHeaders, setApiConfigurationField]) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + const onMessage = useCallback((event: MessageEvent) => { + const message: ExtensionMessage = event.data + + switch (message.type) { + case "openAiModels": { + const updatedModels = message.openAiModels ?? [] + setOpenAiModels(Object.fromEntries(updatedModels.map((item) => [item, openAiModelInfoSaneDefaults]))) + break + } + } + }, []) + + useEvent("message", onMessage) + + return ( + <> + + + + + + + + +
+ { + setOpenAiLegacyFormatSelected(checked) + setApiConfigurationField("openAiLegacyFormat", checked) + }}> + {t("settings:providers.useLegacyFormat")} + +
+ + {t("settings:modelInfo.enableStreaming")} + + + {t("settings:modelInfo.useAzure")} + +
+ { + setAzureApiVersionSelected(checked) + + if (!checked) { + setApiConfigurationField("azureApiVersion", "") + } + }}> + {t("settings:modelInfo.azureApiVersion")} + + {azureApiVersionSelected && ( + + )} +
+ + {/* Custom Headers UI */} +
+
+ + + + +
+ {!customHeaders.length ? ( +
+ {t("settings:providers.noCustomHeaders")} +
+ ) : ( + customHeaders.map(([key, value], index) => ( +
+ handleUpdateHeaderKey(index, e.target.value)} + /> + handleUpdateHeaderValue(index, e.target.value)} + /> + handleRemoveCustomHeader(index)}> + + +
+ )) + )} +
+ +
+ { + setApiConfigurationField("enableReasoningEffort", checked) + + if (!checked) { + const { reasoningEffort: _, ...openAiCustomModelInfo } = + apiConfiguration.openAiCustomModelInfo || openAiModelInfoSaneDefaults + + setApiConfigurationField("openAiCustomModelInfo", openAiCustomModelInfo) + } + }}> + {t("settings:providers.setReasoningLevel")} + + {!!apiConfiguration.enableReasoningEffort && ( + { + if (field === "reasoningEffort") { + const openAiCustomModelInfo = + apiConfiguration.openAiCustomModelInfo || openAiModelInfoSaneDefaults + + setApiConfigurationField("openAiCustomModelInfo", { + ...openAiCustomModelInfo, + reasoningEffort: value as ReasoningEffortType, + }) + } + }} + /> + )} +
+
+
+ {t("settings:providers.customModel.capabilities")} +
+ +
+ { + const value = apiConfiguration?.openAiCustomModelInfo?.maxTokens + + if (!value) { + return "var(--vscode-input-border)" + } + + return value > 0 ? "var(--vscode-charts-green)" : "var(--vscode-errorForeground)" + })(), + }} + title={t("settings:providers.customModel.maxTokens.description")} + onInput={handleInputChange("openAiCustomModelInfo", (e) => { + const value = parseInt((e.target as HTMLInputElement).value) + + return { + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + maxTokens: isNaN(value) ? undefined : value, + } + })} + placeholder={t("settings:placeholders.numbers.maxTokens")} + className="w-full"> + + +
+ {t("settings:providers.customModel.maxTokens.description")} +
+
+ +
+ { + const value = apiConfiguration?.openAiCustomModelInfo?.contextWindow + + if (!value) { + return "var(--vscode-input-border)" + } + + return value > 0 ? "var(--vscode-charts-green)" : "var(--vscode-errorForeground)" + })(), + }} + title={t("settings:providers.customModel.contextWindow.description")} + onInput={handleInputChange("openAiCustomModelInfo", (e) => { + const value = (e.target as HTMLInputElement).value + const parsed = parseInt(value) + + return { + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + contextWindow: isNaN(parsed) ? openAiModelInfoSaneDefaults.contextWindow : parsed, + } + })} + placeholder={t("settings:placeholders.numbers.contextWindow")} + className="w-full"> + + +
+ {t("settings:providers.customModel.contextWindow.description")} +
+
+ +
+
+ { + return { + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + supportsImages: checked, + } + })}> + + {t("settings:providers.customModel.imageSupport.label")} + + + +
+
+ {t("settings:providers.customModel.imageSupport.description")} +
+
+ +
+
+ { + return { + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + supportsComputerUse: checked, + } + })}> + {t("settings:providers.customModel.computerUse.label")} + + +
+
+ {t("settings:providers.customModel.computerUse.description")} +
+
+ +
+
+ { + return { + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + supportsPromptCache: checked, + } + })}> + {t("settings:providers.customModel.promptCache.label")} + + +
+
+ {t("settings:providers.customModel.promptCache.description")} +
+
+ +
+ { + const value = apiConfiguration?.openAiCustomModelInfo?.inputPrice + + if (!value && value !== 0) { + return "var(--vscode-input-border)" + } + + return value >= 0 ? "var(--vscode-charts-green)" : "var(--vscode-errorForeground)" + })(), + }} + onChange={handleInputChange("openAiCustomModelInfo", (e) => { + const value = (e.target as HTMLInputElement).value + const parsed = parseFloat(value) + + return { + ...(apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults), + inputPrice: isNaN(parsed) ? openAiModelInfoSaneDefaults.inputPrice : parsed, + } + })} + placeholder={t("settings:placeholders.numbers.inputPrice")} + className="w-full"> +
+ + +
+
+
+ +
+ { + const value = apiConfiguration?.openAiCustomModelInfo?.outputPrice + + if (!value && value !== 0) { + return "var(--vscode-input-border)" + } + + return value >= 0 ? "var(--vscode-charts-green)" : "var(--vscode-errorForeground)" + })(), + }} + onChange={handleInputChange("openAiCustomModelInfo", (e) => { + const value = (e.target as HTMLInputElement).value + const parsed = parseFloat(value) + + return { + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + outputPrice: isNaN(parsed) ? openAiModelInfoSaneDefaults.outputPrice : parsed, + } + })} + placeholder={t("settings:placeholders.numbers.outputPrice")} + className="w-full"> +
+ + +
+
+
+ + {apiConfiguration?.openAiCustomModelInfo?.supportsPromptCache && ( + <> +
+ { + const value = apiConfiguration?.openAiCustomModelInfo?.cacheReadsPrice + + if (!value && value !== 0) { + return "var(--vscode-input-border)" + } + + return value >= 0 + ? "var(--vscode-charts-green)" + : "var(--vscode-errorForeground)" + })(), + }} + onChange={handleInputChange("openAiCustomModelInfo", (e) => { + const value = (e.target as HTMLInputElement).value + const parsed = parseFloat(value) + + return { + ...(apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults), + cacheReadsPrice: isNaN(parsed) ? 0 : parsed, + } + })} + placeholder={t("settings:placeholders.numbers.inputPrice")} + className="w-full"> +
+ + {t("settings:providers.customModel.pricing.cacheReads.label")} + + +
+
+
+
+ { + const value = apiConfiguration?.openAiCustomModelInfo?.cacheWritesPrice + + if (!value && value !== 0) { + return "var(--vscode-input-border)" + } + + return value >= 0 + ? "var(--vscode-charts-green)" + : "var(--vscode-errorForeground)" + })(), + }} + onChange={handleInputChange("openAiCustomModelInfo", (e) => { + const value = (e.target as HTMLInputElement).value + const parsed = parseFloat(value) + + return { + ...(apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults), + cacheWritesPrice: isNaN(parsed) ? 0 : parsed, + } + })} + placeholder={t("settings:placeholders.numbers.cacheWritePrice")} + className="w-full"> +
+ + +
+
+
+ + )} + + +
+ + ) +} diff --git a/webview-ui/src/components/settings/providers/OpenRouter.tsx b/webview-ui/src/components/settings/providers/OpenRouter.tsx new file mode 100644 index 00000000000..7bb7c30095d --- /dev/null +++ b/webview-ui/src/components/settings/providers/OpenRouter.tsx @@ -0,0 +1,172 @@ +import { useCallback, useState } from "react" +import { Trans } from "react-i18next" +import { Checkbox } from "vscrui" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { ExternalLinkIcon } from "@radix-ui/react-icons" + +import { ProviderSettings, RouterModels, openRouterDefaultModelId } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { getOpenRouterAuthUrl } from "@src/oauth/urls" +import { + useOpenRouterModelProviders, + OPENROUTER_DEFAULT_PROVIDER_NAME, +} from "@src/components/ui/hooks/useOpenRouterModelProviders" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" + +import { inputEventTransform, noTransform } from "../transforms" + +import { ModelPicker } from "../ModelPicker" +import { OpenRouterBalanceDisplay } from "./OpenRouterBalanceDisplay" + +type OpenRouterProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void + routerModels?: RouterModels + selectedModelId: string + uriScheme: string | undefined + fromWelcomeView?: boolean +} + +export const OpenRouter = ({ + apiConfiguration, + setApiConfigurationField, + routerModels, + selectedModelId, + uriScheme, + fromWelcomeView, +}: OpenRouterProps) => { + const { t } = useAppTranslation() + + const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, { + enabled: + !!apiConfiguration?.openRouterModelId && + routerModels?.openrouter && + Object.keys(routerModels.openrouter).length > 1 && + apiConfiguration.openRouterModelId in routerModels.openrouter, + }) + + return ( + <> + +
+ + {apiConfiguration?.openRouterApiKey && ( + + )} +
+
+
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.openRouterApiKey && ( + + {t("settings:providers.getOpenRouterApiKey")} + + )} + {!fromWelcomeView && ( + <> +
+ { + setOpenRouterBaseUrlSelected(checked) + + if (!checked) { + setApiConfigurationField("openRouterBaseUrl", "") + } + }}> + {t("settings:providers.useCustomBaseUrl")} + + {openRouterBaseUrlSelected && ( + + )} +
+ + , + }} + /> + + + )} + + {openRouterModelProviders && Object.keys(openRouterModelProviders).length > 0 && ( +
+
+ + + + +
+ +
+ {t("settings:providers.openRouter.providerRouting.description")}{" "} + + {t("settings:providers.openRouter.providerRouting.learnMore")}. + +
+
+ )} + + ) +} diff --git a/webview-ui/src/components/settings/OpenRouterBalanceDisplay.tsx b/webview-ui/src/components/settings/providers/OpenRouterBalanceDisplay.tsx similarity index 100% rename from webview-ui/src/components/settings/OpenRouterBalanceDisplay.tsx rename to webview-ui/src/components/settings/providers/OpenRouterBalanceDisplay.tsx diff --git a/webview-ui/src/components/settings/providers/Requesty.tsx b/webview-ui/src/components/settings/providers/Requesty.tsx new file mode 100644 index 00000000000..9c6ba2844eb --- /dev/null +++ b/webview-ui/src/components/settings/providers/Requesty.tsx @@ -0,0 +1,97 @@ +import { useCallback, useState } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings, RouterModels, requestyDefaultModelId } from "@roo/shared/api" + +import { vscode } from "@src/utils/vscode" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" +import { Button } from "@src/components/ui" + +import { inputEventTransform } from "../transforms" +import { ModelPicker } from "../ModelPicker" +import { RequestyBalanceDisplay } from "./RequestyBalanceDisplay" + +type RequestyProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void + routerModels?: RouterModels + refetchRouterModels: () => void +} + +export const Requesty = ({ + apiConfiguration, + setApiConfigurationField, + routerModels, + refetchRouterModels, +}: RequestyProps) => { + const { t } = useAppTranslation() + + const [didRefetch, setDidRefetch] = useState() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + +
+ + {apiConfiguration?.requestyApiKey && ( + + )} +
+
+
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.requestyApiKey && ( + + {t("settings:providers.getRequestyApiKey")} + + )} + + {didRefetch && ( +
+ {t("settings:providers.refreshModels.hint")} +
+ )} + + + ) +} diff --git a/webview-ui/src/components/settings/RequestyBalanceDisplay.tsx b/webview-ui/src/components/settings/providers/RequestyBalanceDisplay.tsx similarity index 100% rename from webview-ui/src/components/settings/RequestyBalanceDisplay.tsx rename to webview-ui/src/components/settings/providers/RequestyBalanceDisplay.tsx diff --git a/webview-ui/src/components/settings/providers/Unbound.tsx b/webview-ui/src/components/settings/providers/Unbound.tsx new file mode 100644 index 00000000000..77b24bb7cc5 --- /dev/null +++ b/webview-ui/src/components/settings/providers/Unbound.tsx @@ -0,0 +1,61 @@ +import { useCallback } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings, RouterModels, unboundDefaultModelId } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" +import { ModelPicker } from "../ModelPicker" + +type UnboundProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void + routerModels?: RouterModels +} + +export const Unbound = ({ apiConfiguration, setApiConfigurationField, routerModels }: UnboundProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.unboundApiKey && ( + + {t("settings:providers.getUnboundApiKey")} + + )} + + + ) +} diff --git a/webview-ui/src/components/settings/providers/VSCodeLM.tsx b/webview-ui/src/components/settings/providers/VSCodeLM.tsx new file mode 100644 index 00000000000..7f9426e5ce0 --- /dev/null +++ b/webview-ui/src/components/settings/providers/VSCodeLM.tsx @@ -0,0 +1,86 @@ +import { useState, useCallback } from "react" +import { useEvent } from "react-use" +import { LanguageModelChatSelector } from "vscode" + +import { ProviderSettings } from "@roo/shared/api" +import { ExtensionMessage } from "@roo/shared/ExtensionMessage" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" + +import { inputEventTransform } from "../transforms" + +type VSCodeLMProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const VSCodeLM = ({ apiConfiguration, setApiConfigurationField }: VSCodeLMProps) => { + const { t } = useAppTranslation() + + const [vsCodeLmModels, setVsCodeLmModels] = useState([]) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + const onMessage = useCallback((event: MessageEvent) => { + const message: ExtensionMessage = event.data + + switch (message.type) { + case "vsCodeLmModels": + { + const newModels = message.vsCodeLmModels ?? [] + setVsCodeLmModels(newModels) + } + break + } + }, []) + + useEvent("message", onMessage) + + return ( + <> +
+ + {vsCodeLmModels.length > 0 ? ( + + ) : ( +
+ {t("settings:providers.vscodeLmDescription")} +
+ )} +
+
{t("settings:providers.vscodeLmWarning")}
+ + ) +} diff --git a/webview-ui/src/components/settings/providers/Vertex.tsx b/webview-ui/src/components/settings/providers/Vertex.tsx new file mode 100644 index 00000000000..b43c7938166 --- /dev/null +++ b/webview-ui/src/components/settings/providers/Vertex.tsx @@ -0,0 +1,97 @@ +import { useCallback } from "react" +import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" + +import { inputEventTransform } from "../transforms" +import { VERTEX_REGIONS } from "../constants" + +type VertexProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const Vertex = ({ apiConfiguration, setApiConfigurationField }: VertexProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> +
+
{t("settings:providers.googleCloudSetup.title")}
+
+ + {t("settings:providers.googleCloudSetup.step1")} + +
+
+ + {t("settings:providers.googleCloudSetup.step2")} + +
+
+ + {t("settings:providers.googleCloudSetup.step3")} + +
+
+ + + + + + + + + +
+ + +
+ + ) +} diff --git a/webview-ui/src/components/settings/providers/XAI.tsx b/webview-ui/src/components/settings/providers/XAI.tsx new file mode 100644 index 00000000000..c42bde1b1e1 --- /dev/null +++ b/webview-ui/src/components/settings/providers/XAI.tsx @@ -0,0 +1,50 @@ +import { useCallback } from "react" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import { ProviderSettings } from "@roo/shared/api" + +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" + +import { inputEventTransform } from "../transforms" + +type XAIProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const XAI = ({ apiConfiguration, setApiConfigurationField }: XAIProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.xaiApiKey && ( + + {t("settings:providers.getXaiApiKey")} + + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/index.ts b/webview-ui/src/components/settings/providers/index.ts new file mode 100644 index 00000000000..b244fb515c4 --- /dev/null +++ b/webview-ui/src/components/settings/providers/index.ts @@ -0,0 +1,19 @@ +export { Anthropic } from "./Anthropic" +export { Bedrock } from "./Bedrock" +export { Chutes } from "./Chutes" +export { DeepSeek } from "./DeepSeek" +export { Gemini } from "./Gemini" +export { Glama } from "./Glama" +export { Groq } from "./Groq" +export { LMStudio } from "./LMStudio" +export { Mistral } from "./Mistral" +export { Ollama } from "./Ollama" +export { OpenAI } from "./OpenAI" +export { OpenAICompatible } from "./OpenAICompatible" +export { OpenRouter } from "./OpenRouter" +export { Requesty } from "./Requesty" +export { Unbound } from "./Unbound" +export { Vertex } from "./Vertex" +export { VSCodeLM } from "./VSCodeLM" +export { XAI } from "./XAI" +export { LiteLLM } from "./LiteLLM" diff --git a/webview-ui/src/components/settings/styles.ts b/webview-ui/src/components/settings/styles.ts index ab403ab1b7c..7eac289bb0f 100644 --- a/webview-ui/src/components/settings/styles.ts +++ b/webview-ui/src/components/settings/styles.ts @@ -1,37 +1,6 @@ import styled from "styled-components" -export const DropdownWrapper = styled.div` - position: relative; - width: 100%; -` - -export const DropdownList = styled.div<{ $zIndex: number }>` - position: absolute; - top: calc(100% - 3px); - left: 0; - width: calc(100% - 2px); - max-height: 200px; - overflow-y: auto; - background-color: var(--vscode-dropdown-background); - border: 1px solid var(--vscode-list-activeSelectionBackground); - z-index: ${({ $zIndex }) => $zIndex}; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; -` - -export const DropdownItem = styled.div<{ $selected: boolean }>` - padding: 5px 10px; - cursor: pointer; - word-break: break-all; - white-space: normal; - - background-color: ${({ $selected }) => ($selected ? "var(--vscode-list-activeSelectionBackground)" : "inherit")}; - - &:hover { - background-color: var(--vscode-list-activeSelectionBackground); - } -` - +// Keep StyledMarkdown as it's used by ModelDescriptionMarkdown.tsx export const StyledMarkdown = styled.div` font-family: var(--vscode-font-family), diff --git a/webview-ui/src/components/settings/transforms.ts b/webview-ui/src/components/settings/transforms.ts new file mode 100644 index 00000000000..22befdaf4ed --- /dev/null +++ b/webview-ui/src/components/settings/transforms.ts @@ -0,0 +1,3 @@ +export const noTransform = (value: T) => value + +export const inputEventTransform = (event: E) => (event as { target: HTMLInputElement })?.target?.value as any diff --git a/webview-ui/src/components/settings/utils/__tests__/headers.test.ts b/webview-ui/src/components/settings/utils/__tests__/headers.test.ts new file mode 100644 index 00000000000..e43f3b9e65d --- /dev/null +++ b/webview-ui/src/components/settings/utils/__tests__/headers.test.ts @@ -0,0 +1,122 @@ +import { convertHeadersToObject } from "../headers" + +describe("convertHeadersToObject", () => { + it("should convert headers array to object", () => { + const headers: [string, string][] = [ + ["Content-Type", "application/json"], + ["Authorization", "Bearer token123"], + ] + + const result = convertHeadersToObject(headers) + + expect(result).toEqual({ + "Content-Type": "application/json", + Authorization: "Bearer token123", + }) + }) + + it("should trim whitespace from keys and values", () => { + const headers: [string, string][] = [ + [" Content-Type ", " application/json "], + [" Authorization ", " Bearer token123 "], + ] + + const result = convertHeadersToObject(headers) + + expect(result).toEqual({ + "Content-Type": "application/json", + Authorization: "Bearer token123", + }) + }) + + it("should handle empty headers array", () => { + const headers: [string, string][] = [] + + const result = convertHeadersToObject(headers) + + expect(result).toEqual({}) + }) + + it("should skip headers with empty keys", () => { + const headers: [string, string][] = [ + ["Content-Type", "application/json"], + ["", "This value should be skipped"], + [" ", "This value should also be skipped"], + ["Authorization", "Bearer token123"], + ] + + const result = convertHeadersToObject(headers) + + expect(result).toEqual({ + "Content-Type": "application/json", + Authorization: "Bearer token123", + }) + + // Specifically verify empty keys are not present + expect(result[""]).toBeUndefined() + expect(result[" "]).toBeUndefined() + }) + + it("should use last occurrence when handling duplicate keys", () => { + const headers: [string, string][] = [ + ["Content-Type", "application/json"], + ["Authorization", "Bearer token123"], + ["Content-Type", "text/plain"], // Duplicate key - should override previous value + ["Content-Type", "application/xml"], // Another duplicate - should override again + ] + + const result = convertHeadersToObject(headers) + + // Verify the last value for "Content-Type" is used + expect(result["Content-Type"]).toBe("application/xml") + expect(result).toEqual({ + "Content-Type": "application/xml", + Authorization: "Bearer token123", + }) + }) + + it("should preserve case sensitivity while trimming keys", () => { + const headers: [string, string][] = [ + [" Content-Type", "application/json"], + ["content-type ", "text/plain"], // Different casing (lowercase) with spacing + ] + + const result = convertHeadersToObject(headers) + + // Keys should be trimmed but case sensitivity preserved + // JavaScript object keys are case-sensitive + expect(Object.keys(result)).toHaveLength(2) + expect(result["Content-Type"]).toBe("application/json") + expect(result["content-type"]).toBe("text/plain") + }) + + it("should handle empty values", () => { + const headers: [string, string][] = [ + ["Empty-Value", ""], + ["Whitespace-Value", " "], + ] + + const result = convertHeadersToObject(headers) + + // Empty values should be included but trimmed + expect(result["Empty-Value"]).toBe("") + expect(result["Whitespace-Value"]).toBe("") + }) + + it("should handle complex duplicate key scenarios with mixed casing and spacing", () => { + const headers: [string, string][] = [ + ["content-type", "application/json"], // Original entry + [" Content-Type ", "text/html"], // Different case with spacing + ["content-type", "application/xml"], // Same case as first, should override it + ["Content-Type", "text/plain"], // Same case as second, should override it + ] + + const result = convertHeadersToObject(headers) + + // JavaScript object keys are case-sensitive + // We should have two keys with different cases, each with the last value + expect(Object.keys(result).sort()).toEqual(["Content-Type", "content-type"].sort()) + expect(result["content-type"]).toBe("application/xml") + expect(result["Content-Type"]).toBe("text/plain") + }) +}) diff --git a/webview-ui/src/components/settings/utils/headers.ts b/webview-ui/src/components/settings/utils/headers.ts new file mode 100644 index 00000000000..4b7f90d2e26 --- /dev/null +++ b/webview-ui/src/components/settings/utils/headers.ts @@ -0,0 +1,25 @@ +/** + * Converts an array of header key-value pairs to a Record object. + * + * @param headers Array of [key, value] tuples representing HTTP headers + * @returns Record with trimmed keys and values + */ +export const convertHeadersToObject = (headers: [string, string][]): Record => { + const result: Record = {} + + // Process each header tuple. + for (const [key, value] of headers) { + const trimmedKey = key.trim() + + // Skip empty keys. + if (!trimmedKey) { + continue + } + + // For duplicates, the last one in the array wins. + // This matches how HTTP headers work in general. + result[trimmedKey] = value.trim() + } + + return result +} diff --git a/webview-ui/src/components/ui/alert-dialog.tsx b/webview-ui/src/components/ui/alert-dialog.tsx index 6782b399c12..b1178683214 100644 --- a/webview-ui/src/components/ui/alert-dialog.tsx +++ b/webview-ui/src/components/ui/alert-dialog.tsx @@ -90,7 +90,7 @@ function AlertDialogAction({ className, ...props }: React.ComponentProps @@ -78,7 +78,7 @@ function DialogTitle({ className, ...props }: React.ComponentProps ) @@ -88,7 +88,7 @@ function DialogDescription({ className, ...props }: React.ComponentProps ) diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 83e23a2a4c3..0669742eab7 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -1,7 +1,8 @@ import { - ApiConfiguration, - RouterModels, - ModelInfo, + type ProviderName, + type ProviderSettings, + type RouterModels, + type ModelInfo, anthropicDefaultModelId, anthropicModels, bedrockDefaultModelId, @@ -19,111 +20,191 @@ import { vertexModels, xaiDefaultModelId, xaiModels, + groqModels, + groqDefaultModelId, + chutesModels, + chutesDefaultModelId, vscodeLlmModels, vscodeLlmDefaultModelId, openRouterDefaultModelId, requestyDefaultModelId, glamaDefaultModelId, unboundDefaultModelId, + litellmDefaultModelId, } from "@roo/shared/api" import { useRouterModels } from "./useRouterModels" import { pearaiDefaultModelId, pearaiDefaultModelInfo } from "@roo/shared/pearaiApi" +import { useOpenRouterModelProviders } from "./useOpenRouterModelProviders" -export const useSelectedModel = (apiConfiguration?: ApiConfiguration) => { - const { data: routerModels, isLoading, isError } = useRouterModels() +export const useSelectedModel = (apiConfiguration?: ProviderSettings) => { const provider = apiConfiguration?.apiProvider || "anthropic" - const id = apiConfiguration ? getSelectedModelId({ provider, apiConfiguration }) : anthropicDefaultModelId - const info = routerModels ? getSelectedModelInfo({ provider, id, apiConfiguration, routerModels }) : undefined - return { provider, id, info, isLoading, isError } -} + const openRouterModelId = provider === "openrouter" ? apiConfiguration?.openRouterModelId : undefined -function getSelectedModelId({ provider, apiConfiguration }: { provider: string; apiConfiguration: ApiConfiguration }) { - switch (provider) { - case "openrouter": - return apiConfiguration.openRouterModelId ?? openRouterDefaultModelId - case "requesty": - return apiConfiguration.requestyModelId ?? requestyDefaultModelId - case "glama": - return apiConfiguration.glamaModelId ?? glamaDefaultModelId - case "unbound": - return apiConfiguration.unboundModelId ?? unboundDefaultModelId - case "openai": - return apiConfiguration.openAiModelId || "" - case "ollama": - return apiConfiguration.ollamaModelId || "" - case "lmstudio": - return apiConfiguration.lmStudioModelId || "" - case "pearai": - return apiConfiguration.apiModelId || pearaiDefaultModelId - case "vscode-lm": - return apiConfiguration?.vsCodeLmModelSelector - ? `${apiConfiguration.vsCodeLmModelSelector.vendor}/${apiConfiguration.vsCodeLmModelSelector.family}` - : "" - default: - return apiConfiguration.apiModelId ?? anthropicDefaultModelId + const routerModels = useRouterModels() + const openRouterModelProviders = useOpenRouterModelProviders(openRouterModelId) + + const { id, info } = + apiConfiguration && + typeof routerModels.data !== "undefined" && + typeof openRouterModelProviders.data !== "undefined" + ? getSelectedModel({ + provider, + apiConfiguration, + routerModels: routerModels.data, + openRouterModelProviders: openRouterModelProviders.data, + }) + : { id: anthropicDefaultModelId, info: undefined } + + return { + provider, + id, + info, + isLoading: routerModels.isLoading || openRouterModelProviders.isLoading, + isError: routerModels.isError || openRouterModelProviders.isError, } } -function getSelectedModelInfo({ +function getSelectedModel({ provider, - id, apiConfiguration, routerModels, + openRouterModelProviders, }: { - provider: string - id: string - apiConfiguration?: ApiConfiguration + provider: ProviderName + apiConfiguration: ProviderSettings routerModels: RouterModels -}): ModelInfo { + openRouterModelProviders: Record +}): { id: string; info: ModelInfo } { switch (provider) { - case "openrouter": - return routerModels.openrouter[id] ?? routerModels.openrouter[openRouterDefaultModelId] - case "requesty": - return routerModels.requesty[id] ?? routerModels.requesty[requestyDefaultModelId] - case "glama": - return routerModels.glama[id] ?? routerModels.glama[glamaDefaultModelId] - case "unbound": - return routerModels.unbound[id] ?? routerModels.unbound[unboundDefaultModelId] - case "xai": - return xaiModels[id as keyof typeof xaiModels] ?? xaiModels[xaiDefaultModelId] - case "bedrock": - // Special case for custom ARN. - if (id === "custom-arn") { - return { maxTokens: 5000, contextWindow: 128_000, supportsPromptCache: false, supportsImages: true } + case "openrouter": { + const id = apiConfiguration.openRouterModelId ?? openRouterDefaultModelId + let info = routerModels.openrouter[id] + const specificProvider = apiConfiguration.openRouterSpecificProvider + + if (specificProvider && openRouterModelProviders[specificProvider]) { + info = openRouterModelProviders[specificProvider] } - return bedrockModels[id as keyof typeof bedrockModels] ?? bedrockModels[bedrockDefaultModelId] - case "vertex": - return vertexModels[id as keyof typeof vertexModels] ?? vertexModels[vertexDefaultModelId] - case "gemini": - return geminiModels[id as keyof typeof geminiModels] ?? geminiModels[geminiDefaultModelId] - case "deepseek": - return deepSeekModels[id as keyof typeof deepSeekModels] ?? deepSeekModels[deepSeekDefaultModelId] - case "openai-native": - return ( - openAiNativeModels[id as keyof typeof openAiNativeModels] ?? - openAiNativeModels[openAiNativeDefaultModelId] - ) - case "mistral": - return mistralModels[id as keyof typeof mistralModels] ?? mistralModels[mistralDefaultModelId] - case "openai": - return apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults - case "ollama": - return openAiModelInfoSaneDefaults - case "lmstudio": - return openAiModelInfoSaneDefaults - case "vscode-lm": - const modelFamily = apiConfiguration?.vsCodeLmModelSelector?.family ?? vscodeLlmDefaultModelId + return info + ? { id, info } + : { id: openRouterDefaultModelId, info: routerModels.openrouter[openRouterDefaultModelId] } + } + case "requesty": { + const id = apiConfiguration.requestyModelId ?? requestyDefaultModelId + const info = routerModels.requesty[id] + return info + ? { id, info } + : { id: requestyDefaultModelId, info: routerModels.requesty[requestyDefaultModelId] } + } + case "glama": { + const id = apiConfiguration.glamaModelId ?? glamaDefaultModelId + const info = routerModels.glama[id] + return info ? { id, info } : { id: glamaDefaultModelId, info: routerModels.glama[glamaDefaultModelId] } + } + case "unbound": { + const id = apiConfiguration.unboundModelId ?? unboundDefaultModelId + const info = routerModels.unbound[id] + return info + ? { id, info } + : { id: unboundDefaultModelId, info: routerModels.unbound[unboundDefaultModelId] } + } + case "litellm": { + const id = apiConfiguration.litellmModelId ?? litellmDefaultModelId + const info = routerModels.litellm[id] + return info + ? { id, info } + : { id: litellmDefaultModelId, info: routerModels.litellm[litellmDefaultModelId] } + } + case "xai": { + const id = apiConfiguration.apiModelId ?? xaiDefaultModelId + const info = xaiModels[id as keyof typeof xaiModels] + return info ? { id, info } : { id: xaiDefaultModelId, info: xaiModels[xaiDefaultModelId] } + } + case "groq": { + const id = apiConfiguration.apiModelId ?? groqDefaultModelId + const info = groqModels[id as keyof typeof groqModels] + return info ? { id, info } : { id: groqDefaultModelId, info: groqModels[groqDefaultModelId] } + } + case "chutes": { + const id = apiConfiguration.apiModelId ?? chutesDefaultModelId + const info = chutesModels[id as keyof typeof chutesModels] + return info ? { id, info } : { id: chutesDefaultModelId, info: chutesModels[chutesDefaultModelId] } + } + case "bedrock": { + const id = apiConfiguration.apiModelId ?? bedrockDefaultModelId + const info = bedrockModels[id as keyof typeof bedrockModels] - return { - ...openAiModelInfoSaneDefaults, - ...vscodeLlmModels[modelFamily as keyof typeof vscodeLlmModels], - supportsImages: false, // VSCode LM API currently doesn't support images. + // Special case for custom ARN. + if (id === "custom-arn") { + return { + id, + info: { maxTokens: 5000, contextWindow: 128_000, supportsPromptCache: false, supportsImages: true }, + } } + + return info ? { id, info } : { id: bedrockDefaultModelId, info: bedrockModels[bedrockDefaultModelId] } + } + case "vertex": { + const id = apiConfiguration.apiModelId ?? vertexDefaultModelId + const info = vertexModels[id as keyof typeof vertexModels] + return info ? { id, info } : { id: vertexDefaultModelId, info: vertexModels[vertexDefaultModelId] } + } + case "gemini": { + const id = apiConfiguration.apiModelId ?? geminiDefaultModelId + const info = geminiModels[id as keyof typeof geminiModels] + return info ? { id, info } : { id: geminiDefaultModelId, info: geminiModels[geminiDefaultModelId] } + } + case "deepseek": { + const id = apiConfiguration.apiModelId ?? deepSeekDefaultModelId + const info = deepSeekModels[id as keyof typeof deepSeekModels] + return info ? { id, info } : { id: deepSeekDefaultModelId, info: deepSeekModels[deepSeekDefaultModelId] } + } + case "openai-native": { + const id = apiConfiguration.apiModelId ?? openAiNativeDefaultModelId + const info = openAiNativeModels[id as keyof typeof openAiNativeModels] + return info + ? { id, info } + : { id: openAiNativeDefaultModelId, info: openAiNativeModels[openAiNativeDefaultModelId] } + } + case "mistral": { + const id = apiConfiguration.apiModelId ?? mistralDefaultModelId + const info = mistralModels[id as keyof typeof mistralModels] + return info ? { id, info } : { id: mistralDefaultModelId, info: mistralModels[mistralDefaultModelId] } + } + case "openai": { + const id = apiConfiguration.openAiModelId ?? "" + const info = apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults + return { id, info } + } + case "ollama": { + const id = apiConfiguration.ollamaModelId ?? "" + const info = openAiModelInfoSaneDefaults + return { id, info } + } + case "lmstudio": { + const id = apiConfiguration.lmStudioModelId ?? "" + const info = openAiModelInfoSaneDefaults + return { id, info } + } case "pearai": + const id = apiConfiguration.openAiModelId ?? "" // TODO: Nang to look at return apiConfiguration?.pearaiAgentModels?.models[id] ?? pearaiDefaultModelInfo - default: - return anthropicModels[id as keyof typeof anthropicModels] ?? anthropicModels[anthropicDefaultModelId] + case "vscode-lm": { + const id = apiConfiguration?.vsCodeLmModelSelector + ? `${apiConfiguration.vsCodeLmModelSelector.vendor}/${apiConfiguration.vsCodeLmModelSelector.family}` + : vscodeLlmDefaultModelId + const modelFamily = apiConfiguration?.vsCodeLmModelSelector?.family ?? vscodeLlmDefaultModelId + const info = vscodeLlmModels[modelFamily as keyof typeof vscodeLlmModels] + return { id, info: { ...openAiModelInfoSaneDefaults, ...info, supportsImages: false } } // VSCode LM API currently doesn't support images. + } + // case "anthropic": + // case "human-relay": + // case "fake-ai": + default: { + const id = apiConfiguration.apiModelId ?? anthropicDefaultModelId + const info = anthropicModels[id as keyof typeof anthropicModels] + return info ? { id, info } : { id: anthropicDefaultModelId, info: anthropicModels[anthropicDefaultModelId] } + } } } diff --git a/webview-ui/src/components/ui/select-dropdown.tsx b/webview-ui/src/components/ui/select-dropdown.tsx index 9137627b1cf..c6498c66185 100644 --- a/webview-ui/src/components/ui/select-dropdown.tsx +++ b/webview-ui/src/components/ui/select-dropdown.tsx @@ -195,7 +195,7 @@ export const SelectDropdown = React.memo( className={cn( "w-full min-w-0 max-w-full inline-flex items-center gap-1.5 relative whitespace-nowrap px-1.5 py-1 text-xs", "bg-transparent border border-[rgba(255,255,255,0.08)] rounded-md text-vscode-foreground w-auto", - "transition-all duration-150 focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder", + "transition-all duration-150 focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder focus-visible:ring-inset", disabled ? "opacity-50 cursor-not-allowed" : "opacity-90 hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)] cursor-pointer", diff --git a/webview-ui/src/components/ui/slider.tsx b/webview-ui/src/components/ui/slider.tsx index c37e6015c84..bb127bfb22d 100644 --- a/webview-ui/src/components/ui/slider.tsx +++ b/webview-ui/src/components/ui/slider.tsx @@ -11,10 +11,10 @@ const Slider = React.forwardRef< ref={ref} className={cn("relative flex w-full touch-none select-none items-center", className)} {...props}> - + - + )) Slider.displayName = SliderPrimitive.Root.displayName diff --git a/webview-ui/src/components/ui/textarea.tsx b/webview-ui/src/components/ui/textarea.tsx index 33cdc162c99..a3a24149c17 100644 --- a/webview-ui/src/components/ui/textarea.tsx +++ b/webview-ui/src/components/ui/textarea.tsx @@ -7,8 +7,8 @@ const Textarea = React.forwardRef { }) return ( -
+
{ className="flex items-center gap-2 text-vscode-editor-foreground font-vscode max-w-[250px]"> - {t(tip.titleKey)}: {t(tip.descriptionKey)} + + {t(tip.titleKey)} + + : {t(tip.descriptionKey)}
)) diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 6deabbd23e7..e5a6019527c 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -1,9 +1,8 @@ import React, { createContext, useCallback, useContext, useEffect, useState } from "react" import { useEvent } from "react-use" -import { ApiConfigMeta, ExtensionMessage, ExtensionState } from "@roo/shared/ExtensionMessage" -import { ApiConfiguration } from "@roo/shared/api" -import { vscode } from "@src/utils/vscode" -import { convertTextMateToHljs } from "@src/utils/textMateToHljs" + +import { ProviderSettingsEntry, ExtensionMessage, ExtensionState } from "@roo/shared/ExtensionMessage" +import { ProviderSettings } from "@roo/shared/api" import { findLastIndex } from "@roo/shared/array" import { McpServer } from "@roo/shared/mcp" import { checkExistKey } from "@roo/shared/checkExistApiConfig" @@ -13,6 +12,9 @@ import { experimentDefault, ExperimentId } from "@roo/shared/experiments" import { TelemetrySetting } from "@roo/shared/TelemetrySetting" import { PEARAI_URL, pearaiModels } from "../../../src/shared/pearaiApi" +import { vscode } from "@src/utils/vscode" +import { convertTextMateToHljs } from "@src/utils/textMateToHljs" + export interface ExtensionStateContextType extends ExtensionState { historyPreviewCollapsed?: boolean // Add the new state property didHydrateState: boolean @@ -23,7 +25,7 @@ export interface ExtensionStateContextType extends ExtensionState { currentCheckpoint?: string filePaths: string[] openedTabs: Array<{ label: string; isActive: boolean; path?: string }> - setApiConfiguration: (config: ApiConfiguration) => void + setApiConfiguration: (config: ProviderSettings) => void setCustomInstructions: (value?: string) => void setAlwaysAllowReadOnly: (value: boolean) => void setAlwaysAllowReadOnlyOutsideWorkspace: (value: boolean) => void @@ -66,7 +68,7 @@ export interface ExtensionStateContextType extends ExtensionState { requestDelaySeconds: number setRequestDelaySeconds: (value: number) => void setCurrentApiConfigName: (value: string) => void - setListApiConfigMeta: (value: ApiConfigMeta[]) => void + setListApiConfigMeta: (value: ProviderSettingsEntry[]) => void mode: Mode setMode: (value: Mode) => void setCustomModePrompts: (value: CustomModePrompts) => void @@ -202,7 +204,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode const [currentCheckpoint, setCurrentCheckpoint] = useState() const setListApiConfigMeta = useCallback( - (value: ApiConfigMeta[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })), + (value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })), [], ) diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx index 1ba2d87e9a4..dbac052944f 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx @@ -1,16 +1,16 @@ -// cd webview-ui && npx jest src/context/__tests__/ExtensionStateContext.test.tsx +// npx jest src/context/__tests__/ExtensionStateContext.test.tsx import { render, screen, act } from "@testing-library/react" import { ExtensionState } from "@roo/shared/ExtensionMessage" import { ExtensionStateContextProvider, useExtensionState, mergeExtensionState } from "../ExtensionStateContext" import { ExperimentId } from "@roo/shared/experiments" -import { ApiConfiguration } from "@roo/shared/api" +import { ProviderSettings } from "@roo/shared/api" -// Test component that consumes the context const TestComponent = () => { const { allowedCommands, setAllowedCommands, soundEnabled, showRooIgnoredFiles, setShowRooIgnoredFiles } = useExtensionState() + return (
{JSON.stringify(allowedCommands)}
@@ -26,9 +26,9 @@ const TestComponent = () => { ) } -// Test component for API configuration const ApiConfigTestComponent = () => { const { apiConfiguration, setApiConfiguration } = useExtensionState() + return (
{JSON.stringify(apiConfiguration)}
@@ -197,7 +197,7 @@ describe("mergeExtensionState", () => { customModes: [], maxOpenTabsContext: 20, maxWorkspaceFiles: 100, - apiConfiguration: { providerId: "openrouter" } as ApiConfiguration, + apiConfiguration: { providerId: "openrouter" } as ProviderSettings, telemetrySetting: "unset", showRooIgnoredFiles: true, renderContext: "sidebar", @@ -215,6 +215,7 @@ describe("mergeExtensionState", () => { apiConfiguration: { modelMaxThinkingTokens: 456, modelTemperature: 0.3 }, experiments: { powerSteering: true, + autoCondenseContext: true, } as Record, } @@ -227,6 +228,7 @@ describe("mergeExtensionState", () => { expect(result.experiments).toEqual({ powerSteering: true, + autoCondenseContext: true, }) }) }) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index b8d943380ea..6e9e2fae50a 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -109,7 +109,7 @@ "diffError": { "title": "Edició fallida" }, - "troubleMessage": "Agent està tenint problemes...", + "troubleMessage": "Roo està tenint problemes...", "apiRequest": { "title": "Sol·licitud API", "failed": "Sol·licitud API ha fallat", @@ -135,47 +135,47 @@ "current": "Actual" }, "instructions": { - "wantsToFetch": "Agent vol obtenir instruccions detallades per ajudar amb la tasca actual." + "wantsToFetch": "Roo vol obtenir instruccions detallades per ajudar amb la tasca actual." }, "fileOperations": { - "wantsToRead": "Agent vol llegir aquest fitxer:", - "wantsToReadOutsideWorkspace": "Agent vol llegir aquest fitxer fora de l'espai de treball:", - "didRead": "Agent ha llegit aquest fitxer:", - "wantsToEdit": "Agent vol editar aquest fitxer:", - "wantsToEditOutsideWorkspace": "Agent vol editar aquest fitxer fora de l'espai de treball:", - "wantsToCreate": "Agent vol crear un nou fitxer:", - "wantsToSearchReplace": "Agent vol realitzar cerca i substitució en aquest fitxer:", - "didSearchReplace": "Agent ha realitzat cerca i substitució en aquest fitxer:", - "wantsToInsert": "Agent vol inserir contingut en aquest fitxer:", - "wantsToInsertWithLineNumber": "Agent vol inserir contingut a la línia {{lineNumber}} d'aquest fitxer:", - "wantsToInsertAtEnd": "Agent vol afegir contingut al final d'aquest fitxer:" + "wantsToRead": "Roo vol llegir aquest fitxer:", + "wantsToReadOutsideWorkspace": "Roo vol llegir aquest fitxer fora de l'espai de treball:", + "didRead": "Roo ha llegit aquest fitxer:", + "wantsToEdit": "Roo vol editar aquest fitxer:", + "wantsToEditOutsideWorkspace": "Roo vol editar aquest fitxer fora de l'espai de treball:", + "wantsToCreate": "Roo vol crear un nou fitxer:", + "wantsToSearchReplace": "Roo vol realitzar cerca i substitució en aquest fitxer:", + "didSearchReplace": "Roo ha realitzat cerca i substitució en aquest fitxer:", + "wantsToInsert": "Roo vol inserir contingut en aquest fitxer:", + "wantsToInsertWithLineNumber": "Roo vol inserir contingut a la línia {{lineNumber}} d'aquest fitxer:", + "wantsToInsertAtEnd": "Roo vol afegir contingut al final d'aquest fitxer:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent vol veure els fitxers de nivell superior en aquest directori:", - "didViewTopLevel": "Agent ha vist els fitxers de nivell superior en aquest directori:", - "wantsToViewRecursive": "Agent vol veure recursivament tots els fitxers en aquest directori:", - "didViewRecursive": "Agent ha vist recursivament tots els fitxers en aquest directori:", - "wantsToViewDefinitions": "Agent vol veure noms de definicions de codi font utilitzats en aquest directori:", - "didViewDefinitions": "Agent ha vist noms de definicions de codi font utilitzats en aquest directori:", - "wantsToSearch": "Agent vol cercar en aquest directori {{regex}}:", - "didSearch": "Agent ha cercat en aquest directori {{regex}}:" + "wantsToViewTopLevel": "Roo vol veure els fitxers de nivell superior en aquest directori:", + "didViewTopLevel": "Roo ha vist els fitxers de nivell superior en aquest directori:", + "wantsToViewRecursive": "Roo vol veure recursivament tots els fitxers en aquest directori:", + "didViewRecursive": "Roo ha vist recursivament tots els fitxers en aquest directori:", + "wantsToViewDefinitions": "Roo vol veure noms de definicions de codi font utilitzats en aquest directori:", + "didViewDefinitions": "Roo ha vist noms de definicions de codi font utilitzats en aquest directori:", + "wantsToSearch": "Roo vol cercar en aquest directori {{regex}}:", + "didSearch": "Roo ha cercat en aquest directori {{regex}}:" }, "commandOutput": "Sortida de l'ordre", "response": "Resposta", "arguments": "Arguments", "mcp": { - "wantsToUseTool": "Agent vol utilitzar una eina al servidor MCP {{serverName}}:", - "wantsToAccessResource": "Agent vol accedir a un recurs al servidor MCP {{serverName}}:" + "wantsToUseTool": "Roo vol utilitzar una eina al servidor MCP {{serverName}}:", + "wantsToAccessResource": "Roo vol accedir a un recurs al servidor MCP {{serverName}}:" }, "modes": { - "wantsToSwitch": "Agent vol canviar a mode {{mode}}", - "wantsToSwitchWithReason": "Agent vol canviar a mode {{mode}} perquè: {{reason}}", - "didSwitch": "Agent ha canviat a mode {{mode}}", - "didSwitchWithReason": "Agent ha canviat a mode {{mode}} perquè: {{reason}}" + "wantsToSwitch": "Roo vol canviar a mode {{mode}}", + "wantsToSwitchWithReason": "Roo vol canviar a mode {{mode}} perquè: {{reason}}", + "didSwitch": "Roo ha canviat a mode {{mode}}", + "didSwitchWithReason": "Roo ha canviat a mode {{mode}} perquè: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent vol crear una nova subtasca en mode {{mode}}:", - "wantsToFinish": "Agent vol finalitzar aquesta subtasca", + "wantsToCreate": "Roo vol crear una nova subtasca en mode {{mode}}:", + "wantsToFinish": "Roo vol finalitzar aquesta subtasca", "newTaskContent": "Instruccions de la subtasca", "completionContent": "Subtasca completada", "resultContent": "Resultats de la subtasca", @@ -183,7 +183,7 @@ "completionInstructions": "Subtasca completada! Pots revisar els resultats i suggerir correccions o següents passos. Si tot sembla correcte, confirma per tornar el resultat a la tasca principal." }, "questions": { - "hasQuestion": "Agent té una pregunta:" + "hasQuestion": "Roo té una pregunta:" }, "taskCompleted": "Tasca completada", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "Copiar a l'entrada (o Shift + clic)" }, "announcement": { - "title": "🎉 Roo Code 3.15 publicat", - "description": "Agent Code 3.15 porta noves funcionalitats i millores basades en els teus comentaris.", + "title": "🎉 Roo Code 3.17 publicat", + "description": "Roo Code 3.17 porta noves funcionalitats potents i millores basades en els teus comentaris.", "whatsNew": "Novetats", - "feature1": "Memòria cau de prompts per a Vertex: S'ha afegit suport de memòria cau de prompts per a Vertex AI, millorant els temps de resposta i reduint els costos d'API", - "feature2": "Mecanisme alternatiu per a Terminal: S'ha implementat un mecanisme alternatiu quan la integració de shell del terminal VSCode falla", - "feature3": "Fragments de codi millorats: S'ha millorat la renderització i interacció amb fragments de codi a la interfície de xat", + "feature1": "Emmagatzematge en caché implícit per a Gemini: Les crides a l'API de Gemini ara s'emmagatzemen automàticament en caché, reduint els costos d'API", + "feature2": "Selecció de mode més intel·ligent: Les definicions de mode ara poden incloure orientació sobre quan s'ha d'utilitzar cada mode, permetent una millor orquestració", + "feature3": "Condensació intel·ligent del context: Resumeix de manera intel·ligent l'historial de converses quan el context s'omple en lloc de truncar-lo (activa-ho a Configuració -> Experimental)", "hideButton": "Amagar anunci", "detailsDiscussLinks": "Obtingues més detalls i participa a Discord i Reddit 🚀" }, "browser": { - "rooWantsToUse": "Agent vol utilitzar el navegador:", + "rooWantsToUse": "Roo vol utilitzar el navegador:", "consoleLogs": "Registres de consola", "noNewLogs": "(Cap registre nou)", "screenshot": "Captura de pantalla del navegador", @@ -233,6 +233,15 @@ "close": "Tancar navegador" } }, + "codeblock": { + "tooltips": { + "expand": "Expandir bloc de codi", + "collapse": "Contraure bloc de codi", + "enable_wrap": "Activar ajustament de línia", + "disable_wrap": "Desactivar ajustament de línia", + "copy_code": "Copiar codi" + } + }, "systemPromptWarning": "ADVERTÈNCIA: S'ha activat una substitució personalitzada d'instruccions del sistema. Això pot trencar greument la funcionalitat i causar un comportament impredictible.", "shellIntegration": { "title": "Advertència d'execució d'ordres", diff --git a/webview-ui/src/i18n/locales/ca/mcp.json b/webview-ui/src/i18n/locales/ca/mcp.json index 8da2cbb953c..478bbc095b5 100644 --- a/webview-ui/src/i18n/locales/ca/mcp.json +++ b/webview-ui/src/i18n/locales/ca/mcp.json @@ -1,33 +1,38 @@ { "title": "Servidors MCP", "done": "Fet", - "description": "El <0>Model Context Protocol permet la comunicació amb servidors MCP que s'executen localment i proporcionen eines i recursos addicionals per ampliar les capacitats de Roo. Pots utilitzar <1>servidors creats per la comunitat o demanar a Roo que creï noves eines específiques per al teu flux de treball (per exemple, \"afegir una eina que obtingui la documentació més recent de npm\").", + "description": "Activa el Model Context Protocol (MCP) perquè Roo Code pugui utilitzar eines i serveis addicionals de servidors externs. Això amplia el que Roo pot fer per tu. <0>Més informació", "enableToggle": { - "title": "Habilitar servidors MCP", - "description": "Quan està habilitat, Roo podrà interactuar amb servidors MCP per a funcionalitats avançades. Si no utilitzes MCP, pots desactivar això per reduir l'ús de tokens de Roo." + "title": "Activa els servidors MCP", + "description": "Activa-ho perquè Roo pugui utilitzar eines dels servidors MCP connectats. Això dóna més capacitats a Roo. Si no vols utilitzar aquestes eines addicionals, desactiva-ho per ajudar a reduir el cost dels tokens API." }, "enableServerCreation": { - "title": "Habilitar creació de servidors MCP", - "description": "Quan està habilitat, Roo pot ajudar-te a crear nous servidors MCP mitjançant ordres com \"afegir una nova eina per a...\". Si no necessites crear servidors MCP, pots desactivar això per reduir l'ús de tokens de Roo." + "title": "Activa la creació de servidors MCP", + "description": "Activa-ho perquè Roo t'ajudi a crear <1>nous servidors MCP personalitzats. <0>Més informació sobre la creació de servidors", + "hint": "Consell: Per reduir el cost dels tokens API, desactiva aquesta opció quan no demanis a Roo que creï un nou servidor MCP." }, - "editGlobalMCP": "Editar MCP Global", - "editProjectMCP": "Editar MCP del Projecte", + "editGlobalMCP": "Edita MCP global", + "editProjectMCP": "Edita MCP del projecte", + "learnMoreEditingSettings": "Més informació sobre com editar fitxers de configuració MCP", "tool": { - "alwaysAllow": "Permetre sempre", + "alwaysAllow": "Permet sempre", "parameters": "Paràmetres", "noDescription": "Sense descripció" }, "tabs": { "tools": "Eines", - "resources": "Recursos" + "resources": "Recursos", + "errors": "Errors" }, "emptyState": { "noTools": "No s'han trobat eines", - "noResources": "No s'han trobat recursos" + "noResources": "No s'han trobat recursos", + "noLogs": "No s'han trobat registres", + "noErrors": "No s'han trobat errors" }, "networkTimeout": { "label": "Temps d'espera de xarxa", - "description": "Temps màxim d'espera per a respostes del servidor", + "description": "Temps màxim d'espera per a les respostes del servidor", "options": { "15seconds": "15 segons", "30seconds": "30 segons", @@ -40,13 +45,13 @@ } }, "deleteDialog": { - "title": "Eliminar servidor MCP", - "description": "Estàs segur que vols eliminar el servidor MCP \"{{serverName}}\"? Aquesta acció no es pot desfer.", - "cancel": "Cancel·lar", - "delete": "Eliminar" + "title": "Elimina el servidor MCP", + "description": "Segur que vols eliminar el servidor MCP \"{{serverName}}\"? Aquesta acció no es pot desfer.", + "cancel": "Cancel·la", + "delete": "Elimina" }, "serverStatus": { - "retrying": "Reintentant...", - "retryConnection": "Reintentar connexió" + "retrying": "Tornant a intentar...", + "retryConnection": "Torna a intentar la connexió" } } diff --git a/webview-ui/src/i18n/locales/ca/prompts.json b/webview-ui/src/i18n/locales/ca/prompts.json index 0a1ffc1425f..f6e61c28344 100644 --- a/webview-ui/src/i18n/locales/ca/prompts.json +++ b/webview-ui/src/i18n/locales/ca/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Crear nou mode", "editModesConfig": "Editar configuració de modes", "editGlobalModes": "Editar modes globals", - "editProjectModes": "Editar modes de projecte (.pearai-agent-ignore)", - "createModeHelpText": "Feu clic a + per crear un nou mode personalitzat, o simplement demaneu a Roo al xat que en creï un per a vostè!" + "editProjectModes": "Editar modes de projecte (.roomodes)", + "createModeHelpText": "Els modes són persones especialitzades que adapten el comportament de Roo. <0>Aprèn sobre l'ús de modes o <1>Personalització de modes.", + "selectMode": "Cerqueu modes" }, "apiConfiguration": { "title": "Configuració d'API", @@ -33,16 +34,21 @@ "resetToDefault": "Restablir a valors predeterminats", "description": "Definiu l'experiència i personalitat de Roo per a aquest mode. Aquesta descripció determina com Roo es presenta i aborda les tasques." }, + "whenToUse": { + "title": "Quan utilitzar (opcional)", + "description": "Descriviu quan s'hauria d'utilitzar aquest mode. Això ajuda l'Orchestrator a escollir el mode correcte per a una tasca.", + "resetToDefault": "Restablir la descripció 'Quan utilitzar' a valors predeterminats" + }, "customInstructions": { "title": "Instruccions personalitzades específiques del mode (opcional)", "resetToDefault": "Restablir a valors predeterminats", "description": "Afegiu directrius de comportament específiques per al mode {{modeName}}.", - "loadFromFile": "Les instruccions personalitzades específiques per al mode {{mode}} també es poden carregar des de la carpeta .pearai-agent/rules-{{slug}}/ al vostre espai de treball (.roorules-{{slug}} i .clinerules-{{slug}} estan obsolets i deixaran de funcionar aviat)." + "loadFromFile": "Les instruccions personalitzades específiques per al mode {{mode}} també es poden carregar des de la carpeta .roo/rules-{{slug}}/ al vostre espai de treball (.roorules-{{slug}} i .clinerules-{{slug}} estan obsolets i deixaran de funcionar aviat)." }, "globalCustomInstructions": { "title": "Instruccions personalitzades per a tots els modes", - "description": "Aquestes instruccions s'apliquen a tots els modes. Proporcionen un conjunt bàsic de comportaments que es poden millorar amb instruccions específiques de cada mode a continuació.\nSi voleu que Roo pensi i parli en un idioma diferent al de la visualització del vostre editor ({{language}}), podeu especificar-ho aquí.", - "loadFromFile": "Les instruccions també es poden carregar des de la carpeta .pearai-agent/rules/ al vostre espai de treball (.roorules i .clinerules estan obsolets i deixaran de funcionar aviat)." + "description": "Aquestes instruccions s'apliquen a tots els modes. Proporcionen un conjunt bàsic de comportaments que es poden millorar amb instruccions específiques de cada mode a continuació. <0>Més informació", + "loadFromFile": "Les instruccions també es poden carregar des de la carpeta .roo/rules/ al vostre espai de treball (.roorules i .clinerules estan obsolets i deixaran de funcionar aviat)." }, "systemPrompt": { "preview": "Previsualització del prompt del sistema", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Avançat: Sobreescriure prompt del sistema", - "description": "Podeu reemplaçar completament el prompt del sistema per a aquest mode (a part de la definició de rol i instruccions personalitzades) creant un fitxer a .pearai-agent/system-prompt-{{slug}} al vostre espai de treball. Aquesta és una funcionalitat molt avançada que eludeix les salvaguardes integrades i les comprovacions de consistència (especialment al voltant de l'ús d'eines), així que aneu amb compte!" + "description": "<2>⚠️ Avís: Aquesta funcionalitat avançada eludeix les salvaguardes. <1>LLEGIU AIXÒ ABANS D'UTILITZAR!Sobreescriviu el prompt del sistema per defecte creant un fitxer a .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Crear nou mode", @@ -122,7 +128,7 @@ "description": "Disponible a tots els espais de treball" }, "project": { - "label": "Específic del projecte (.pearai-agent-ignore)", + "label": "Específic del projecte (.roomodes)", "description": "Només disponible en aquest espai de treball, té prioritat sobre el global" } }, @@ -130,6 +136,10 @@ "label": "Definició de rol", "description": "Definiu l'experiència i personalitat de Roo per a aquest mode." }, + "whenToUse": { + "label": "Quan utilitzar (opcional)", + "description": "Proporcioneu una descripció clara de quan aquest mode és més efectiu i per a quins tipus de tasques excel·leix." + }, "tools": { "label": "Eines disponibles", "description": "Seleccioneu quines eines pot utilitzar aquest mode." diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index b742cfeb71c..d5977adcbda 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Proveïdors", - "autoApprove": "Aprovació automàtica", - "browser": "Navegador / Ús de l'ordinador", + "autoApprove": "Auto-aprovació", + "browser": "Accés a l'ordinador", "checkpoints": "Punts de control", "notifications": "Notificacions", - "contextManagement": "Gestió de context", + "contextManagement": "Context", "terminal": "Terminal", - "advanced": "Avançat", - "experimental": "Funcions experimentals", + "experimental": "Experimental", "language": "Idioma", - "about": "Sobre Roo Code", - "interface": "Interfície" + "about": "Sobre Roo Code" }, "autoApprove": { "description": "Permet que Roo realitzi operacions automàticament sense requerir aprovació. Activeu aquesta configuració només si confieu plenament en la IA i enteneu els riscos de seguretat associats.", @@ -84,11 +82,11 @@ } }, "providers": { - "configProfile": "Perfil de configuració", "providerDocumentation": "Documentació de {{provider}}", + "configProfile": "Perfil de configuració", "description": "Deseu diferents configuracions d'API per canviar ràpidament entre proveïdors i configuracions.", "apiProvider": "Proveïdor d'API", - "openRouterApiKey": "Clau API d'OpenRouter", + "model": "Model", "nameEmpty": "El nom no pot estar buit", "nameExists": "Ja existeix un perfil amb aquest nom", "deleteProfile": "Esborrar perfil", @@ -105,7 +103,11 @@ "vscodeLmDescription": "L'API del model de llenguatge de VS Code us permet executar models proporcionats per altres extensions de VS Code (incloent-hi, però no limitat a, GitHub Copilot). La manera més senzilla de començar és instal·lar les extensions Copilot i Copilot Chat des del VS Code Marketplace.", "awsCustomArnUse": "Introduïu un ARN vàlid d'Amazon Bedrock per al model que voleu utilitzar. Exemples de format:", "awsCustomArnDesc": "Assegureu-vos que la regió a l'ARN coincideix amb la regió d'AWS seleccionada anteriorment.", + "openRouterApiKey": "Clau API d'OpenRouter", + "getOpenRouterApiKey": "Obtenir clau API d'OpenRouter", "apiKeyStorageNotice": "Les claus API s'emmagatzemen de forma segura a l'Emmagatzematge Secret de VSCode", + "glamaApiKey": "Clau API de Glama", + "getGlamaApiKey": "Obtenir clau API de Glama", "useCustomBaseUrl": "Utilitzar URL base personalitzada", "useHostHeader": "Utilitzar capçalera Host personalitzada", "useLegacyFormat": "Utilitzar el format d'API OpenAI antic", @@ -113,19 +115,23 @@ "headerName": "Nom de la capçalera", "headerValue": "Valor de la capçalera", "noCustomHeaders": "No hi ha capçaleres personalitzades definides. Feu clic al botó + per afegir-ne una.", - "openRouterTransformsText": "Comprimir prompts i cadenes de missatges a la mida del context (Transformacions d'OpenRouter)", - "model": "Model", - "getOpenRouterApiKey": "Obtenir clau API d'OpenRouter", - "glamaApiKey": "Clau API de Glama", - "getGlamaApiKey": "Obtenir clau API de Glama", "requestyApiKey": "Clau API de Requesty", + "refreshModels": { + "label": "Actualitzar models", + "hint": "Si us plau, torneu a obrir la configuració per veure els models més recents." + }, "getRequestyApiKey": "Obtenir clau API de Requesty", + "openRouterTransformsText": "Comprimir prompts i cadenes de missatges a la mida del context (Transformacions d'OpenRouter)", "anthropicApiKey": "Clau API d'Anthropic", "getAnthropicApiKey": "Obtenir clau API d'Anthropic", "anthropicUseAuthToken": "Passar la clau API d'Anthropic com a capçalera d'autorització en lloc de X-Api-Key", + "chutesApiKey": "Clau API de Chutes", + "getChutesApiKey": "Obtenir clau API de Chutes", "deepSeekApiKey": "Clau API de DeepSeek", "getDeepSeekApiKey": "Obtenir clau API de DeepSeek", "geminiApiKey": "Clau API de Gemini", + "getGroqApiKey": "Obtenir clau API de Groq", + "groqApiKey": "Clau API de Groq", "getGeminiApiKey": "Obtenir clau API de Gemini", "openAiApiKey": "Clau API d'OpenAI", "openAiBaseUrl": "URL base", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Establir una URL alternativa per al model Codestral.", "xaiApiKey": "Clau API de xAI", "getXaiApiKey": "Obtenir clau API de xAI", + "litellmApiKey": "Clau API de LiteLLM", + "litellmBaseUrl": "URL base de LiteLLM", "awsCredentials": "Credencials d'AWS", "awsProfile": "Perfil d'AWS", "awsProfileName": "Nom del perfil d'AWS", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Habilitar eina de navegador", - "description": "Quan està habilitat, Roo pot utilitzar un navegador per interactuar amb llocs web quan s'utilitzen models que admeten l'ús de l'ordinador." + "description": "Quan està habilitat, Roo pot utilitzar un navegador per interactuar amb llocs web quan s'utilitzen models que admeten l'ús de l'ordinador. <0>Més informació" }, "viewport": { "label": "Mida del viewport", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Habilitar punts de control automàtics", - "description": "Quan està habilitat, Roo crearà automàticament punts de control durant l'execució de tasques, facilitant la revisió de canvis o la reversió a estats anteriors." + "description": "Quan està habilitat, Roo crearà automàticament punts de control durant l'execució de tasques, facilitant la revisió de canvis o la reversió a estats anteriors. <0>Més informació" } }, "notifications": { @@ -298,57 +306,69 @@ "label": "Límit de context de fitxers de l'espai de treball", "description": "Nombre màxim de fitxers a incloure als detalls del directori de treball actual. Valors més alts proporcionen més context però augmenten l'ús de token." }, - "pearai-agent-ignore": { - "label": "Mostrar fitxers .pearai-agent-ignore en llistes i cerques", - "description": "Quan està habilitat, els fitxers que coincideixen amb els patrons a .pearai-agent-ignore es mostraran en llistes amb un símbol de cadenat. Quan està deshabilitat, aquests fitxers s'ocultaran completament de les llistes de fitxers i cerques." + "rooignore": { + "label": "Mostrar fitxers .rooignore en llistes i cerques", + "description": "Quan està habilitat, els fitxers que coincideixen amb els patrons a .rooignore es mostraran en llistes amb un símbol de cadenat. Quan està deshabilitat, aquests fitxers s'ocultaran completament de les llistes de fitxers i cerques." }, "maxReadFile": { "label": "Llindar d'auto-truncament de lectura de fitxers", - "description": "Agent llegeix aquest nombre de línies quan el model omet els valors d'inici/final. Si aquest nombre és menor que el total del fitxer, Roo genera un índex de números de línia de les definicions de codi. Casos especials: -1 indica a Roo que llegeixi tot el fitxer (sense indexació), i 0 indica que no llegeixi cap línia i proporcioni només índexs de línia per a un context mínim. Valors més baixos minimitzen l'ús inicial de context, permetent lectures posteriors de rangs de línies precisos. Les sol·licituds amb inici/final explícits no estan limitades per aquesta configuració.", + "description": "Roo llegeix aquest nombre de línies quan el model omet els valors d'inici/final. Si aquest nombre és menor que el total del fitxer, Roo genera un índex de números de línia de les definicions de codi. Casos especials: -1 indica a Roo que llegeixi tot el fitxer (sense indexació), i 0 indica que no llegeixi cap línia i proporcioni només índexs de línia per a un context mínim. Valors més baixos minimitzen l'ús inicial de context, permetent lectures posteriors de rangs de línies precisos. Les sol·licituds amb inici/final explícits no estan limitades per aquesta configuració.", "lines": "línies", "always_full_read": "Llegeix sempre el fitxer sencer" } }, "terminal": { + "basic": { + "label": "Configuració del terminal: Bàsica", + "description": "Configuració bàsica del terminal" + }, + "advanced": { + "label": "Configuració del terminal: Avançada", + "description": "Les següents opcions poden requerir reiniciar el terminal per aplicar la configuració." + }, "outputLineLimit": { "label": "Límit de sortida de terminal", - "description": "Nombre màxim de línies a incloure a la sortida del terminal en executar comandes. Quan s'excedeix, s'eliminaran línies del mig, estalviant token." + "description": "Nombre màxim de línies a incloure a la sortida del terminal en executar comandes. Quan s'excedeix, s'eliminaran línies del mig, estalviant token. <0>Més informació" }, "shellIntegrationTimeout": { "label": "Temps d'espera d'integració de shell del terminal", - "description": "Temps màxim d'espera per a la inicialització de la integració de shell abans d'executar comandes. Per a usuaris amb temps d'inici de shell llargs, aquest valor pot necessitar ser augmentat si veieu errors \"Shell Integration Unavailable\" al terminal." + "description": "Temps màxim d'espera per a la inicialització de la integració de shell abans d'executar comandes. Per a usuaris amb temps d'inici de shell llargs, aquest valor pot necessitar ser augmentat si veieu errors \"Shell Integration Unavailable\" al terminal. <0>Més informació" }, "shellIntegrationDisabled": { "label": "Desactiva la integració de l'intèrpret d'ordres del terminal", - "description": "Activa això si les ordres del terminal no funcionen correctament o si veus errors de 'Shell Integration Unavailable'. Això utilitza un mètode més senzill per executar ordres, evitant algunes funcions avançades del terminal." - }, - "compressProgressBar": { - "label": "Comprimir sortida de barra de progrés", - "description": "Quan està habilitat, processa la sortida del terminal amb retorns de carro (\\r) per simular com un terminal real mostraria el contingut. Això elimina els estats intermedis de les barres de progrés, mantenint només l'estat final, la qual cosa conserva espai de context per a informació més rellevant." - }, - "zdotdir": { - "label": "Habilitar gestió de ZDOTDIR", - "description": "Quan està habilitat, crea un directori temporal per a ZDOTDIR per gestionar correctament la integració del shell zsh. Això assegura que la integració del shell de VSCode funcioni correctament amb zsh mentre es preserva la teva configuració de zsh. (experimental)" + "description": "Activa això si les ordres del terminal no funcionen correctament o si veus errors de 'Shell Integration Unavailable'. Això utilitza un mètode més senzill per executar ordres, evitant algunes funcions avançades del terminal. <0>Més informació" }, "commandDelay": { "label": "Retard de comanda del terminal", - "description": "Retard en mil·lisegons a afegir després de l'execució de la comanda. La configuració predeterminada de 0 desactiva completament el retard. Això pot ajudar a assegurar que la sortida de la comanda es capturi completament en terminals amb problemes de temporització. En la majoria de terminals s'implementa establint `PROMPT_COMMAND='sleep N'` i Powershell afegeix `start-sleep` al final de cada comanda. Originalment era una solució per al error VSCode#237208 i pot no ser necessari." + "description": "Retard en mil·lisegons a afegir després de l'execució de la comanda. La configuració predeterminada de 0 desactiva completament el retard. Això pot ajudar a assegurar que la sortida de la comanda es capturi completament en terminals amb problemes de temporització. En la majoria de terminals s'implementa establint `PROMPT_COMMAND='sleep N'` i Powershell afegeix `start-sleep` al final de cada comanda. Originalment era una solució per al error VSCode#237208 i pot no ser necessari. <0>Més informació" + }, + "compressProgressBar": { + "label": "Comprimir sortida de barra de progrés", + "description": "Quan està habilitat, processa la sortida del terminal amb retorns de carro (\\r) per simular com un terminal real mostraria el contingut. Això elimina els estats intermedis de les barres de progrés, mantenint només l'estat final, la qual cosa conserva espai de context per a informació més rellevant. <0>Més informació" }, "powershellCounter": { "label": "Habilita la solució temporal del comptador PowerShell", - "description": "Quan està habilitat, afegeix un comptador a les comandes PowerShell per assegurar l'execució correcta de les comandes. Això ajuda amb els terminals PowerShell que poden tenir problemes amb la captura de sortida." + "description": "Quan està habilitat, afegeix un comptador a les comandes PowerShell per assegurar l'execució correcta de les comandes. Això ajuda amb els terminals PowerShell que poden tenir problemes amb la captura de sortida. <0>Més informació" }, "zshClearEolMark": { "label": "Neteja la marca EOL de ZSH", - "description": "Quan està habilitat, neteja la marca de final de línia de ZSH establint PROMPT_EOL_MARK=''. Això evita problemes amb la interpretació de la sortida de comandes quan acaba amb caràcters especials com '%'." + "description": "Quan està habilitat, neteja la marca de final de línia de ZSH establint PROMPT_EOL_MARK=''. Això evita problemes amb la interpretació de la sortida de comandes quan acaba amb caràcters especials com '%'. <0>Més informació" }, "zshOhMy": { "label": "Habilita la integració Oh My Zsh", - "description": "Quan està habilitat, estableix ITERM_SHELL_INTEGRATION_INSTALLED=Yes per habilitar les característiques d'integració del shell Oh My Zsh. Aplicar aquesta configuració pot requerir reiniciar l'IDE. (experimental)" + "description": "Quan està habilitat, estableix ITERM_SHELL_INTEGRATION_INSTALLED=Yes per habilitar les característiques d'integració del shell Oh My Zsh. Aplicar aquesta configuració pot requerir reiniciar l'IDE. <0>Més informació" }, "zshP10k": { "label": "Habilita la integració Powerlevel10k", - "description": "Quan està habilitat, estableix POWERLEVEL9K_TERM_SHELL_INTEGRATION=true per habilitar les característiques d'integració del shell Powerlevel10k. (experimental)" + "description": "Quan està habilitat, estableix POWERLEVEL9K_TERM_SHELL_INTEGRATION=true per habilitar les característiques d'integració del shell Powerlevel10k. <0>Més informació" + }, + "zdotdir": { + "label": "Habilitar gestió de ZDOTDIR", + "description": "Quan està habilitat, crea un directori temporal per a ZDOTDIR per gestionar correctament la integració del shell zsh. Això assegura que la integració del shell de VSCode funcioni correctament amb zsh mentre es preserva la teva configuració de zsh. <0>Més informació" + }, + "inheritEnv": { + "label": "Hereta variables d'entorn", + "description": "Quan està habilitat, el terminal hereta les variables d'entorn del procés pare de VSCode, com ara la configuració d'integració del shell definida al perfil d'usuari. Això commuta directament la configuració global de VSCode `terminal.integrated.inheritEnv`. <0>Més informació" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Condensar intel·ligentment la finestra de context", + "description": "Utilitza una crida LLM per resumir la conversa anterior quan la finestra de context de la tasca està gairebé plena, en lloc d'eliminar missatges antics. Avís: el cost de resumir actualment no s'inclou en els costos d'API mostrats a la interfície." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Utilitzar estratègia diff unificada experimental", "description": "Activar l'estratègia diff unificada experimental. Aquesta estratègia podria reduir el nombre de reintents causats per errors del model, però pot causar comportaments inesperats o edicions incorrectes. Activeu-la només si enteneu els riscos i esteu disposats a revisar acuradament tots els canvis." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Habilitar emmagatzematge en caché de prompts", - "description": "Quan està habilitat, Roo utilitzarà aquest model amb la memòria cau de prompts activada per reduir costos." + "label": "Desactivar la memòria cau de prompts", + "description": "Quan està marcat, Roo no utilitzarà la memòria cau de prompts per a aquest model." }, "temperature": { "useCustom": "Utilitzar temperatura personalitzada", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Si teniu qualsevol pregunta o comentari, no dubteu a obrir un issue a github.com/RooVetGit/Roo-Code o unir-vos a reddit.com/r/RooCode o discord.gg/roocode", - "version": "Agent v{{version}}", "telemetry": { "label": "Permetre informes anònims d'errors i ús", "description": "Ajudeu a millorar Roo Code enviant dades d'ús anònimes i informes d'errors. Mai s'envia codi, prompts o informació personal. Vegeu la nostra política de privacitat per a més detalls." diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index aad2e43f5ac..7e450d9cd0b 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -109,7 +109,7 @@ "diffError": { "title": "Bearbeitung fehlgeschlagen" }, - "troubleMessage": "Agent hat Probleme...", + "troubleMessage": "Roo hat Probleme...", "apiRequest": { "title": "API-Anfrage", "failed": "API-Anfrage fehlgeschlagen", @@ -135,47 +135,47 @@ "current": "Aktuell" }, "instructions": { - "wantsToFetch": "Agent möchte detaillierte Anweisungen abrufen, um bei der aktuellen Aufgabe zu helfen" + "wantsToFetch": "Roo möchte detaillierte Anweisungen abrufen, um bei der aktuellen Aufgabe zu helfen" }, "fileOperations": { - "wantsToRead": "Agent möchte diese Datei lesen:", - "wantsToReadOutsideWorkspace": "Agent möchte diese Datei außerhalb des Arbeitsbereichs lesen:", - "didRead": "Agent hat diese Datei gelesen:", - "wantsToEdit": "Agent möchte diese Datei bearbeiten:", - "wantsToEditOutsideWorkspace": "Agent möchte diese Datei außerhalb des Arbeitsbereichs bearbeiten:", - "wantsToCreate": "Agent möchte eine neue Datei erstellen:", - "wantsToSearchReplace": "Agent möchte in dieser Datei suchen und ersetzen:", - "didSearchReplace": "Agent hat Suchen und Ersetzen in dieser Datei durchgeführt:", - "wantsToInsert": "Agent möchte Inhalte in diese Datei einfügen:", - "wantsToInsertWithLineNumber": "Agent möchte Inhalte in diese Datei in Zeile {{lineNumber}} einfügen:", - "wantsToInsertAtEnd": "Agent möchte Inhalte am Ende dieser Datei anhängen:" + "wantsToRead": "Roo möchte diese Datei lesen:", + "wantsToReadOutsideWorkspace": "Roo möchte diese Datei außerhalb des Arbeitsbereichs lesen:", + "didRead": "Roo hat diese Datei gelesen:", + "wantsToEdit": "Roo möchte diese Datei bearbeiten:", + "wantsToEditOutsideWorkspace": "Roo möchte diese Datei außerhalb des Arbeitsbereichs bearbeiten:", + "wantsToCreate": "Roo möchte eine neue Datei erstellen:", + "wantsToSearchReplace": "Roo möchte in dieser Datei suchen und ersetzen:", + "didSearchReplace": "Roo hat Suchen und Ersetzen in dieser Datei durchgeführt:", + "wantsToInsert": "Roo möchte Inhalte in diese Datei einfügen:", + "wantsToInsertWithLineNumber": "Roo möchte Inhalte in diese Datei in Zeile {{lineNumber}} einfügen:", + "wantsToInsertAtEnd": "Roo möchte Inhalte am Ende dieser Datei anhängen:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent möchte die Dateien auf oberster Ebene in diesem Verzeichnis anzeigen:", - "didViewTopLevel": "Agent hat die Dateien auf oberster Ebene in diesem Verzeichnis angezeigt:", - "wantsToViewRecursive": "Agent möchte rekursiv alle Dateien in diesem Verzeichnis anzeigen:", - "didViewRecursive": "Agent hat rekursiv alle Dateien in diesem Verzeichnis angezeigt:", - "wantsToViewDefinitions": "Agent möchte Quellcode-Definitionsnamen in diesem Verzeichnis anzeigen:", - "didViewDefinitions": "Agent hat Quellcode-Definitionsnamen in diesem Verzeichnis angezeigt:", - "wantsToSearch": "Agent möchte dieses Verzeichnis nach {{regex}} durchsuchen:", - "didSearch": "Agent hat dieses Verzeichnis nach {{regex}} durchsucht:" + "wantsToViewTopLevel": "Roo möchte die Dateien auf oberster Ebene in diesem Verzeichnis anzeigen:", + "didViewTopLevel": "Roo hat die Dateien auf oberster Ebene in diesem Verzeichnis angezeigt:", + "wantsToViewRecursive": "Roo möchte rekursiv alle Dateien in diesem Verzeichnis anzeigen:", + "didViewRecursive": "Roo hat rekursiv alle Dateien in diesem Verzeichnis angezeigt:", + "wantsToViewDefinitions": "Roo möchte Quellcode-Definitionsnamen in diesem Verzeichnis anzeigen:", + "didViewDefinitions": "Roo hat Quellcode-Definitionsnamen in diesem Verzeichnis angezeigt:", + "wantsToSearch": "Roo möchte dieses Verzeichnis nach {{regex}} durchsuchen:", + "didSearch": "Roo hat dieses Verzeichnis nach {{regex}} durchsucht:" }, "commandOutput": "Befehlsausgabe", "response": "Antwort", "arguments": "Argumente", "mcp": { - "wantsToUseTool": "Agent möchte ein Tool auf dem {{serverName}} MCP-Server verwenden:", - "wantsToAccessResource": "Agent möchte auf eine Ressource auf dem {{serverName}} MCP-Server zugreifen:" + "wantsToUseTool": "Roo möchte ein Tool auf dem {{serverName}} MCP-Server verwenden:", + "wantsToAccessResource": "Roo möchte auf eine Ressource auf dem {{serverName}} MCP-Server zugreifen:" }, "modes": { - "wantsToSwitch": "Agent möchte zum {{mode}}-Modus wechseln", - "wantsToSwitchWithReason": "Agent möchte zum {{mode}}-Modus wechseln, weil: {{reason}}", - "didSwitch": "Agent hat zum {{mode}}-Modus gewechselt", - "didSwitchWithReason": "Agent hat zum {{mode}}-Modus gewechselt, weil: {{reason}}" + "wantsToSwitch": "Roo möchte zum {{mode}}-Modus wechseln", + "wantsToSwitchWithReason": "Roo möchte zum {{mode}}-Modus wechseln, weil: {{reason}}", + "didSwitch": "Roo hat zum {{mode}}-Modus gewechselt", + "didSwitchWithReason": "Roo hat zum {{mode}}-Modus gewechselt, weil: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent möchte eine neue Teilaufgabe im {{mode}}-Modus erstellen:", - "wantsToFinish": "Agent möchte diese Teilaufgabe abschließen", + "wantsToCreate": "Roo möchte eine neue Teilaufgabe im {{mode}}-Modus erstellen:", + "wantsToFinish": "Roo möchte diese Teilaufgabe abschließen", "newTaskContent": "Teilaufgabenanweisungen", "completionContent": "Teilaufgabe abgeschlossen", "resultContent": "Teilaufgabenergebnisse", @@ -183,7 +183,7 @@ "completionInstructions": "Teilaufgabe abgeschlossen! Du kannst die Ergebnisse überprüfen und Korrekturen oder nächste Schritte vorschlagen. Wenn alles gut aussieht, bestätige, um das Ergebnis an die übergeordnete Aufgabe zurückzugeben." }, "questions": { - "hasQuestion": "Agent hat eine Frage:" + "hasQuestion": "Roo hat eine Frage:" }, "taskCompleted": "Aufgabe abgeschlossen", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "In Eingabefeld kopieren (oder Shift + Klick)" }, "announcement": { - "title": "🎉 Roo Code 3.15 veröffentlicht", - "description": "Agent Code 3.15 bringt neue Funktionen und Verbesserungen basierend auf deinem Feedback.", + "title": "🎉 Roo Code 3.17 veröffentlicht", + "description": "Roo Code 3.17 bringt leistungsstarke neue Funktionen und Verbesserungen basierend auf deinem Feedback.", "whatsNew": "Was ist neu", - "feature1": "Prompt-Caching für Vertex: Prompt-Caching-Unterstützung für Vertex AI hinzugefügt, verbessert Antwortzeiten und reduziert API-Kosten", - "feature2": "Terminal-Fallback: Implementierung eines Fallback-Mechanismus, wenn die VSCode-Terminal-Shell-Integration fehlschlägt", - "feature3": "Verbesserte Code-Snippets: Verbesserte Darstellung und Interaktion mit Code-Snippets in der Chat-Oberfläche", + "feature1": "Implizites Caching für Gemini: Gemini API-Aufrufe werden jetzt automatisch zwischengespeichert, wodurch API-Kosten reduziert werden", + "feature2": "Intelligentere Modusauswahl: Modusdefinitionen können jetzt Hinweise enthalten, wann jeder Modus verwendet werden sollte, was eine bessere Orchestrierung ermöglicht", + "feature3": "Intelligente Kontextkomprimierung: Fasst Gesprächsverlauf intelligent zusammen, wenn der Kontext voll wird, anstatt ihn abzuschneiden (aktiviere dies in Einstellungen -> Experimentell)", "hideButton": "Ankündigung ausblenden", "detailsDiscussLinks": "Erhalte mehr Details und diskutiere auf Discord und Reddit 🚀" }, "browser": { - "rooWantsToUse": "Agent möchte den Browser verwenden:", + "rooWantsToUse": "Roo möchte den Browser verwenden:", "consoleLogs": "Konsolenprotokolle", "noNewLogs": "(Keine neuen Protokolle)", "screenshot": "Browser-Screenshot", @@ -233,6 +233,15 @@ "close": "Browser schließen" } }, + "codeblock": { + "tooltips": { + "expand": "Code-Block erweitern", + "collapse": "Code-Block reduzieren", + "enable_wrap": "Zeilenumbruch aktivieren", + "disable_wrap": "Zeilenumbruch deaktivieren", + "copy_code": "Code kopieren" + } + }, "systemPromptWarning": "WARNUNG: Benutzerdefinierte Systemaufforderung aktiv. Dies kann die Funktionalität erheblich beeinträchtigen und zu unvorhersehbarem Verhalten führen.", "shellIntegration": { "title": "Befehlsausführungswarnung", diff --git a/webview-ui/src/i18n/locales/de/mcp.json b/webview-ui/src/i18n/locales/de/mcp.json index 60332c27d6f..1140d443ac1 100644 --- a/webview-ui/src/i18n/locales/de/mcp.json +++ b/webview-ui/src/i18n/locales/de/mcp.json @@ -1,17 +1,19 @@ { "title": "MCP-Server", "done": "Fertig", - "description": "Das <0>Model Context Protocol ermöglicht die Kommunikation mit lokal laufenden MCP-Servern, die zusätzliche Tools und Ressourcen zur Erweiterung der Fähigkeiten von Roo bereitstellen. Du kannst <1>von der Community erstellte Server verwenden oder Roo bitten, neue Tools speziell für deinen Workflow zu erstellen (z.B. \"ein Tool hinzufügen, das die neueste npm-Dokumentation abruft\").", + "description": "Aktiviere das Model Context Protocol (MCP), damit Roo Code zusätzliche Tools und Dienste von externen Servern nutzen kann. Dies erweitert, was Roo für dich tun kann. <0>Mehr erfahren", "enableToggle": { "title": "MCP-Server aktivieren", - "description": "Wenn aktiviert, kann Roo mit MCP-Servern für erweiterte Funktionen interagieren. Wenn du MCP nicht verwendest, kannst du dies deaktivieren, um den Token-Verbrauch von Roo zu reduzieren." + "description": "Schalte dies EIN, damit Roo Tools von verbundenen MCP-Servern verwenden kann. Dies gibt Roo mehr Möglichkeiten. Wenn du diese zusätzlichen Tools nicht verwenden möchtest, schalte es AUS, um API-Token-Kosten zu senken." }, "enableServerCreation": { "title": "MCP-Server-Erstellung aktivieren", - "description": "Wenn aktiviert, kann Roo dir helfen, neue MCP-Server über Befehle wie \"neues Tool hinzufügen zu...\" zu erstellen. Wenn du keine MCP-Server erstellen musst, kannst du dies deaktivieren, um den Token-Verbrauch von Roo zu reduzieren." + "description": "Aktiviere dies, damit Roo dir helfen kann, <1>neue benutzerdefinierte MCP-Server zu erstellen. <0>Erfahre mehr über Server-Erstellung", + "hint": "Hinweis: Um API-Token-Kosten zu senken, deaktiviere diese Einstellung, wenn du Roo nicht aktiv darum bittest, einen neuen MCP-Server zu erstellen." }, - "editGlobalMCP": "Globales MCP bearbeiten", + "editGlobalMCP": "Globale MCP bearbeiten", "editProjectMCP": "Projekt-MCP bearbeiten", + "learnMoreEditingSettings": "Mehr über das Bearbeiten von MCP-Einstellungsdateien erfahren", "tool": { "alwaysAllow": "Immer erlauben", "parameters": "Parameter", @@ -19,11 +21,14 @@ }, "tabs": { "tools": "Tools", - "resources": "Ressourcen" + "resources": "Ressourcen", + "errors": "Fehler" }, "emptyState": { "noTools": "Keine Tools gefunden", - "noResources": "Keine Ressourcen gefunden" + "noResources": "Keine Ressourcen gefunden", + "noLogs": "Keine Protokolle gefunden", + "noErrors": "Keine Fehler gefunden" }, "networkTimeout": { "label": "Netzwerk-Timeout", @@ -47,6 +52,6 @@ }, "serverStatus": { "retrying": "Wiederhole...", - "retryConnection": "Verbindung wiederherstellen" + "retryConnection": "Verbindung wiederholen" } } diff --git a/webview-ui/src/i18n/locales/de/prompts.json b/webview-ui/src/i18n/locales/de/prompts.json index df67f25aee5..bad6247352a 100644 --- a/webview-ui/src/i18n/locales/de/prompts.json +++ b/webview-ui/src/i18n/locales/de/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Neuen Modus erstellen", "editModesConfig": "Moduskonfiguration bearbeiten", "editGlobalModes": "Globale Modi bearbeiten", - "editProjectModes": "Projektmodi bearbeiten (.pearai-agent-ignore)", - "createModeHelpText": "Klicke auf +, um einen neuen benutzerdefinierten Modus zu erstellen, oder bitte Roo einfach im Chat, einen für dich zu erstellen!" + "editProjectModes": "Projektmodi bearbeiten (.roomodes)", + "createModeHelpText": "Modi sind spezialisierte Personas, die Roos Verhalten anpassen. <0>Erfahre mehr über die Verwendung von Modi oder <1>die Anpassung von Modi.", + "selectMode": "Modi suchen" }, "apiConfiguration": { "title": "API-Konfiguration", @@ -33,16 +34,21 @@ "resetToDefault": "Auf Standardwerte zurücksetzen", "description": "Definiere Roos Expertise und Persönlichkeit für diesen Modus. Diese Beschreibung prägt, wie Roo sich präsentiert und an Aufgaben herangeht." }, + "whenToUse": { + "title": "Wann zu verwenden (optional)", + "description": "Beschreibe, wann dieser Modus verwendet werden sollte. Dies hilft dem Orchestrator, den richtigen Modus für eine Aufgabe auszuwählen.", + "resetToDefault": "Beschreibung 'Wann zu verwenden' auf Standardwerte zurücksetzen" + }, "customInstructions": { "title": "Modusspezifische benutzerdefinierte Anweisungen (optional)", "resetToDefault": "Auf Standardwerte zurücksetzen", "description": "Fügen Sie verhaltensspezifische Richtlinien für den Modus {{modeName}} hinzu.", - "loadFromFile": "Benutzerdefinierte Anweisungen für den Modus {{mode}} können auch aus dem Ordner .pearai-agent/rules-{{slug}}/ in deinem Arbeitsbereich geladen werden (.roorules-{{slug}} und .clinerules-{{slug}} sind veraltet und werden bald nicht mehr funktionieren)." + "loadFromFile": "Benutzerdefinierte Anweisungen für den Modus {{mode}} können auch aus dem Ordner .roo/rules-{{slug}}/ in deinem Arbeitsbereich geladen werden (.roorules-{{slug}} und .clinerules-{{slug}} sind veraltet und werden bald nicht mehr funktionieren)." }, "globalCustomInstructions": { "title": "Benutzerdefinierte Anweisungen für alle Modi", - "description": "Diese Anweisungen gelten für alle Modi. Sie bieten einen grundlegenden Satz von Verhaltensweisen, die durch modusspezifische Anweisungen unten erweitert werden können.\nWenn du möchtest, dass Roo in einer anderen Sprache als deiner Editor-Anzeigesprache ({{language}}) denkt und spricht, kannst du das hier angeben.", - "loadFromFile": "Anweisungen können auch aus dem Ordner .pearai-agent/rules/ in deinem Arbeitsbereich geladen werden (.roorules und .clinerules sind veraltet und werden bald nicht mehr funktionieren)." + "description": "Diese Anweisungen gelten für alle Modi. Sie bieten einen grundlegenden Satz von Verhaltensweisen, die durch modusspezifische Anweisungen unten erweitert werden können. <0>Mehr erfahren", + "loadFromFile": "Anweisungen können auch aus dem Ordner .roo/rules/ in deinem Arbeitsbereich geladen werden (.roorules und .clinerules sind veraltet und werden bald nicht mehr funktionieren)." }, "systemPrompt": { "preview": "System-Prompt Vorschau", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Erweitert: System-Prompt überschreiben", - "description": "Du kannst den System-Prompt für diesen Modus vollständig ersetzen (abgesehen von der Rollendefinition und benutzerdefinierten Anweisungen), indem du eine Datei unter .pearai-agent/system-prompt-{{slug}} in deinem Arbeitsbereich erstellst. Dies ist eine sehr fortgeschrittene Funktion, die eingebaute Schutzmaßnahmen und Konsistenzprüfungen umgeht (besonders bei der Werkzeugnutzung), also sei vorsichtig!" + "description": "<2>⚠️ Warnung: Diese erweiterte Funktion umgeht Sicherheitsvorkehrungen. <1>LESEN SIE DIES VOR DER VERWENDUNG!Überschreiben Sie den Standard-System-Prompt, indem Sie eine Datei unter .roo/system-prompt-{{slug}} erstellen." }, "createModeDialog": { "title": "Neuen Modus erstellen", @@ -122,7 +128,7 @@ "description": "Verfügbar in allen Arbeitsbereichen" }, "project": { - "label": "Projektspezifisch (.pearai-agent-ignore)", + "label": "Projektspezifisch (.roomodes)", "description": "Nur in diesem Arbeitsbereich verfügbar, hat Vorrang vor global" } }, @@ -130,6 +136,10 @@ "label": "Rollendefinition", "description": "Definiere Roos Expertise und Persönlichkeit für diesen Modus." }, + "whenToUse": { + "label": "Wann zu verwenden (optional)", + "description": "Gib eine klare Beschreibung, wann dieser Modus am effektivsten ist und für welche Arten von Aufgaben er sich besonders eignet." + }, "tools": { "label": "Verfügbare Werkzeuge", "description": "Wähle, welche Werkzeuge dieser Modus verwenden kann." diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index ef31bc04a10..5763f0be93e 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Anbieter", - "autoApprove": "Automatische Genehmigung", - "browser": "Browser / Computer-Nutzung", + "autoApprove": "Auto-Genehmigung", + "browser": "Computerzugriff", "checkpoints": "Kontrollpunkte", "notifications": "Benachrichtigungen", - "contextManagement": "Kontext-Management", + "contextManagement": "Kontext", "terminal": "Terminal", - "advanced": "Erweitert", - "experimental": "Experimentelle Funktionen", + "experimental": "Experimentell", "language": "Sprache", - "about": "Über Roo Code", - "interface": "Oberfläche" + "about": "Über Roo Code" }, "autoApprove": { "description": "Erlaubt Roo, Operationen automatisch ohne Genehmigung durchzuführen. Aktiviere diese Einstellungen nur, wenn du der KI vollständig vertraust und die damit verbundenen Sicherheitsrisiken verstehst.", @@ -41,7 +39,7 @@ "description": "Wenn aktiviert, wird Roo automatisch Verzeichnisinhalte anzeigen und Dateien lesen, ohne dass du auf die Genehmigen-Schaltfläche klicken musst.", "outsideWorkspace": { "label": "Dateien außerhalb des Arbeitsbereichs einbeziehen", - "description": "Agent erlauben, Dateien außerhalb des aktuellen Arbeitsbereichs ohne Genehmigung zu lesen." + "description": "Roo erlauben, Dateien außerhalb des aktuellen Arbeitsbereichs ohne Genehmigung zu lesen." } }, "write": { @@ -50,7 +48,7 @@ "delayLabel": "Verzögerung nach Schreibvorgängen, damit Diagnosefunktionen potenzielle Probleme erkennen können", "outsideWorkspace": { "label": "Dateien außerhalb des Arbeitsbereichs einbeziehen", - "description": "Agent erlauben, Dateien außerhalb des aktuellen Arbeitsbereichs ohne Genehmigung zu erstellen und zu bearbeiten." + "description": "Roo erlauben, Dateien außerhalb des aktuellen Arbeitsbereichs ohne Genehmigung zu erstellen und zu bearbeiten." } }, "browser": { @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "Konfigurationsprofil", "providerDocumentation": "{{provider}}-Dokumentation", + "configProfile": "Konfigurationsprofil", "description": "Speichern Sie verschiedene API-Konfigurationen, um schnell zwischen Anbietern und Einstellungen zu wechseln.", "apiProvider": "API-Anbieter", "model": "Modell", @@ -118,14 +116,22 @@ "headerValue": "Header-Wert", "noCustomHeaders": "Keine benutzerdefinierten Headers definiert. Klicke auf die + Schaltfläche, um einen hinzuzufügen.", "requestyApiKey": "Requesty API-Schlüssel", + "refreshModels": { + "label": "Modelle aktualisieren", + "hint": "Bitte öffne die Einstellungen erneut, um die neuesten Modelle zu sehen." + }, "getRequestyApiKey": "Requesty API-Schlüssel erhalten", "openRouterTransformsText": "Prompts und Nachrichtenketten auf Kontextgröße komprimieren (OpenRouter Transformationen)", "anthropicApiKey": "Anthropic API-Schlüssel", "getAnthropicApiKey": "Anthropic API-Schlüssel erhalten", "anthropicUseAuthToken": "Anthropic API-Schlüssel als Authorization-Header anstelle von X-Api-Key übergeben", + "chutesApiKey": "Chutes API-Schlüssel", + "getChutesApiKey": "Chutes API-Schlüssel erhalten", "deepSeekApiKey": "DeepSeek API-Schlüssel", "getDeepSeekApiKey": "DeepSeek API-Schlüssel erhalten", "geminiApiKey": "Gemini API-Schlüssel", + "getGroqApiKey": "Groq API-Schlüssel erhalten", + "groqApiKey": "Groq API-Schlüssel", "getGeminiApiKey": "Gemini API-Schlüssel erhalten", "openAiApiKey": "OpenAI API-Schlüssel", "openAiBaseUrl": "Basis-URL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Legen Sie eine alternative URL für das Codestral-Modell fest.", "xaiApiKey": "xAI API-Schlüssel", "getXaiApiKey": "xAI API-Schlüssel erhalten", + "litellmApiKey": "LiteLLM API-Schlüssel", + "litellmBaseUrl": "LiteLLM Basis-URL", "awsCredentials": "AWS Anmeldedaten", "awsProfile": "AWS Profil", "awsProfileName": "AWS Profilname", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Browser-Tool aktivieren", - "description": "Wenn aktiviert, kann Roo einen Browser verwenden, um mit Websites zu interagieren, wenn Modelle verwendet werden, die Computer-Nutzung unterstützen." + "description": "Wenn aktiviert, kann Roo einen Browser verwenden, um mit Websites zu interagieren, wenn Modelle verwendet werden, die Computer-Nutzung unterstützen. <0>Mehr erfahren" }, "viewport": { "label": "Viewport-Größe", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Automatische Kontrollpunkte aktivieren", - "description": "Wenn aktiviert, erstellt Roo automatisch Kontrollpunkte während der Aufgabenausführung, was die Überprüfung von Änderungen oder die Rückkehr zu früheren Zuständen erleichtert." + "description": "Wenn aktiviert, erstellt Roo automatisch Kontrollpunkte während der Aufgabenausführung, was die Überprüfung von Änderungen oder die Rückkehr zu früheren Zuständen erleichtert. <0>Mehr erfahren" } }, "notifications": { @@ -298,57 +306,69 @@ "label": "Workspace-Dateien Kontextlimit", "description": "Maximale Anzahl von Dateien, die in den Details des aktuellen Arbeitsverzeichnisses enthalten sein sollen. Höhere Werte bieten mehr Kontext, erhöhen aber den Token-Verbrauch." }, - "pearai-agent-ignore": { - "label": ".pearai-agent-ignore-Dateien in Listen und Suchen anzeigen", - "description": "Wenn aktiviert, werden Dateien, die mit Mustern in .pearai-agent-ignore übereinstimmen, in Listen mit einem Schlosssymbol angezeigt. Wenn deaktiviert, werden diese Dateien vollständig aus Dateilisten und Suchen ausgeblendet." + "rooignore": { + "label": ".rooignore-Dateien in Listen und Suchen anzeigen", + "description": "Wenn aktiviert, werden Dateien, die mit Mustern in .rooignore übereinstimmen, in Listen mit einem Schlosssymbol angezeigt. Wenn deaktiviert, werden diese Dateien vollständig aus Dateilisten und Suchen ausgeblendet." }, "maxReadFile": { "label": "Schwellenwert für automatische Dateilesekürzung", - "description": "Agent liest diese Anzahl von Zeilen, wenn das Modell keine Start-/Endwerte angibt. Wenn diese Zahl kleiner als die Gesamtzahl der Zeilen ist, erstellt Roo einen Zeilennummernindex der Codedefinitionen. Spezialfälle: -1 weist Roo an, die gesamte Datei zu lesen (ohne Indexierung), und 0 weist an, keine Zeilen zu lesen und nur Zeilenindizes für minimalen Kontext bereitzustellen. Niedrigere Werte minimieren die anfängliche Kontextnutzung und ermöglichen präzise nachfolgende Zeilenbereich-Lesungen. Explizite Start-/End-Anfragen sind von dieser Einstellung nicht begrenzt.", + "description": "Roo liest diese Anzahl von Zeilen, wenn das Modell keine Start-/Endwerte angibt. Wenn diese Zahl kleiner als die Gesamtzahl der Zeilen ist, erstellt Roo einen Zeilennummernindex der Codedefinitionen. Spezialfälle: -1 weist Roo an, die gesamte Datei zu lesen (ohne Indexierung), und 0 weist an, keine Zeilen zu lesen und nur Zeilenindizes für minimalen Kontext bereitzustellen. Niedrigere Werte minimieren die anfängliche Kontextnutzung und ermöglichen präzise nachfolgende Zeilenbereich-Lesungen. Explizite Start-/End-Anfragen sind von dieser Einstellung nicht begrenzt.", "lines": "Zeilen", "always_full_read": "Immer die gesamte Datei lesen" } }, "terminal": { + "basic": { + "label": "Terminal-Einstellungen: Grundlegend", + "description": "Grundlegende Terminal-Einstellungen" + }, + "advanced": { + "label": "Terminal-Einstellungen: Erweitert", + "description": "Die folgenden Optionen erfordern möglicherweise einen Terminal-Neustart, um die Einstellung zu übernehmen." + }, "outputLineLimit": { "label": "Terminal-Ausgabelimit", - "description": "Maximale Anzahl von Zeilen, die in der Terminal-Ausgabe bei der Ausführung von Befehlen enthalten sein sollen. Bei Überschreitung werden Zeilen aus der Mitte entfernt, wodurch Token gespart werden." + "description": "Maximale Anzahl von Zeilen, die in der Terminal-Ausgabe bei der Ausführung von Befehlen enthalten sein sollen. Bei Überschreitung werden Zeilen aus der Mitte entfernt, wodurch Token gespart werden. <0>Mehr erfahren" }, "shellIntegrationTimeout": { "label": "Terminal-Shell-Integrationszeit-Limit", - "description": "Maximale Wartezeit für die Shell-Integration, bevor Befehle ausgeführt werden. Für Benutzer mit langen Shell-Startzeiten musst du diesen Wert möglicherweise erhöhen, wenn du Fehler vom Typ \"Shell Integration Unavailable\" im Terminal siehst." + "description": "Maximale Wartezeit für die Shell-Integration, bevor Befehle ausgeführt werden. Für Benutzer mit langen Shell-Startzeiten musst du diesen Wert möglicherweise erhöhen, wenn du Fehler vom Typ \"Shell Integration Unavailable\" im Terminal siehst. <0>Mehr erfahren" }, "shellIntegrationDisabled": { "label": "Terminal-Shell-Integration deaktivieren", - "description": "Aktiviere dies, wenn Terminalbefehle nicht korrekt funktionieren oder du Fehler wie 'Shell Integration Unavailable' siehst. Dies verwendet eine einfachere Methode zur Ausführung von Befehlen und umgeht einige erweiterte Terminalfunktionen." - }, - "compressProgressBar": { - "label": "Fortschrittsbalken-Ausgabe komprimieren", - "description": "Wenn aktiviert, verarbeitet diese Option Terminal-Ausgaben mit Wagenrücklaufzeichen (\\r), um zu simulieren, wie ein echtes Terminal Inhalte anzeigen würde. Dies entfernt Zwischenzustände von Fortschrittsbalken und behält nur den Endzustand bei, wodurch Kontextraum für relevantere Informationen gespart wird." - }, - "zdotdir": { - "label": "ZDOTDIR-Behandlung aktivieren", - "description": "Erstellt bei Aktivierung ein temporäres Verzeichnis für ZDOTDIR, um die zsh-Shell-Integration korrekt zu handhaben. Dies stellt sicher, dass die VSCode-Shell-Integration mit zsh funktioniert und dabei deine zsh-Konfiguration erhalten bleibt. (experimentell)" + "description": "Aktiviere dies, wenn Terminalbefehle nicht korrekt funktionieren oder du Fehler wie 'Shell Integration Unavailable' siehst. Dies verwendet eine einfachere Methode zur Ausführung von Befehlen und umgeht einige erweiterte Terminalfunktionen. <0>Mehr erfahren" }, "commandDelay": { "label": "Terminal-Befehlsverzögerung", - "description": "Verzögerung in Millisekunden, die nach der Befehlsausführung hinzugefügt wird. Die Standardeinstellung von 0 deaktiviert die Verzögerung vollständig. Dies kann dazu beitragen, dass die Befehlsausgabe in Terminals mit Timing-Problemen vollständig erfasst wird. In den meisten Terminals wird dies durch Setzen von `PROMPT_COMMAND='sleep N'` implementiert, und Powershell fügt `start-sleep` am Ende jedes Befehls hinzu. Ursprünglich war dies eine Lösung für VSCode-Bug#237208 und ist möglicherweise nicht mehr erforderlich." + "description": "Verzögerung in Millisekunden, die nach der Befehlsausführung hinzugefügt wird. Die Standardeinstellung von 0 deaktiviert die Verzögerung vollständig. Dies kann dazu beitragen, dass die Befehlsausgabe in Terminals mit Timing-Problemen vollständig erfasst wird. In den meisten Terminals wird dies durch Setzen von `PROMPT_COMMAND='sleep N'` implementiert, und Powershell fügt `start-sleep` am Ende jedes Befehls hinzu. Ursprünglich war dies eine Lösung für VSCode-Bug#237208 und ist möglicherweise nicht mehr erforderlich. <0>Mehr erfahren" + }, + "compressProgressBar": { + "label": "Fortschrittsbalken-Ausgabe komprimieren", + "description": "Wenn aktiviert, verarbeitet diese Option Terminal-Ausgaben mit Wagenrücklaufzeichen (\\r), um zu simulieren, wie ein echtes Terminal Inhalte anzeigen würde. Dies entfernt Zwischenzustände von Fortschrittsbalken und behält nur den Endzustand bei, wodurch Kontextraum für relevantere Informationen gespart wird. <0>Mehr erfahren" }, "powershellCounter": { "label": "PowerShell-Zähler-Workaround aktivieren", - "description": "Wenn aktiviert, fügt einen Zähler zu PowerShell-Befehlen hinzu, um die korrekte Befehlsausführung sicherzustellen. Dies hilft bei PowerShell-Terminals, die Probleme mit der Ausgabeerfassung haben könnten." + "description": "Wenn aktiviert, fügt einen Zähler zu PowerShell-Befehlen hinzu, um die korrekte Befehlsausführung sicherzustellen. Dies hilft bei PowerShell-Terminals, die Probleme mit der Ausgabeerfassung haben könnten. <0>Mehr erfahren" }, "zshClearEolMark": { "label": "ZSH-Zeilenende-Markierung löschen", - "description": "Wenn aktiviert, wird die ZSH-Zeilenende-Markierung durch Setzen von PROMPT_EOL_MARK='' gelöscht. Dies verhindert Probleme bei der Interpretation der Befehlsausgabe, wenn diese mit Sonderzeichen wie '%' endet." + "description": "Wenn aktiviert, wird die ZSH-Zeilenende-Markierung durch Setzen von PROMPT_EOL_MARK='' gelöscht. Dies verhindert Probleme bei der Interpretation der Befehlsausgabe, wenn diese mit Sonderzeichen wie '%' endet. <0>Mehr erfahren" }, "zshOhMy": { "label": "Oh My Zsh-Integration aktivieren", - "description": "Wenn aktiviert, wird ITERM_SHELL_INTEGRATION_INSTALLED=Yes gesetzt, um die Shell-Integrationsfunktionen von Oh My Zsh zu aktivieren. Das Anwenden dieser Einstellung erfordert möglicherweise einen Neustart der IDE. (experimentell)" + "description": "Wenn aktiviert, wird ITERM_SHELL_INTEGRATION_INSTALLED=Yes gesetzt, um die Shell-Integrationsfunktionen von Oh My Zsh zu aktivieren. Das Anwenden dieser Einstellung erfordert möglicherweise einen Neustart der IDE. <0>Mehr erfahren" }, "zshP10k": { "label": "Powerlevel10k-Integration aktivieren", - "description": "Wenn aktiviert, wird POWERLEVEL9K_TERM_SHELL_INTEGRATION=true gesetzt, um die Shell-Integrationsfunktionen von Powerlevel10k zu aktivieren. (experimentell)" + "description": "Wenn aktiviert, wird POWERLEVEL9K_TERM_SHELL_INTEGRATION=true gesetzt, um die Shell-Integrationsfunktionen von Powerlevel10k zu aktivieren. <0>Mehr erfahren" + }, + "zdotdir": { + "label": "ZDOTDIR-Behandlung aktivieren", + "description": "Erstellt bei Aktivierung ein temporäres Verzeichnis für ZDOTDIR, um die zsh-Shell-Integration korrekt zu handhaben. Dies stellt sicher, dass die VSCode-Shell-Integration mit zsh funktioniert und dabei deine zsh-Konfiguration erhalten bleibt. <0>Mehr erfahren" + }, + "inheritEnv": { + "label": "Umgebungsvariablen übernehmen", + "description": "Wenn aktiviert, übernimmt das Terminal Umgebungsvariablen vom übergeordneten VSCode-Prozess, wie z.B. in Benutzerprofilen definierte Shell-Integrationseinstellungen. Dies schaltet direkt die globale VSCode-Einstellung `terminal.integrated.inheritEnv` um. <0>Mehr erfahren" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Kontextfenster intelligent komprimieren", + "description": "Verwendet einen LLM-Aufruf, um das vorherige Gespräch zusammenzufassen, wenn das Kontextfenster der Aufgabe fast voll ist, anstatt alte Nachrichten zu verwerfen. Hinweis: Die Kosten für die Zusammenfassung sind derzeit nicht in den in der Benutzeroberfläche angezeigten API-Kosten enthalten." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Experimentelle einheitliche Diff-Strategie verwenden", "description": "Aktiviert die experimentelle einheitliche Diff-Strategie. Diese Strategie könnte die Anzahl der durch Modellfehler verursachten Wiederholungen reduzieren, kann aber unerwartetes Verhalten oder falsche Bearbeitungen verursachen. Nur aktivieren, wenn du die Risiken verstehst und bereit bist, alle Änderungen sorgfältig zu überprüfen." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Prompt-Caching aktivieren", - "description": "Wenn aktiviert, wird Roo dieses Modell mit aktiviertem Prompt-Caching verwenden, um Kosten zu reduzieren." + "label": "Prompt-Caching deaktivieren", + "description": "Wenn aktiviert, wird Roo für dieses Modell kein Prompt-Caching verwenden." }, "temperature": { "useCustom": "Benutzerdefinierte Temperatur verwenden", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Wenn du Fragen oder Feedback hast, kannst du gerne ein Issue auf github.com/RooVetGit/Roo-Code öffnen oder reddit.com/r/RooCode oder discord.gg/roocode beitreten", - "version": "Agent v{{version}}", "telemetry": { "label": "Anonyme Fehler- und Nutzungsberichte zulassen", "description": "Helfen Sie, Roo Code zu verbessern, indem Sie anonyme Nutzungsdaten und Fehlerberichte senden. Es werden niemals Code, Prompts oder persönliche Informationen gesendet. Weitere Details finden Sie in unserer Datenschutzrichtlinie." diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index f87f1e8f7e1..22b3386b30a 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -1,5 +1,5 @@ { - "greeting": "Welcome to PearAI Agent (Powered by Roo Code / Cline)", + "greeting": "Welcome to Roo Code", "task": { "title": "Task", "seeMore": "See more", @@ -130,47 +130,47 @@ "current": "Current" }, "instructions": { - "wantsToFetch": "Agent wants to fetch detailed instructions to assist with the current task" + "wantsToFetch": "Roo wants to fetch detailed instructions to assist with the current task" }, "fileOperations": { - "wantsToRead": "Agent wants to read this file:", - "wantsToReadOutsideWorkspace": "Agent wants to read this file outside of the workspace:", - "didRead": "Agent read this file:", - "wantsToEdit": "Agent wants to edit this file:", - "wantsToEditOutsideWorkspace": "Agent wants to edit this file outside of the workspace:", - "wantsToCreate": "Agent wants to create a new file:", - "wantsToSearchReplace": "Agent wants to search and replace in this file:", - "didSearchReplace": "Agent performed search and replace on this file:", - "wantsToInsert": "Agent wants to insert content into this file:", - "wantsToInsertWithLineNumber": "Agent wants to insert content into this file at line {{lineNumber}}:", - "wantsToInsertAtEnd": "Agent wants to append content to the end of this file:" + "wantsToRead": "Roo wants to read this file:", + "wantsToReadOutsideWorkspace": "Roo wants to read this file outside of the workspace:", + "didRead": "Roo read this file:", + "wantsToEdit": "Roo wants to edit this file:", + "wantsToEditOutsideWorkspace": "Roo wants to edit this file outside of the workspace:", + "wantsToCreate": "Roo wants to create a new file:", + "wantsToSearchReplace": "Roo wants to search and replace in this file:", + "didSearchReplace": "Roo performed search and replace on this file:", + "wantsToInsert": "Roo wants to insert content into this file:", + "wantsToInsertWithLineNumber": "Roo wants to insert content into this file at line {{lineNumber}}:", + "wantsToInsertAtEnd": "Roo wants to append content to the end of this file:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent wants to view the top level files in this directory:", - "didViewTopLevel": "Agent viewed the top level files in this directory:", - "wantsToViewRecursive": "Agent wants to recursively view all files in this directory:", - "didViewRecursive": "Agent recursively viewed all files in this directory:", - "wantsToViewDefinitions": "Agent wants to view source code definition names used in this directory:", - "didViewDefinitions": "Agent viewed source code definition names used in this directory:", - "wantsToSearch": "Agent wants to search this directory for {{regex}}:", - "didSearch": "Agent searched this directory for {{regex}}:" + "wantsToViewTopLevel": "Roo wants to view the top level files in this directory:", + "didViewTopLevel": "Roo viewed the top level files in this directory:", + "wantsToViewRecursive": "Roo wants to recursively view all files in this directory:", + "didViewRecursive": "Roo recursively viewed all files in this directory:", + "wantsToViewDefinitions": "Roo wants to view source code definition names used in this directory:", + "didViewDefinitions": "Roo viewed source code definition names used in this directory:", + "wantsToSearch": "Roo wants to search this directory for {{regex}}:", + "didSearch": "Roo searched this directory for {{regex}}:" }, "commandOutput": "Command Output", "response": "Response", "arguments": "Arguments", "mcp": { - "wantsToUseTool": "Agent wants to use a tool on the {{serverName}} MCP server:", - "wantsToAccessResource": "Agent wants to access a resource on the {{serverName}} MCP server:" + "wantsToUseTool": "Roo wants to use a tool on the {{serverName}} MCP server:", + "wantsToAccessResource": "Roo wants to access a resource on the {{serverName}} MCP server:" }, "modes": { - "wantsToSwitch": "Agent wants to switch to {{mode}} mode", - "wantsToSwitchWithReason": "Agent wants to switch to {{mode}} mode because: {{reason}}", - "didSwitch": "Agent switched to {{mode}} mode", - "didSwitchWithReason": "Agent switched to {{mode}} mode because: {{reason}}" + "wantsToSwitch": "Roo wants to switch to {{mode}} mode", + "wantsToSwitchWithReason": "Roo wants to switch to {{mode}} mode because: {{reason}}", + "didSwitch": "Roo switched to {{mode}} mode", + "didSwitchWithReason": "Roo switched to {{mode}} mode because: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent wants to create a new subtask in {{mode}} mode:", - "wantsToFinish": "Agent wants to finish this subtask", + "wantsToCreate": "Roo wants to create a new subtask in {{mode}} mode:", + "wantsToFinish": "Roo wants to finish this subtask", "newTaskContent": "Subtask Instructions", "completionContent": "Subtask Completed", "resultContent": "Subtask Results", @@ -178,29 +178,29 @@ "completionInstructions": "Subtask completed! You can review the results and suggest any corrections or next steps. If everything looks good, confirm to return the result to the parent task." }, "questions": { - "hasQuestion": "Agent has a question:" + "hasQuestion": "Roo has a question:" }, "taskCompleted": "Task Completed", "error": "Error", "diffError": { "title": "Edit Unsuccessful" }, - "troubleMessage": "Agent is having trouble...", + "troubleMessage": "Roo is having trouble...", "powershell": { "issues": "It seems like you're having Windows PowerShell issues, please see this" }, "autoApprove": { "title": "Auto-approve:", "none": "None", - "description": "Auto-approve allows PearAI Agent (Powered by Roo Code / Cline) to perform actions without asking for permission. Only enable for actions you fully trust. More detailed configuration available in Settings." + "description": "Auto-approve allows Roo Code to perform actions without asking for permission. Only enable for actions you fully trust. More detailed configuration available in Settings." }, "announcement": { - "title": "🎉 Roo Code 3.15 Released", - "description": "Agent Code 3.15 brings new features and improvements based on your feedback.", + "title": "🎉 Roo Code 3.17 Released", + "description": "Roo Code 3.17 brings powerful new features and improvements based on your feedback.", "whatsNew": "What's New", - "feature1": "Prompt Caching for Vertex: Added prompt caching support for Vertex AI, improving response times and reducing API costs", - "feature2": "Terminal Fallback: Implemented a fallback mechanism when VSCode terminal shell integration fails", - "feature3": "Improved Code Snippets: Enhanced code snippet rendering and interaction in the chat interface", + "feature1": "Implicit Caching for Gemini: Gemini API calls are now automatically cached, reducing API costs", + "feature2": "Smarter Mode Selection: Mode definitions can now include guidance on when each mode should be used, enabling better orchestration", + "feature3": "Intelligent Context Condensing: Intelligently summarizes conversation history when context fills up instead of truncating (enable in Settings -> Experimental)", "hideButton": "Hide announcement", "detailsDiscussLinks": "Get more details and discuss in Discord and Reddit 🚀" }, @@ -212,7 +212,7 @@ "copyToInput": "Copy to input (same as shift + click)" }, "browser": { - "rooWantsToUse": "Agent wants to use the browser:", + "rooWantsToUse": "Roo wants to use the browser:", "consoleLogs": "Console Logs", "noNewLogs": "(No new logs)", "screenshot": "Browser screenshot", @@ -233,10 +233,19 @@ "close": "Close browser" } }, + "codeblock": { + "tooltips": { + "expand": "Expand code block", + "collapse": "Collapse code block", + "enable_wrap": "Enable word wrap", + "disable_wrap": "Disable word wrap", + "copy_code": "Copy code" + } + }, "systemPromptWarning": "WARNING: Custom system prompt override active. This can severely break functionality and cause unpredictable behavior.", "shellIntegration": { "title": "Command Execution Warning", - "description": "Your command is being executed without VSCode terminal shell integration. To suppress this warning you can disable shell integration in the Terminal section of the PearAI Agent (Powered by Roo Code / Cline) settings or troubleshoot VSCode terminal integration using the link below.", + "description": "Your command is being executed without VSCode terminal shell integration. To suppress this warning you can disable shell integration in the Terminal section of the Roo Code settings or troubleshoot VSCode terminal integration using the link below.", "troubleshooting": "Click here for shell integration documentation." } } diff --git a/webview-ui/src/i18n/locales/en/mcp.json b/webview-ui/src/i18n/locales/en/mcp.json index 99ddb85c84a..c7d6f851ff9 100644 --- a/webview-ui/src/i18n/locales/en/mcp.json +++ b/webview-ui/src/i18n/locales/en/mcp.json @@ -1,17 +1,19 @@ { "title": "MCP Servers", "done": "Done", - "description": "The <0>Model Context Protocol enables communication with locally running MCP servers that provide additional tools and resources to extend Agent's capabilities. You can use <1>community-made servers or ask Roo to create new tools specific to your workflow (e.g., \"add a tool that gets the latest npm docs\").", + "description": "Enable the Model Context Protocol (MCP) to let Roo Code use extra tools and services from external servers. This expands what Roo can do for you. <0>Learn More", "enableToggle": { "title": "Enable MCP Servers", - "description": "When enabled, Roo will be able to interact with MCP servers for advanced functionality. If you're not using MCP, you can disable this to reduce Agent's token usage." + "description": "Turn this ON to let Roo use tools from connected MCP servers. This gives Roo more capabilities. If you don't plan to use these extra tools, turn it OFF to help reduce API token costs." }, "enableServerCreation": { "title": "Enable MCP Server Creation", - "description": "When enabled, Roo can help you create new MCP servers via commands like \"add a new tool to...\". If you don't need to create MCP servers you can disable this to reduce Agent's token usage." + "description": "Enable this to have Roo help you build <1>new custom MCP servers. <0>Learn about server creation", + "hint": "Hint: To reduce API token costs, disable this setting when you are not actively asking Roo to create a new MCP server." }, "editGlobalMCP": "Edit Global MCP", "editProjectMCP": "Edit Project MCP", + "learnMoreEditingSettings": "Learn more about editing MCP settings files", "tool": { "alwaysAllow": "Always allow", "parameters": "Parameters", @@ -19,11 +21,14 @@ }, "tabs": { "tools": "Tools", - "resources": "Resources" + "resources": "Resources", + "errors": "Errors" }, "emptyState": { "noTools": "No tools found", - "noResources": "No resources found" + "noResources": "No resources found", + "noLogs": "No logs found", + "noErrors": "No errors found" }, "networkTimeout": { "label": "Network Timeout", diff --git a/webview-ui/src/i18n/locales/en/prompts.json b/webview-ui/src/i18n/locales/en/prompts.json index f01d8c1884d..7359d1701ff 100644 --- a/webview-ui/src/i18n/locales/en/prompts.json +++ b/webview-ui/src/i18n/locales/en/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Create new mode", "editModesConfig": "Edit modes configuration", "editGlobalModes": "Edit Global Modes", - "editProjectModes": "Edit Project Modes (.pearai-agent-ignore)", - "createModeHelpText": "Hit the + to create a new custom mode, or just ask Roo in chat to create one for you!" + "editProjectModes": "Edit Project Modes (.roomodes)", + "createModeHelpText": "Modes are specialized personas that tailor Roo's behavior. <0>Learn about Using Modes or <1>Customizing Modes.", + "selectMode": "Search modes" }, "apiConfiguration": { "title": "API Configuration", @@ -31,18 +32,23 @@ "roleDefinition": { "title": "Role Definition", "resetToDefault": "Reset to default", - "description": "Define Agent's expertise and personality for this mode. This description shapes how Roo presents itself and approaches tasks." + "description": "Define Roo's expertise and personality for this mode. This description shapes how Roo presents itself and approaches tasks." + }, + "whenToUse": { + "title": "When to Use (optional)", + "description": "Describe when this mode should be used. This helps the Orchestrator choose the right mode for a task.", + "resetToDefault": "Reset to default 'When to Use' description" }, "customInstructions": { "title": "Mode-specific Custom Instructions (optional)", "resetToDefault": "Reset to default", "description": "Add behavioral guidelines specific to {{modeName}} mode.", - "loadFromFile": "Custom instructions specific to {{mode}} mode can also be loaded from the .pearai-agent/rules-{{slug}}/ folder in your workspace (.roorules-{{slug}} and .clinerules-{{slug}} are deprecated and will stop working soon)." + "loadFromFile": "Custom instructions specific to {{mode}} mode can also be loaded from the .roo/rules-{{slug}}/ folder in your workspace (.roorules-{{slug}} and .clinerules-{{slug}} are deprecated and will stop working soon)." }, "globalCustomInstructions": { "title": "Custom Instructions for All Modes", - "description": "These instructions apply to all modes. They provide a base set of behaviors that can be enhanced by mode-specific instructions below.\nIf you would like Roo to think and speak in a different language than your editor display language ({{language}}), you can specify it here.", - "loadFromFile": "Instructions can also be loaded from the .pearai-agent/rules/ folder in your workspace (.roorules and .clinerules are deprecated and will stop working soon)." + "description": "These instructions apply to all modes. They provide a base set of behaviors that can be enhanced by mode-specific instructions below. <0>Learn more", + "loadFromFile": "Instructions can also be loaded from the .roo/rules/ folder in your workspace (.roorules and .clinerules are deprecated and will stop working soon)." }, "systemPrompt": { "preview": "Preview System Prompt", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Advanced: Override System Prompt", - "description": "You can completely replace the system prompt for this mode (aside from the role definition and custom instructions) by creating a file at .pearai-agent/system-prompt-{{slug}} in your workspace. This is a very advanced feature that bypasses built-in safeguards and consistency checks (especially around tool usage), so be careful!" + "description": "<2>⚠️ Warning: This advanced feature bypasses safeguards. <1>READ THIS BEFORE USING!Override the default system prompt by creating a file at .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Create New Mode", @@ -122,13 +128,17 @@ "description": "Available in all workspaces" }, "project": { - "label": "Project-specific (.pearai-agent-ignore)", + "label": "Project-specific (.roomodes)", "description": "Only available in this workspace, takes precedence over global" } }, "roleDefinition": { "label": "Role Definition", - "description": "Define Agent's expertise and personality for this mode." + "description": "Define Roo's expertise and personality for this mode." + }, + "whenToUse": { + "label": "When to Use (optional)", + "description": "Provide a clear description of when this mode is most effective and what types of tasks it excels at." }, "tools": { "label": "Available Tools", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 197937bb26e..a312767a8f8 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -23,25 +23,23 @@ "sections": { "providers": "Providers", "autoApprove": "Auto-Approve", - "browser": "Browser / Computer Use", + "browser": "Browser", "checkpoints": "Checkpoints", "notifications": "Notifications", - "contextManagement": "Context Management", + "contextManagement": "Context", "terminal": "Terminal", - "advanced": "Advanced", - "experimental": "Experimental Features", + "experimental": "Experimental", "language": "Language", - "about": "About PearAI Agent (Powered by Roo Code / Cline", - "interface": "Interface" + "about": "About Roo Code" }, "autoApprove": { - "description": "Allow Agent to automatically perform operations without requiring approval. Enable these settings only if you fully trust the AI and understand the associated security risks.", + "description": "Allow Roo to automatically perform operations without requiring approval. Enable these settings only if you fully trust the AI and understand the associated security risks.", "readOnly": { "label": "Read", - "description": "When enabled, Agent will automatically view directory contents and read files without requiring you to click the Approve button.", + "description": "When enabled, Roo will automatically view directory contents and read files without requiring you to click the Approve button.", "outsideWorkspace": { "label": "Include files outside workspace", - "description": "Allow Agent to read files outside the current workspace without requiring approval." + "description": "Allow Roo to read files outside the current workspace without requiring approval." } }, "write": { @@ -50,7 +48,7 @@ "delayLabel": "Delay after writes to allow diagnostics to detect potential problems", "outsideWorkspace": { "label": "Include files outside workspace", - "description": "Allow Agent to create and edit files outside the current workspace without requiring approval." + "description": "Allow Roo to create and edit files outside the current workspace without requiring approval." } }, "browser": { @@ -118,14 +116,22 @@ "headerValue": "Header value", "noCustomHeaders": "No custom headers defined. Click the + button to add one.", "requestyApiKey": "Requesty API Key", + "refreshModels": { + "label": "Refresh Models", + "hint": "Please reopen the settings to see the latest models." + }, "getRequestyApiKey": "Get Requesty API Key", "openRouterTransformsText": "Compress prompts and message chains to the context size (OpenRouter Transforms)", "anthropicApiKey": "Anthropic API Key", "getAnthropicApiKey": "Get Anthropic API Key", "anthropicUseAuthToken": "Pass Anthropic API Key as Authorization header instead of X-Api-Key", + "chutesApiKey": "Chutes API Key", + "getChutesApiKey": "Get Chutes API Key", "deepSeekApiKey": "DeepSeek API Key", "getDeepSeekApiKey": "Get DeepSeek API Key", "geminiApiKey": "Gemini API Key", + "getGroqApiKey": "Get Groq API Key", + "groqApiKey": "Groq API Key", "getGeminiApiKey": "Get Gemini API Key", "openAiApiKey": "OpenAI API Key", "openAiBaseUrl": "Base URL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Set an alternative URL for the Codestral model.", "xaiApiKey": "xAI API Key", "getXaiApiKey": "Get xAI API Key", + "litellmApiKey": "LiteLLM API Key", + "litellmBaseUrl": "LiteLLM Base URL", "awsCredentials": "AWS Credentials", "awsProfile": "AWS Profile", "awsProfileName": "AWS Profile Name", @@ -167,13 +175,13 @@ "draftModelDesc": "Draft model must be from the same model family for speculative decoding to work correctly.", "selectDraftModel": "Select Draft Model", "noModelsFound": "No draft models found. Please ensure LM Studio is running with Server Mode enabled.", - "description": "LM Studio allows you to run models locally on your computer. For instructions on how to get started, see their quickstart guide. You will also need to start LM Studio's local server feature to use it with this extension. Note: Agent uses complex prompts and works best with Claude models. Less capable models may not work as expected." + "description": "LM Studio allows you to run models locally on your computer. For instructions on how to get started, see their quickstart guide. You will also need to start LM Studio's local server feature to use it with this extension. Note: Roo Code uses complex prompts and works best with Claude models. Less capable models may not work as expected." }, "ollama": { "baseUrl": "Base URL (optional)", "modelId": "Model ID", "description": "Ollama allows you to run models locally on your computer. For instructions on how to get started, see their quickstart guide.", - "warning": "Note: Agent uses complex prompts and works best with Claude models. Less capable models may not work as expected." + "warning": "Note: Roo Code uses complex prompts and works best with Claude models. Less capable models may not work as expected." }, "unboundApiKey": "Unbound API Key", "getUnboundApiKey": "Get Unbound API Key", @@ -189,7 +197,7 @@ } }, "customModel": { - "capabilities": "Configure the capabilities and pricing for your custom OpenAI-compatible model. Be careful when specifying the model capabilities, as they can affect how Agent performs.", + "capabilities": "Configure the capabilities and pricing for your custom OpenAI-compatible model. Be careful when specifying the model capabilities, as they can affect how Roo Code performs.", "maxTokens": { "label": "Max Output Tokens", "description": "Maximum number of tokens the model can generate in a response. (Specify -1 to allow the server to set the max tokens.)" @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Enable browser tool", - "description": "When enabled, Agent can use a browser to interact with websites when using models that support computer use." + "description": "When enabled, Roo can use a browser to interact with websites when using models that support computer use. <0>Learn more" }, "viewport": { "label": "Viewport size", @@ -273,18 +281,18 @@ "checkpoints": { "enable": { "label": "Enable automatic checkpoints", - "description": "When enabled, Agent will automatically create checkpoints during task execution, making it easy to review changes or revert to earlier states." + "description": "When enabled, Roo will automatically create checkpoints during task execution, making it easy to review changes or revert to earlier states. <0>Learn more" } }, "notifications": { "sound": { "label": "Enable sound effects", - "description": "When enabled, Agent will play sound effects for notifications and events.", + "description": "When enabled, Roo will play sound effects for notifications and events.", "volumeLabel": "Volume" }, "tts": { "label": "Enable text-to-speech", - "description": "When enabled, Agent will read aloud its responses using text-to-speech.", + "description": "When enabled, Roo will read aloud its responses using text-to-speech.", "speedLabel": "Speed" } }, @@ -298,63 +306,75 @@ "label": "Workspace files context limit", "description": "Maximum number of files to include in current working directory details. Higher values provide more context but increase token usage." }, - "pearai-agent-ignore": { - "label": "Show .pearai-agent-ignore'd files in lists and searches", - "description": "When enabled, files matching patterns in .pearai-agent-ignore will be shown in lists with a lock symbol. When disabled, these files will be completely hidden from file lists and searches." + "rooignore": { + "label": "Show .rooignore'd files in lists and searches", + "description": "When enabled, files matching patterns in .rooignore will be shown in lists with a lock symbol. When disabled, these files will be completely hidden from file lists and searches." }, "maxReadFile": { "label": "File read auto-truncate threshold", - "description": "Agent reads this number of lines when the model omits start/end values. If this number is less than the file's total, Agent generates a line number index of code definitions. Special cases: -1 instructs Agent to read the entire file (without indexing), and 0 instructs it to read no lines and provides line indexes only for minimal context. Lower values minimize initial context usage, enabling precise subsequent line-range reads. Explicit start/end requests are not limited by this setting.", + "description": "Roo reads this number of lines when the model omits start/end values. If this number is less than the file's total, Roo generates a line number index of code definitions. Special cases: -1 instructs Roo to read the entire file (without indexing), and 0 instructs it to read no lines and provides line indexes only for minimal context. Lower values minimize initial context usage, enabling precise subsequent line-range reads. Explicit start/end requests are not limited by this setting.", "lines": "lines", "always_full_read": "Always read entire file" } }, "terminal": { + "basic": { + "label": "Terminal Settings: Basic", + "description": "Basic terminal settings" + }, + "advanced": { + "label": "Terminal Settings: Advanced", + "description": "The following options may require a terminal restart to apply the setting." + }, "outputLineLimit": { "label": "Terminal output limit", - "description": "Maximum number of lines to include in terminal output when executing commands. When exceeded lines will be removed from the middle, saving tokens." + "description": "Maximum number of lines to include in terminal output when executing commands. When exceeded lines will be removed from the middle, saving tokens. <0>Learn more" }, "shellIntegrationTimeout": { "label": "Terminal shell integration timeout", - "description": "Maximum time to wait for shell integration to initialize before executing commands. For users with long shell startup times, this value may need to be increased if you see \"Shell Integration Unavailable\" errors in the terminal." + "description": "Maximum time to wait for shell integration to initialize before executing commands. For users with long shell startup times, this value may need to be increased if you see \"Shell Integration Unavailable\" errors in the terminal. <0>Learn more" }, "shellIntegrationDisabled": { "label": "Disable terminal shell integration", - "description": "Enable this if terminal commands aren't working correctly or you see 'Shell Integration Unavailable' errors. This uses a simpler method to run commands, bypassing some advanced terminal features." + "description": "Enable this if terminal commands aren't working correctly or you see 'Shell Integration Unavailable' errors. This uses a simpler method to run commands, bypassing some advanced terminal features. <0>Learn more" }, "commandDelay": { "label": "Terminal command delay", - "description": "Delay in milliseconds to add after command execution. The default setting of 0 disables the delay completely. This can help ensure command output is fully captured in terminals with timing issues. In most terminals it is implemented by setting `PROMPT_COMMAND='sleep N'` and Powershell appends `start-sleep` to the end of each command. Originally was workaround for VSCode bug#237208 and may not be needed." + "description": "Delay in milliseconds to add after command execution. The default setting of 0 disables the delay completely. This can help ensure command output is fully captured in terminals with timing issues. In most terminals it is implemented by setting `PROMPT_COMMAND='sleep N'` and Powershell appends `start-sleep` to the end of each command. Originally was workaround for VSCode bug#237208 and may not be needed. <0>Learn more" }, "compressProgressBar": { "label": "Compress progress bar output", - "description": "When enabled, processes terminal output with carriage returns (\\r) to simulate how a real terminal would display content. This removes intermediate progress bar states, retaining only the final state, which conserves context space for more relevant information." + "description": "When enabled, processes terminal output with carriage returns (\\r) to simulate how a real terminal would display content. This removes intermediate progress bar states, retaining only the final state, which conserves context space for more relevant information. <0>Learn more" }, "powershellCounter": { "label": "Enable PowerShell counter workaround", - "description": "When enabled, adds a counter to PowerShell commands to ensure proper command execution. This helps with PowerShell terminals that might have issues with command output capture." + "description": "When enabled, adds a counter to PowerShell commands to ensure proper command execution. This helps with PowerShell terminals that might have issues with command output capture. <0>Learn more" }, "zshClearEolMark": { "label": "Clear ZSH EOL mark", - "description": "When enabled, clears the ZSH end-of-line mark by setting PROMPT_EOL_MARK=''. This prevents issues with command output interpretation when output ends with special characters like '%'." + "description": "When enabled, clears the ZSH end-of-line mark by setting PROMPT_EOL_MARK=''. This prevents issues with command output interpretation when output ends with special characters like '%'. <0>Learn more" }, "zshOhMy": { "label": "Enable Oh My Zsh integration", - "description": "When enabled, sets ITERM_SHELL_INTEGRATION_INSTALLED=Yes to enable Oh My Zsh shell integration features. Applying this setting might require restarting the IDE. (experimental)" + "description": "When enabled, sets ITERM_SHELL_INTEGRATION_INSTALLED=Yes to enable Oh My Zsh shell integration features. Applying this setting might require restarting the IDE. <0>Learn more" }, "zshP10k": { "label": "Enable Powerlevel10k integration", - "description": "When enabled, sets POWERLEVEL9K_TERM_SHELL_INTEGRATION=true to enable Powerlevel10k shell integration features. (experimental)" + "description": "When enabled, sets POWERLEVEL9K_TERM_SHELL_INTEGRATION=true to enable Powerlevel10k shell integration features. <0>Learn more" }, "zdotdir": { "label": "Enable ZDOTDIR handling", - "description": "When enabled, creates a temporary directory for ZDOTDIR to handle zsh shell integration properly. This ensures VSCode shell integration works correctly with zsh while preserving your zsh configuration. (experimental)" + "description": "When enabled, creates a temporary directory for ZDOTDIR to handle zsh shell integration properly. This ensures VSCode shell integration works correctly with zsh while preserving your zsh configuration. <0>Learn more" + }, + "inheritEnv": { + "label": "Inherit environment variables", + "description": "When enabled, the terminal will inherit environment variables from VSCode's parent process, such as user-profile-defined shell integration settings. This directly toggles VSCode global setting `terminal.integrated.inheritEnv`. <0>Learn more" } }, "advanced": { "diff": { "label": "Enable editing through diffs", - "description": "When enabled, Agent will be able to edit files more quickly and will automatically reject truncated full-file writes. Works best with the latest Claude 3.7 Sonnet model.", + "description": "When enabled, Roo will be able to edit files more quickly and will automatically reject truncated full-file writes. Works best with the latest Claude 3.7 Sonnet model.", "strategy": { "label": "Diff strategy", "options": { @@ -376,30 +396,34 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Intelligently condense the context window", + "description": "Uses an LLM call to summarize the past conversation when the task's context window is almost full, rather than dropping old messages. Disclaimer: the cost of summarizing is not currently included in the API costs shown in the UI." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Use experimental unified diff strategy", "description": "Enable the experimental unified diff strategy. This strategy might reduce the number of retries caused by model errors but may cause unexpected behavior or incorrect edits. Only enable if you understand the risks and are willing to carefully review all changes." }, "SEARCH_AND_REPLACE": { "name": "Use experimental search and replace tool", - "description": "Enable the experimental search and replace tool, allowing Agent to replace multiple instances of a search term in one request." + "description": "Enable the experimental search and replace tool, allowing Roo to replace multiple instances of a search term in one request." }, "INSERT_BLOCK": { "name": "Use experimental insert content tool", - "description": "Enable the experimental insert content tool, allowing Agent to insert content at specific line numbers without needing to create a diff." + "description": "Enable the experimental insert content tool, allowing Roo to insert content at specific line numbers without needing to create a diff." }, "POWER_STEERING": { "name": "Use experimental \"power steering\" mode", - "description": "When enabled, Agent will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message." + "description": "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message." }, "MULTI_SEARCH_AND_REPLACE": { "name": "Use experimental multi block diff tool", - "description": "When enabled, Agent will use multi block diff tool. This will try to update multiple code blocks in the file in one request." + "description": "When enabled, Roo will use multi block diff tool. This will try to update multiple code blocks in the file in one request." } }, "promptCaching": { - "label": "Enable prompt caching", - "description": "When enabled, Agent will use this model with prompt caching turned on in order to reduce costs." + "label": "Disable prompt caching", + "description": "When checked, Roo will not use prompt caching for this model." }, "temperature": { "useCustom": "Use custom temperature", @@ -430,7 +454,7 @@ } }, "modelPicker": { - "automaticFetch": "The extension automatically fetches the latest list of models available on {{serviceName}}. If you're unsure which model to choose, Agent works best with {{defaultModelId}}. You can also try searching \"free\" for no-cost options currently available.", + "automaticFetch": "The extension automatically fetches the latest list of models available on {{serviceName}}. If you're unsure which model to choose, Roo Code works best with {{defaultModelId}}. You can also try searching \"free\" for no-cost options currently available.", "label": "Model", "searchPlaceholder": "Search", "noMatchFound": "No match found", @@ -440,7 +464,7 @@ "feedback": "If you have any questions or feedback, feel free to open an issue at github.com/RooVetGit/Roo-Code or join reddit.com/r/RooCode or discord.gg/roocode", "telemetry": { "label": "Allow anonymous error and usage reporting", - "description": "Help improve Agent by sending anonymous usage data and error reports. No code, prompts, or personal information is ever sent. See our privacy policy for more details." + "description": "Help improve Roo Code by sending anonymous usage data and error reports. No code, prompts, or personal information is ever sent. See our privacy policy for more details." }, "settings": { "import": "Import", diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 4c5837c4881..9a1e5dcf04a 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -109,7 +109,7 @@ "diffError": { "title": "Edición fallida" }, - "troubleMessage": "Agent está teniendo problemas...", + "troubleMessage": "Roo está teniendo problemas...", "apiRequest": { "title": "Solicitud API", "failed": "Solicitud API falló", @@ -135,47 +135,47 @@ "current": "Actual" }, "instructions": { - "wantsToFetch": "Agent quiere obtener instrucciones detalladas para ayudar con la tarea actual" + "wantsToFetch": "Roo quiere obtener instrucciones detalladas para ayudar con la tarea actual" }, "fileOperations": { - "wantsToRead": "Agent quiere leer este archivo:", - "wantsToReadOutsideWorkspace": "Agent quiere leer este archivo fuera del espacio de trabajo:", - "didRead": "Agent leyó este archivo:", - "wantsToEdit": "Agent quiere editar este archivo:", - "wantsToEditOutsideWorkspace": "Agent quiere editar este archivo fuera del espacio de trabajo:", - "wantsToCreate": "Agent quiere crear un nuevo archivo:", - "wantsToSearchReplace": "Agent quiere realizar búsqueda y reemplazo en este archivo:", - "didSearchReplace": "Agent realizó búsqueda y reemplazo en este archivo:", - "wantsToInsert": "Agent quiere insertar contenido en este archivo:", - "wantsToInsertWithLineNumber": "Agent quiere insertar contenido en este archivo en la línea {{lineNumber}}:", - "wantsToInsertAtEnd": "Agent quiere añadir contenido al final de este archivo:" + "wantsToRead": "Roo quiere leer este archivo:", + "wantsToReadOutsideWorkspace": "Roo quiere leer este archivo fuera del espacio de trabajo:", + "didRead": "Roo leyó este archivo:", + "wantsToEdit": "Roo quiere editar este archivo:", + "wantsToEditOutsideWorkspace": "Roo quiere editar este archivo fuera del espacio de trabajo:", + "wantsToCreate": "Roo quiere crear un nuevo archivo:", + "wantsToSearchReplace": "Roo quiere realizar búsqueda y reemplazo en este archivo:", + "didSearchReplace": "Roo realizó búsqueda y reemplazo en este archivo:", + "wantsToInsert": "Roo quiere insertar contenido en este archivo:", + "wantsToInsertWithLineNumber": "Roo quiere insertar contenido en este archivo en la línea {{lineNumber}}:", + "wantsToInsertAtEnd": "Roo quiere añadir contenido al final de este archivo:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent quiere ver los archivos de nivel superior en este directorio:", - "didViewTopLevel": "Agent vio los archivos de nivel superior en este directorio:", - "wantsToViewRecursive": "Agent quiere ver recursivamente todos los archivos en este directorio:", - "didViewRecursive": "Agent vio recursivamente todos los archivos en este directorio:", - "wantsToViewDefinitions": "Agent quiere ver nombres de definiciones de código fuente utilizados en este directorio:", - "didViewDefinitions": "Agent vio nombres de definiciones de código fuente utilizados en este directorio:", - "wantsToSearch": "Agent quiere buscar en este directorio {{regex}}:", - "didSearch": "Agent buscó en este directorio {{regex}}:" + "wantsToViewTopLevel": "Roo quiere ver los archivos de nivel superior en este directorio:", + "didViewTopLevel": "Roo vio los archivos de nivel superior en este directorio:", + "wantsToViewRecursive": "Roo quiere ver recursivamente todos los archivos en este directorio:", + "didViewRecursive": "Roo vio recursivamente todos los archivos en este directorio:", + "wantsToViewDefinitions": "Roo quiere ver nombres de definiciones de código fuente utilizados en este directorio:", + "didViewDefinitions": "Roo vio nombres de definiciones de código fuente utilizados en este directorio:", + "wantsToSearch": "Roo quiere buscar en este directorio {{regex}}:", + "didSearch": "Roo buscó en este directorio {{regex}}:" }, "commandOutput": "Salida del comando", "response": "Respuesta", "arguments": "Argumentos", "mcp": { - "wantsToUseTool": "Agent quiere usar una herramienta en el servidor MCP {{serverName}}:", - "wantsToAccessResource": "Agent quiere acceder a un recurso en el servidor MCP {{serverName}}:" + "wantsToUseTool": "Roo quiere usar una herramienta en el servidor MCP {{serverName}}:", + "wantsToAccessResource": "Roo quiere acceder a un recurso en el servidor MCP {{serverName}}:" }, "modes": { - "wantsToSwitch": "Agent quiere cambiar a modo {{mode}}", - "wantsToSwitchWithReason": "Agent quiere cambiar a modo {{mode}} porque: {{reason}}", - "didSwitch": "Agent cambió a modo {{mode}}", - "didSwitchWithReason": "Agent cambió a modo {{mode}} porque: {{reason}}" + "wantsToSwitch": "Roo quiere cambiar a modo {{mode}}", + "wantsToSwitchWithReason": "Roo quiere cambiar a modo {{mode}} porque: {{reason}}", + "didSwitch": "Roo cambió a modo {{mode}}", + "didSwitchWithReason": "Roo cambió a modo {{mode}} porque: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent quiere crear una nueva subtarea en modo {{mode}}:", - "wantsToFinish": "Agent quiere finalizar esta subtarea", + "wantsToCreate": "Roo quiere crear una nueva subtarea en modo {{mode}}:", + "wantsToFinish": "Roo quiere finalizar esta subtarea", "newTaskContent": "Instrucciones de la subtarea", "completionContent": "Subtarea completada", "resultContent": "Resultados de la subtarea", @@ -183,7 +183,7 @@ "completionInstructions": "¡Subtarea completada! Puedes revisar los resultados y sugerir correcciones o próximos pasos. Si todo se ve bien, confirma para devolver el resultado a la tarea principal." }, "questions": { - "hasQuestion": "Agent tiene una pregunta:" + "hasQuestion": "Roo tiene una pregunta:" }, "taskCompleted": "Tarea completada", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "Copiar a la entrada (o Shift + clic)" }, "announcement": { - "title": "🎉 Roo Code 3.15 publicado", - "description": "Agent Code 3.15 trae nuevas funcionalidades y mejoras basadas en tus comentarios.", + "title": "🎉 Roo Code 3.17 publicado", + "description": "Roo Code 3.17 trae potentes nuevas funcionalidades y mejoras basadas en tus comentarios.", "whatsNew": "Novedades", - "feature1": "Caché de prompts para Vertex: Se ha añadido soporte de caché de prompts para Vertex AI, mejorando los tiempos de respuesta y reduciendo los costos de API", - "feature2": "Mecanismo alternativo para Terminal: Se ha implementado un mecanismo alternativo cuando falla la integración de shell de terminal de VSCode", - "feature3": "Fragmentos de código mejorados: Renderizado e interacción mejorados de fragmentos de código en la interfaz de chat", + "feature1": "Caché implícito para Gemini: Las llamadas a la API de Gemini ahora se almacenan automáticamente en caché, reduciendo los costos de API", + "feature2": "Selección de modo más inteligente: Las definiciones de modo ahora pueden incluir orientación sobre cuándo debe usarse cada modo, permitiendo una mejor orquestación", + "feature3": "Condensación inteligente de contexto: Resume de forma inteligente el historial de conversación cuando el contexto se llena en lugar de truncarlo (activar en Configuración -> Experimental)", "hideButton": "Ocultar anuncio", "detailsDiscussLinks": "Obtén más detalles y participa en Discord y Reddit 🚀" }, "browser": { - "rooWantsToUse": "Agent quiere usar el navegador:", + "rooWantsToUse": "Roo quiere usar el navegador:", "consoleLogs": "Registros de la consola", "noNewLogs": "(No hay nuevos registros)", "screenshot": "Captura de pantalla del navegador", @@ -233,6 +233,15 @@ "close": "Cerrar navegador" } }, + "codeblock": { + "tooltips": { + "expand": "Expandir bloque de código", + "collapse": "Contraer bloque de código", + "enable_wrap": "Activar ajuste de línea", + "disable_wrap": "Desactivar ajuste de línea", + "copy_code": "Copiar código" + } + }, "systemPromptWarning": "ADVERTENCIA: Anulación de instrucciones del sistema personalizada activa. Esto puede romper gravemente la funcionalidad y causar un comportamiento impredecible.", "shellIntegration": { "title": "Advertencia de ejecución de comandos", diff --git a/webview-ui/src/i18n/locales/es/mcp.json b/webview-ui/src/i18n/locales/es/mcp.json index 6b6c7eb1988..ef98380191c 100644 --- a/webview-ui/src/i18n/locales/es/mcp.json +++ b/webview-ui/src/i18n/locales/es/mcp.json @@ -1,17 +1,19 @@ { "title": "Servidores MCP", - "done": "Listo", - "description": "El <0>Model Context Protocol permite la comunicación con servidores MCP que se ejecutan localmente y proporcionan herramientas y recursos adicionales para extender las capacidades de Roo. Puedes usar <1>servidores creados por la comunidad o pedir a Roo que cree nuevas herramientas específicas para tu flujo de trabajo (por ejemplo, \"añadir una herramienta que obtenga la documentación más reciente de npm\").", + "done": "Hecho", + "description": "Activa el Model Context Protocol (MCP) para que Roo Code pueda usar herramientas y servicios adicionales de servidores externos. Esto amplía lo que Roo puede hacer por ti. <0>Más información", "enableToggle": { - "title": "Habilitar servidores MCP", - "description": "Cuando está habilitado, Roo podrá interactuar con servidores MCP para obtener funcionalidades avanzadas. Si no usas MCP, puedes desactivarlo para reducir el uso de tokens de Roo." + "title": "Activar servidores MCP", + "description": "Actívalo para que Roo pueda usar herramientas de servidores MCP conectados. Esto le da más capacidades a Roo. Si no planeas usar estas herramientas extra, desactívalo para ayudar a reducir los costes de tokens API." }, "enableServerCreation": { - "title": "Habilitar creación de servidores MCP", - "description": "Cuando está habilitado, Roo puede ayudarte a crear nuevos servidores MCP mediante comandos como \"añadir una nueva herramienta para...\". Si no necesitas crear servidores MCP, puedes desactivar esto para reducir el uso de tokens de Roo." + "title": "Activar creación de servidores MCP", + "description": "Actívalo para que Roo te ayude a crear <1>nuevos servidores MCP personalizados. <0>Más información sobre la creación de servidores", + "hint": "Consejo: Para reducir los costes de tokens API, desactiva esta opción cuando no le pidas a Roo que cree un nuevo servidor MCP." }, - "editGlobalMCP": "Editar MCP Global", - "editProjectMCP": "Editar MCP del Proyecto", + "editGlobalMCP": "Editar MCP global", + "editProjectMCP": "Editar MCP del proyecto", + "learnMoreEditingSettings": "Más información sobre cómo editar archivos de configuración MCP", "tool": { "alwaysAllow": "Permitir siempre", "parameters": "Parámetros", @@ -19,11 +21,14 @@ }, "tabs": { "tools": "Herramientas", - "resources": "Recursos" + "resources": "Recursos", + "errors": "Errores" }, "emptyState": { "noTools": "No se encontraron herramientas", - "noResources": "No se encontraron recursos" + "noResources": "No se encontraron recursos", + "noLogs": "No se encontraron registros", + "noErrors": "No se encontraron errores" }, "networkTimeout": { "label": "Tiempo de espera de red", @@ -41,7 +46,7 @@ }, "deleteDialog": { "title": "Eliminar servidor MCP", - "description": "¿Estás seguro de que deseas eliminar el servidor MCP \"{{serverName}}\"? Esta acción no se puede deshacer.", + "description": "¿Seguro que quieres eliminar el servidor MCP \"{{serverName}}\"? Esta acción no se puede deshacer.", "cancel": "Cancelar", "delete": "Eliminar" }, diff --git a/webview-ui/src/i18n/locales/es/prompts.json b/webview-ui/src/i18n/locales/es/prompts.json index a34f97b0deb..bbf8cd71720 100644 --- a/webview-ui/src/i18n/locales/es/prompts.json +++ b/webview-ui/src/i18n/locales/es/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Crear nuevo modo", "editModesConfig": "Editar configuración de modos", "editGlobalModes": "Editar modos globales", - "editProjectModes": "Editar modos del proyecto (.pearai-agent-ignore)", - "createModeHelpText": "¡Haz clic en + para crear un nuevo modo personalizado, o simplemente pídele a Roo en el chat que te cree uno!" + "editProjectModes": "Editar modos del proyecto (.roomodes)", + "createModeHelpText": "Los modos son personas especializadas que adaptan el comportamiento de Roo. <0>Aprende sobre el uso de modos o <1>Personalización de modos.", + "selectMode": "Buscar modos" }, "apiConfiguration": { "title": "Configuración de API", @@ -33,16 +34,21 @@ "resetToDefault": "Restablecer a valores predeterminados", "description": "Define la experiencia y personalidad de Roo para este modo. Esta descripción determina cómo Roo se presenta y aborda las tareas." }, + "whenToUse": { + "title": "Cuándo usar (opcional)", + "description": "Describe cuándo se debe usar este modo. Esto ayuda al Orchestrator a elegir el modo correcto para una tarea.", + "resetToDefault": "Restablecer a valores predeterminados la descripción 'Cuándo usar'" + }, "customInstructions": { "title": "Instrucciones personalizadas para el modo (opcional)", "resetToDefault": "Restablecer a valores predeterminados", "description": "Agrega directrices de comportamiento específicas para el modo {{modeName}}.", - "loadFromFile": "Las instrucciones personalizadas para el modo {{mode}} también se pueden cargar desde la carpeta .pearai-agent/rules-{{slug}}/ en tu espacio de trabajo (.roorules-{{slug}} y .clinerules-{{slug}} están obsoletos y dejarán de funcionar pronto)." + "loadFromFile": "Las instrucciones personalizadas para el modo {{mode}} también se pueden cargar desde la carpeta .roo/rules-{{slug}}/ en tu espacio de trabajo (.roorules-{{slug}} y .clinerules-{{slug}} están obsoletos y dejarán de funcionar pronto)." }, "globalCustomInstructions": { "title": "Instrucciones personalizadas para todos los modos", - "description": "Estas instrucciones se aplican a todos los modos. Proporcionan un conjunto base de comportamientos que pueden ser mejorados por instrucciones específicas de cada modo.\nSi quieres que Roo piense y hable en un idioma diferente al idioma de visualización de tu editor ({{language}}), puedes especificarlo aquí.", - "loadFromFile": "Las instrucciones también se pueden cargar desde la carpeta .pearai-agent/rules/ en tu espacio de trabajo (.roorules y .clinerules están obsoletos y dejarán de funcionar pronto)." + "description": "Estas instrucciones se aplican a todos los modos. Proporcionan un conjunto base de comportamientos que pueden ser mejorados por instrucciones específicas de cada modo a continuación. <0>Más información", + "loadFromFile": "Las instrucciones también se pueden cargar desde la carpeta .roo/rules/ en tu espacio de trabajo (.roorules y .clinerules están obsoletos y dejarán de funcionar pronto)." }, "systemPrompt": { "preview": "Vista previa de la solicitud del sistema", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Avanzado: Anular solicitud del sistema", - "description": "Puedes reemplazar completamente la solicitud del sistema para este modo (aparte de la definición de rol e instrucciones personalizadas) creando un archivo en .pearai-agent/system-prompt-{{slug}} en tu espacio de trabajo. ¡Esta es una función muy avanzada que omite las salvaguardas integradas y las verificaciones de consistencia (especialmente en torno al uso de herramientas), así que ten cuidado!" + "description": "<2>⚠️ Advertencia: Esta función avanzada omite las medidas de seguridad. <1>¡LEE ESTO ANTES DE USAR!Anula la solicitud del sistema predeterminada creando un archivo en .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Crear nuevo modo", @@ -122,7 +128,7 @@ "description": "Disponible en todos los espacios de trabajo" }, "project": { - "label": "Específico del proyecto (.pearai-agent-ignore)", + "label": "Específico del proyecto (.roomodes)", "description": "Solo disponible en este espacio de trabajo, tiene prioridad sobre el global" } }, @@ -130,6 +136,10 @@ "label": "Definición de rol", "description": "Define la experiencia y personalidad de Roo para este modo." }, + "whenToUse": { + "label": "Cuándo usar (opcional)", + "description": "Proporciona una descripción clara de cuándo este modo es más efectivo y para qué tipos de tareas es más adecuado." + }, "tools": { "label": "Herramientas disponibles", "description": "Selecciona qué herramientas puede usar este modo." diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 74b2b9fd0f6..4230c5e75f0 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Proveedores", - "autoApprove": "Aprobación automática", - "browser": "Navegador / Uso del ordenador", + "autoApprove": "Auto-aprobación", + "browser": "Acceso al ordenador", "checkpoints": "Puntos de control", "notifications": "Notificaciones", - "contextManagement": "Gestión de contexto", + "contextManagement": "Contexto", "terminal": "Terminal", - "advanced": "Avanzado", - "experimental": "Funciones experimentales", + "experimental": "Experimental", "language": "Idioma", - "about": "Acerca de Roo Code", - "interface": "Interfaz" + "about": "Acerca de Roo Code" }, "autoApprove": { "description": "Permitir que Roo realice operaciones automáticamente sin requerir aprobación. Habilite esta configuración solo si confía plenamente en la IA y comprende los riesgos de seguridad asociados.", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "Perfil de configuración", "providerDocumentation": "Documentación de {{provider}}", + "configProfile": "Perfil de configuración", "description": "Guarde diferentes configuraciones de API para cambiar rápidamente entre proveedores y ajustes.", "apiProvider": "Proveedor de API", "model": "Modelo", @@ -118,14 +116,22 @@ "headerValue": "Valor del encabezado", "noCustomHeaders": "No hay encabezados personalizados definidos. Haga clic en el botón + para añadir uno.", "requestyApiKey": "Clave API de Requesty", + "refreshModels": { + "label": "Actualizar modelos", + "hint": "Por favor, vuelve a abrir la configuración para ver los modelos más recientes." + }, "getRequestyApiKey": "Obtener clave API de Requesty", "openRouterTransformsText": "Comprimir prompts y cadenas de mensajes al tamaño del contexto (Transformaciones de OpenRouter)", "anthropicApiKey": "Clave API de Anthropic", "getAnthropicApiKey": "Obtener clave API de Anthropic", "anthropicUseAuthToken": "Pasar la clave API de Anthropic como encabezado de autorización en lugar de X-Api-Key", + "chutesApiKey": "Clave API de Chutes", + "getChutesApiKey": "Obtener clave API de Chutes", "deepSeekApiKey": "Clave API de DeepSeek", "getDeepSeekApiKey": "Obtener clave API de DeepSeek", "geminiApiKey": "Clave API de Gemini", + "getGroqApiKey": "Obtener clave API de Groq", + "groqApiKey": "Clave API de Groq", "getGeminiApiKey": "Obtener clave API de Gemini", "openAiApiKey": "Clave API de OpenAI", "openAiBaseUrl": "URL base", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Establecer una URL alternativa para el modelo Codestral.", "xaiApiKey": "Clave API de xAI", "getXaiApiKey": "Obtener clave API de xAI", + "litellmApiKey": "Clave API de LiteLLM", + "litellmBaseUrl": "URL base de LiteLLM", "awsCredentials": "Credenciales de AWS", "awsProfile": "Perfil de AWS", "awsProfileName": "Nombre del perfil de AWS", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Habilitar herramienta de navegador", - "description": "Cuando está habilitado, Roo puede usar un navegador para interactuar con sitios web cuando se utilizan modelos que admiten el uso del ordenador." + "description": "Cuando está habilitado, Roo puede usar un navegador para interactuar con sitios web cuando se utilizan modelos que admiten el uso del ordenador. <0>Más información" }, "viewport": { "label": "Tamaño del viewport", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Habilitar puntos de control automáticos", - "description": "Cuando está habilitado, Roo creará automáticamente puntos de control durante la ejecución de tareas, facilitando la revisión de cambios o la reversión a estados anteriores." + "description": "Cuando está habilitado, Roo creará automáticamente puntos de control durante la ejecución de tareas, facilitando la revisión de cambios o la reversión a estados anteriores. <0>Más información" } }, "notifications": { @@ -298,57 +306,69 @@ "label": "Límite de contexto de archivos del espacio de trabajo", "description": "Número máximo de archivos a incluir en los detalles del directorio de trabajo actual. Valores más altos proporcionan más contexto pero aumentan el uso de token." }, - "pearai-agent-ignore": { - "label": "Mostrar archivos .pearai-agent-ignore en listas y búsquedas", - "description": "Cuando está habilitado, los archivos que coinciden con los patrones en .pearai-agent-ignore se mostrarán en listas con un símbolo de candado. Cuando está deshabilitado, estos archivos se ocultarán completamente de las listas de archivos y búsquedas." + "rooignore": { + "label": "Mostrar archivos .rooignore en listas y búsquedas", + "description": "Cuando está habilitado, los archivos que coinciden con los patrones en .rooignore se mostrarán en listas con un símbolo de candado. Cuando está deshabilitado, estos archivos se ocultarán completamente de las listas de archivos y búsquedas." }, "maxReadFile": { "label": "Umbral de auto-truncado de lectura de archivos", - "description": "Agent lee este número de líneas cuando el modelo omite valores de inicio/fin. Si este número es menor que el total del archivo, Roo genera un índice de números de línea de las definiciones de código. Casos especiales: -1 indica a Roo que lea el archivo completo (sin indexación), y 0 indica que no lea líneas y proporcione solo índices de línea para un contexto mínimo. Valores más bajos minimizan el uso inicial de contexto, permitiendo lecturas posteriores de rangos de líneas precisos. Las solicitudes con inicio/fin explícitos no están limitadas por esta configuración.", + "description": "Roo lee este número de líneas cuando el modelo omite valores de inicio/fin. Si este número es menor que el total del archivo, Roo genera un índice de números de línea de las definiciones de código. Casos especiales: -1 indica a Roo que lea el archivo completo (sin indexación), y 0 indica que no lea líneas y proporcione solo índices de línea para un contexto mínimo. Valores más bajos minimizan el uso inicial de contexto, permitiendo lecturas posteriores de rangos de líneas precisos. Las solicitudes con inicio/fin explícitos no están limitadas por esta configuración.", "lines": "líneas", "always_full_read": "Siempre leer el archivo completo" } }, "terminal": { + "basic": { + "label": "Configuración del terminal: Básica", + "description": "Configuración básica del terminal" + }, + "advanced": { + "label": "Configuración del terminal: Avanzada", + "description": "Las siguientes opciones pueden requerir reiniciar el terminal para aplicar la configuración." + }, "outputLineLimit": { "label": "Límite de salida de terminal", - "description": "Número máximo de líneas a incluir en la salida del terminal al ejecutar comandos. Cuando se excede, se eliminarán líneas del medio, ahorrando token." + "description": "Número máximo de líneas a incluir en la salida del terminal al ejecutar comandos. Cuando se excede, se eliminarán líneas del medio, ahorrando token. <0>Más información" }, "shellIntegrationTimeout": { "label": "Tiempo de espera de integración del shell del terminal", - "description": "Tiempo máximo de espera para la inicialización de la integración del shell antes de ejecutar comandos. Para usuarios con tiempos de inicio de shell largos, este valor puede necesitar ser aumentado si ve errores \"Shell Integration Unavailable\" en el terminal." + "description": "Tiempo máximo de espera para la inicialización de la integración del shell antes de ejecutar comandos. Para usuarios con tiempos de inicio de shell largos, este valor puede necesitar ser aumentado si ve errores \"Shell Integration Unavailable\" en el terminal. <0>Más información" }, "shellIntegrationDisabled": { "label": "Desactivar la integración del shell del terminal", - "description": "Activa esto si los comandos del terminal no funcionan correctamente o si ves errores de 'Shell Integration Unavailable'. Esto utiliza un método más simple para ejecutar comandos, omitiendo algunas funciones avanzadas del terminal." - }, - "compressProgressBar": { - "label": "Comprimir salida de barras de progreso", - "description": "Cuando está habilitado, procesa la salida del terminal con retornos de carro (\\r) para simular cómo un terminal real mostraría el contenido. Esto elimina los estados intermedios de las barras de progreso, conservando solo el estado final, lo que ahorra espacio de contexto para información más relevante." - }, - "zdotdir": { - "label": "Habilitar gestión de ZDOTDIR", - "description": "Cuando está habilitado, crea un directorio temporal para ZDOTDIR para manejar correctamente la integración del shell zsh. Esto asegura que la integración del shell de VSCode funcione correctamente con zsh mientras preserva tu configuración de zsh. (experimental)" + "description": "Activa esto si los comandos del terminal no funcionan correctamente o si ves errores de 'Shell Integration Unavailable'. Esto utiliza un método más simple para ejecutar comandos, omitiendo algunas funciones avanzadas del terminal. <0>Más información" }, "commandDelay": { "label": "Retraso de comando del terminal", - "description": "Retraso en milisegundos para añadir después de la ejecución del comando. La configuración predeterminada de 0 desactiva completamente el retraso. Esto puede ayudar a asegurar que la salida del comando se capture completamente en terminales con problemas de temporización. En la mayoría de terminales se implementa estableciendo `PROMPT_COMMAND='sleep N'` y Powershell añade `start-sleep` al final de cada comando. Originalmente era una solución para el error VSCode#237208 y puede no ser necesario." + "description": "Retraso en milisegundos para añadir después de la ejecución del comando. La configuración predeterminada de 0 desactiva completamente el retraso. Esto puede ayudar a asegurar que la salida del comando se capture completamente en terminales con problemas de temporización. En la mayoría de terminales se implementa estableciendo `PROMPT_COMMAND='sleep N'` y Powershell añade `start-sleep` al final de cada comando. Originalmente era una solución para el error VSCode#237208 y puede no ser necesario. <0>Más información" + }, + "compressProgressBar": { + "label": "Comprimir salida de barras de progreso", + "description": "Cuando está habilitado, procesa la salida del terminal con retornos de carro (\\r) para simular cómo un terminal real mostraría el contenido. Esto elimina los estados intermedios de las barras de progreso, conservando solo el estado final, lo que ahorra espacio de contexto para información más relevante. <0>Más información" }, "powershellCounter": { "label": "Habilitar solución temporal del contador de PowerShell", - "description": "Cuando está habilitado, agrega un contador a los comandos de PowerShell para garantizar la ejecución correcta de los comandos. Esto ayuda con las terminales PowerShell que pueden tener problemas con la captura de salida de comandos." + "description": "Cuando está habilitado, agrega un contador a los comandos de PowerShell para garantizar la ejecución correcta de los comandos. Esto ayuda con las terminales PowerShell que pueden tener problemas con la captura de salida de comandos. <0>Más información" }, "zshClearEolMark": { "label": "Limpiar marca de fin de línea de ZSH", - "description": "Cuando está habilitado, limpia la marca de fin de línea de ZSH estableciendo PROMPT_EOL_MARK=''. Esto evita problemas con la interpretación de la salida de comandos cuando termina con caracteres especiales como '%'." + "description": "Cuando está habilitado, limpia la marca de fin de línea de ZSH estableciendo PROMPT_EOL_MARK=''. Esto evita problemas con la interpretación de la salida de comandos cuando termina con caracteres especiales como '%'. <0>Más información" }, "zshOhMy": { "label": "Habilitar integración Oh My Zsh", - "description": "Cuando está habilitado, establece ITERM_SHELL_INTEGRATION_INSTALLED=Yes para habilitar las características de integración del shell Oh My Zsh. Aplicar esta configuración puede requerir reiniciar el IDE. (experimental)" + "description": "Cuando está habilitado, establece ITERM_SHELL_INTEGRATION_INSTALLED=Yes para habilitar las características de integración del shell Oh My Zsh. Aplicar esta configuración puede requerir reiniciar el IDE. <0>Más información" }, "zshP10k": { "label": "Habilitar integración Powerlevel10k", - "description": "Cuando está habilitado, establece POWERLEVEL9K_TERM_SHELL_INTEGRATION=true para habilitar las características de integración del shell Powerlevel10k. (experimental)" + "description": "Cuando está habilitado, establece POWERLEVEL9K_TERM_SHELL_INTEGRATION=true para habilitar las características de integración del shell Powerlevel10k. <0>Más información" + }, + "zdotdir": { + "label": "Habilitar gestión de ZDOTDIR", + "description": "Cuando está habilitado, crea un directorio temporal para ZDOTDIR para manejar correctamente la integración del shell zsh. Esto asegura que la integración del shell de VSCode funcione correctamente con zsh mientras preserva tu configuración de zsh. <0>Más información" + }, + "inheritEnv": { + "label": "Heredar variables de entorno", + "description": "Cuando está habilitado, el terminal hereda las variables de entorno del proceso padre de VSCode, como la configuración de integración del shell definida en el perfil del usuario. Esto alterna directamente la configuración global de VSCode `terminal.integrated.inheritEnv`. <0>Más información" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Condensar inteligentemente la ventana de contexto", + "description": "Utiliza una llamada LLM para resumir la conversación anterior cuando la ventana de contexto de la tarea está casi llena, en lugar de eliminar mensajes antiguos. Aviso: el costo de resumir actualmente no está incluido en los costos de API mostrados en la interfaz." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Usar estrategia de diff unificada experimental", "description": "Habilitar la estrategia de diff unificada experimental. Esta estrategia podría reducir el número de reintentos causados por errores del modelo, pero puede causar comportamientos inesperados o ediciones incorrectas. Habilítela solo si comprende los riesgos y está dispuesto a revisar cuidadosamente todos los cambios." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Habilitar caché de prompts", - "description": "Cuando está habilitado, Roo usará este modelo con el caché de prompts activado para reducir costos." + "label": "Desactivar caché de prompts", + "description": "Cuando está marcado, Roo no utilizará el caché de prompts para este modelo." }, "temperature": { "useCustom": "Usar temperatura personalizada", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Si tiene alguna pregunta o comentario, no dude en abrir un issue en github.com/RooVetGit/Roo-Code o unirse a reddit.com/r/RooCode o discord.gg/roocode", - "version": "Agent v{{version}}", "telemetry": { "label": "Permitir informes anónimos de errores y uso", "description": "Ayude a mejorar Roo Code enviando datos de uso anónimos e informes de errores. Nunca se envía código, prompts o información personal. Consulte nuestra política de privacidad para más detalles." diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 1f57c598471..2e39eb0a827 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -109,7 +109,7 @@ "diffError": { "title": "Modification échouée" }, - "troubleMessage": "Agent rencontre des difficultés...", + "troubleMessage": "Roo rencontre des difficultés...", "apiRequest": { "title": "Requête API", "failed": "Échec de la requête API", @@ -135,47 +135,47 @@ "current": "Actuel" }, "fileOperations": { - "wantsToRead": "Agent veut lire ce fichier :", - "wantsToReadOutsideWorkspace": "Agent veut lire ce fichier en dehors de l'espace de travail :", - "didRead": "Agent a lu ce fichier :", - "wantsToEdit": "Agent veut éditer ce fichier :", - "wantsToEditOutsideWorkspace": "Agent veut éditer ce fichier en dehors de l'espace de travail :", - "wantsToCreate": "Agent veut créer un nouveau fichier :", - "wantsToSearchReplace": "Agent veut effectuer une recherche et remplacement sur ce fichier :", - "didSearchReplace": "Agent a effectué une recherche et remplacement sur ce fichier :", - "wantsToInsert": "Agent veut insérer du contenu dans ce fichier :", - "wantsToInsertWithLineNumber": "Agent veut insérer du contenu dans ce fichier à la ligne {{lineNumber}} :", - "wantsToInsertAtEnd": "Agent veut ajouter du contenu à la fin de ce fichier :" + "wantsToRead": "Roo veut lire ce fichier :", + "wantsToReadOutsideWorkspace": "Roo veut lire ce fichier en dehors de l'espace de travail :", + "didRead": "Roo a lu ce fichier :", + "wantsToEdit": "Roo veut éditer ce fichier :", + "wantsToEditOutsideWorkspace": "Roo veut éditer ce fichier en dehors de l'espace de travail :", + "wantsToCreate": "Roo veut créer un nouveau fichier :", + "wantsToSearchReplace": "Roo veut effectuer une recherche et remplacement sur ce fichier :", + "didSearchReplace": "Roo a effectué une recherche et remplacement sur ce fichier :", + "wantsToInsert": "Roo veut insérer du contenu dans ce fichier :", + "wantsToInsertWithLineNumber": "Roo veut insérer du contenu dans ce fichier à la ligne {{lineNumber}} :", + "wantsToInsertAtEnd": "Roo veut ajouter du contenu à la fin de ce fichier :" }, "instructions": { - "wantsToFetch": "Agent veut récupérer des instructions détaillées pour aider à la tâche actuelle" + "wantsToFetch": "Roo veut récupérer des instructions détaillées pour aider à la tâche actuelle" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent veut voir les fichiers de premier niveau dans ce répertoire :", - "didViewTopLevel": "Agent a vu les fichiers de premier niveau dans ce répertoire :", - "wantsToViewRecursive": "Agent veut voir récursivement tous les fichiers dans ce répertoire :", - "didViewRecursive": "Agent a vu récursivement tous les fichiers dans ce répertoire :", - "wantsToViewDefinitions": "Agent veut voir les noms de définitions de code source utilisés dans ce répertoire :", - "didViewDefinitions": "Agent a vu les noms de définitions de code source utilisés dans ce répertoire :", - "wantsToSearch": "Agent veut rechercher dans ce répertoire {{regex}} :", - "didSearch": "Agent a recherché dans ce répertoire {{regex}} :" + "wantsToViewTopLevel": "Roo veut voir les fichiers de premier niveau dans ce répertoire :", + "didViewTopLevel": "Roo a vu les fichiers de premier niveau dans ce répertoire :", + "wantsToViewRecursive": "Roo veut voir récursivement tous les fichiers dans ce répertoire :", + "didViewRecursive": "Roo a vu récursivement tous les fichiers dans ce répertoire :", + "wantsToViewDefinitions": "Roo veut voir les noms de définitions de code source utilisés dans ce répertoire :", + "didViewDefinitions": "Roo a vu les noms de définitions de code source utilisés dans ce répertoire :", + "wantsToSearch": "Roo veut rechercher dans ce répertoire {{regex}} :", + "didSearch": "Roo a recherché dans ce répertoire {{regex}} :" }, "commandOutput": "Sortie de commande", "response": "Réponse", "arguments": "Arguments", "mcp": { - "wantsToUseTool": "Agent veut utiliser un outil sur le serveur MCP {{serverName}} :", - "wantsToAccessResource": "Agent veut accéder à une ressource sur le serveur MCP {{serverName}} :" + "wantsToUseTool": "Roo veut utiliser un outil sur le serveur MCP {{serverName}} :", + "wantsToAccessResource": "Roo veut accéder à une ressource sur le serveur MCP {{serverName}} :" }, "modes": { - "wantsToSwitch": "Agent veut passer au mode {{mode}}", - "wantsToSwitchWithReason": "Agent veut passer au mode {{mode}} car : {{reason}}", - "didSwitch": "Agent est passé au mode {{mode}}", - "didSwitchWithReason": "Agent est passé au mode {{mode}} car : {{reason}}" + "wantsToSwitch": "Roo veut passer au mode {{mode}}", + "wantsToSwitchWithReason": "Roo veut passer au mode {{mode}} car : {{reason}}", + "didSwitch": "Roo est passé au mode {{mode}}", + "didSwitchWithReason": "Roo est passé au mode {{mode}} car : {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent veut créer une nouvelle sous-tâche en mode {{mode}} :", - "wantsToFinish": "Agent veut terminer cette sous-tâche", + "wantsToCreate": "Roo veut créer une nouvelle sous-tâche en mode {{mode}} :", + "wantsToFinish": "Roo veut terminer cette sous-tâche", "newTaskContent": "Instructions de la sous-tâche", "completionContent": "Sous-tâche terminée", "resultContent": "Résultats de la sous-tâche", @@ -183,7 +183,7 @@ "completionInstructions": "Sous-tâche terminée ! Vous pouvez examiner les résultats et suggérer des corrections ou les prochaines étapes. Si tout semble bon, confirmez pour retourner le résultat à la tâche parente." }, "questions": { - "hasQuestion": "Agent a une question :" + "hasQuestion": "Roo a une question :" }, "taskCompleted": "Tâche terminée", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "Copier vers l'entrée (ou Shift + clic)" }, "announcement": { - "title": "🎉 Roo Code 3.15 est sortie", - "description": "Agent Code 3.15 apporte de nouvelles fonctionnalités et améliorations basées sur vos retours.", + "title": "🎉 Roo Code 3.17 est sortie", + "description": "Roo Code 3.17 apporte de puissantes nouvelles fonctionnalités et améliorations basées sur vos retours.", "whatsNew": "Quoi de neuf", - "feature1": "Mise en cache des prompts pour Vertex : Ajout du support de mise en cache des prompts pour Vertex AI, améliorant les temps de réponse et réduisant les coûts API", - "feature2": "Solution de secours pour Terminal : Implémentation d'un mécanisme de secours lorsque l'intégration shell du terminal VSCode échoue", - "feature3": "Extraits de code améliorés : Rendu et interaction améliorés des extraits de code dans l'interface de chat", + "feature1": "Mise en cache implicite pour Gemini : Les appels à l'API Gemini sont désormais automatiquement mis en cache, réduisant les coûts d'API", + "feature2": "Sélection de mode plus intelligente : Les définitions de mode peuvent maintenant inclure des indications sur quand chaque mode doit être utilisé, permettant une meilleure orchestration", + "feature3": "Condensation intelligente du contexte : Résume intelligemment l'historique des conversations lorsque le contexte se remplit au lieu de le tronquer (activer dans Paramètres -> Expérimental)", "hideButton": "Masquer l'annonce", "detailsDiscussLinks": "Obtenez plus de détails et participez aux discussions sur Discord et Reddit 🚀" }, "browser": { - "rooWantsToUse": "Agent veut utiliser le navigateur :", + "rooWantsToUse": "Roo veut utiliser le navigateur :", "consoleLogs": "Journaux de console", "noNewLogs": "(Pas de nouveaux journaux)", "screenshot": "Capture d'écran du navigateur", @@ -233,6 +233,15 @@ "close": "Fermer le navigateur" } }, + "codeblock": { + "tooltips": { + "expand": "Développer le bloc de code", + "collapse": "Réduire le bloc de code", + "enable_wrap": "Activer le retour à la ligne", + "disable_wrap": "Désactiver le retour à la ligne", + "copy_code": "Copier le code" + } + }, "systemPromptWarning": "AVERTISSEMENT : Remplacement d'instructions système personnalisées actif. Cela peut gravement perturber la fonctionnalité et provoquer un comportement imprévisible.", "shellIntegration": { "title": "Avertissement d'exécution de commande", diff --git a/webview-ui/src/i18n/locales/fr/mcp.json b/webview-ui/src/i18n/locales/fr/mcp.json index 2f3195067ca..6aeb888c16f 100644 --- a/webview-ui/src/i18n/locales/fr/mcp.json +++ b/webview-ui/src/i18n/locales/fr/mcp.json @@ -1,17 +1,19 @@ { "title": "Serveurs MCP", "done": "Terminé", - "description": "Le <0>Model Context Protocol permet la communication avec des serveurs MCP exécutés localement qui fournissent des outils et des ressources supplémentaires pour étendre les capacités de Roo. Vous pouvez utiliser <1>des serveurs créés par la communauté ou demander à Roo de créer de nouveaux outils spécifiques à votre flux de travail (par exemple, \"ajouter un outil qui récupère la dernière documentation npm\").", + "description": "Active le Model Context Protocol (MCP) pour permettre à Roo Code d'utiliser des outils et services supplémentaires depuis des serveurs externes. Cela élargit ce que Roo peut faire pour toi. <0>En savoir plus", "enableToggle": { "title": "Activer les serveurs MCP", - "description": "Lorsqu'activé, Roo pourra interagir avec les serveurs MCP pour des fonctionnalités avancées. Si vous n'utilisez pas MCP, vous pouvez désactiver cette option pour réduire l'utilisation de tokens par Roo." + "description": "Active cette option pour que Roo puisse utiliser des outils provenant de serveurs MCP connectés. Cela donne plus de capacités à Roo. Si tu ne comptes pas utiliser ces outils supplémentaires, désactive-la pour réduire les coûts de tokens API." }, "enableServerCreation": { "title": "Activer la création de serveurs MCP", - "description": "Lorsqu'activé, Roo peut vous aider à créer de nouveaux serveurs MCP via des commandes comme \"ajouter un nouvel outil pour...\". Si vous n'avez pas besoin de créer des serveurs MCP, vous pouvez désactiver cette option pour réduire l'utilisation de tokens par Roo." + "description": "Active cette option pour que Roo t'aide à créer de <1>nouveaux serveurs MCP personnalisés. <0>En savoir plus sur la création de serveurs", + "hint": "Astuce : Pour réduire les coûts de tokens API, désactive cette option quand tu ne demandes pas à Roo de créer un nouveau serveur MCP." }, - "editGlobalMCP": "Modifier MCP Global", - "editProjectMCP": "Modifier MCP du Projet", + "editGlobalMCP": "Modifier le MCP global", + "editProjectMCP": "Modifier le MCP du projet", + "learnMoreEditingSettings": "En savoir plus sur la modification des fichiers de configuration MCP", "tool": { "alwaysAllow": "Toujours autoriser", "parameters": "Paramètres", @@ -19,15 +21,18 @@ }, "tabs": { "tools": "Outils", - "resources": "Ressources" + "resources": "Ressources", + "errors": "Erreurs" }, "emptyState": { "noTools": "Aucun outil trouvé", - "noResources": "Aucune ressource trouvée" + "noResources": "Aucune ressource trouvée", + "noLogs": "Aucun journal trouvé", + "noErrors": "Aucune erreur trouvée" }, "networkTimeout": { "label": "Délai d'attente réseau", - "description": "Temps maximal d'attente pour les réponses du serveur", + "description": "Temps d'attente maximal pour les réponses du serveur", "options": { "15seconds": "15 secondes", "30seconds": "30 secondes", @@ -41,7 +46,7 @@ }, "deleteDialog": { "title": "Supprimer le serveur MCP", - "description": "Êtes-vous sûr de vouloir supprimer le serveur MCP \"{{serverName}}\" ? Cette action ne peut pas être annulée.", + "description": "Es-tu sûr de vouloir supprimer le serveur MCP \"{{serverName}}\" ? Cette action est irréversible.", "cancel": "Annuler", "delete": "Supprimer" }, diff --git a/webview-ui/src/i18n/locales/fr/prompts.json b/webview-ui/src/i18n/locales/fr/prompts.json index 72b86999bc3..634fd49d054 100644 --- a/webview-ui/src/i18n/locales/fr/prompts.json +++ b/webview-ui/src/i18n/locales/fr/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Créer un nouveau mode", "editModesConfig": "Modifier la configuration des modes", "editGlobalModes": "Modifier les modes globaux", - "editProjectModes": "Modifier les modes du projet (.pearai-agent-ignore)", - "createModeHelpText": "Cliquez sur + pour créer un nouveau mode personnalisé, ou demandez simplement à Roo dans le chat de vous en créer un !" + "editProjectModes": "Modifier les modes du projet (.roomodes)", + "createModeHelpText": "Les modes sont des personas spécialisés qui adaptent le comportement de Roo. <0>En savoir plus sur l'utilisation des modes ou <1>la personnalisation des modes.", + "selectMode": "Rechercher les modes" }, "apiConfiguration": { "title": "Configuration API", @@ -33,16 +34,21 @@ "resetToDefault": "Réinitialiser aux valeurs par défaut", "description": "Définissez l'expertise et la personnalité de Roo pour ce mode. Cette description façonne la manière dont Roo se présente et aborde les tâches." }, + "whenToUse": { + "title": "Quand utiliser (optionnel)", + "description": "Décrivez quand ce mode doit être utilisé. Cela aide l'Orchestrateur à choisir le mode approprié pour une tâche.", + "resetToDefault": "Réinitialiser la description 'Quand utiliser' aux valeurs par défaut" + }, "customInstructions": { "title": "Instructions personnalisées spécifiques au mode (optionnel)", "resetToDefault": "Réinitialiser aux valeurs par défaut", "description": "Ajoutez des directives comportementales spécifiques au mode {{modeName}}.", - "loadFromFile": "Les instructions personnalisées spécifiques au mode {{mode}} peuvent également être chargées depuis le dossier .pearai-agent/rules-{{slug}}/ dans votre espace de travail (.roorules-{{slug}} et .clinerules-{{slug}} sont obsolètes et cesseront de fonctionner bientôt)." + "loadFromFile": "Les instructions personnalisées spécifiques au mode {{mode}} peuvent également être chargées depuis le dossier .roo/rules-{{slug}}/ dans votre espace de travail (.roorules-{{slug}} et .clinerules-{{slug}} sont obsolètes et cesseront de fonctionner bientôt)." }, "globalCustomInstructions": { "title": "Instructions personnalisées pour tous les modes", - "description": "Ces instructions s'appliquent à tous les modes. Elles fournissent un ensemble de comportements de base qui peuvent être améliorés par des instructions spécifiques au mode ci-dessous.\nSi vous souhaitez que Roo pense et parle dans une langue différente de celle de votre éditeur ({{language}}), vous pouvez le spécifier ici.", - "loadFromFile": "Les instructions peuvent également être chargées depuis le dossier .pearai-agent/rules/ dans votre espace de travail (.roorules et .clinerules sont obsolètes et cesseront de fonctionner bientôt)." + "description": "Ces instructions s'appliquent à tous les modes. Elles fournissent un ensemble de comportements de base qui peuvent être améliorés par des instructions spécifiques au mode ci-dessous. <0>En savoir plus", + "loadFromFile": "Les instructions peuvent également être chargées depuis le dossier .roo/rules/ dans votre espace de travail (.roorules et .clinerules sont obsolètes et cesseront de fonctionner bientôt)." }, "systemPrompt": { "preview": "Aperçu du prompt système", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Avancé : Remplacer le prompt système", - "description": "Vous pouvez complètement remplacer le prompt système pour ce mode (en dehors de la définition du rôle et des instructions personnalisées) en créant un fichier à .pearai-agent/system-prompt-{{slug}} dans votre espace de travail. Il s'agit d'une fonctionnalité très avancée qui contourne les garanties intégrées et les vérifications de cohérence (notamment concernant l'utilisation des outils), alors soyez prudent !" + "description": "<2>⚠️ Attention : Cette fonctionnalité avancée contourne les mesures de protection. <1>LISEZ CECI AVANT UTILISATION !Remplacez le prompt système par défaut en créant un fichier à l'emplacement .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Créer un nouveau mode", @@ -122,7 +128,7 @@ "description": "Disponible dans tous les espaces de travail" }, "project": { - "label": "Spécifique au projet (.pearai-agent-ignore)", + "label": "Spécifique au projet (.roomodes)", "description": "Disponible uniquement dans cet espace de travail, a priorité sur le global" } }, @@ -130,6 +136,10 @@ "label": "Définition du rôle", "description": "Définissez l'expertise et la personnalité de Roo pour ce mode." }, + "whenToUse": { + "label": "Quand utiliser (optionnel)", + "description": "Fournissez une description claire de quand ce mode est le plus efficace et pour quels types de tâches il excelle." + }, "tools": { "label": "Outils disponibles", "description": "Sélectionnez quels outils ce mode peut utiliser." diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 9d0930d8108..a16d17ca410 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Fournisseurs", - "autoApprove": "Approbation automatique", - "browser": "Navigateur / Utilisation de l'ordinateur", + "autoApprove": "Auto-approbation", + "browser": "Accès ordinateur", "checkpoints": "Points de contrôle", "notifications": "Notifications", - "contextManagement": "Gestion du contexte", + "contextManagement": "Contexte", "terminal": "Terminal", - "advanced": "Avancé", - "experimental": "Fonctionnalités expérimentales", + "experimental": "Expérimental", "language": "Langue", - "about": "À propos de Roo Code", - "interface": "Interface" + "about": "À propos de Roo Code" }, "autoApprove": { "description": "Permettre à Roo d'effectuer automatiquement des opérations sans requérir d'approbation. Activez ces paramètres uniquement si vous faites entièrement confiance à l'IA et que vous comprenez les risques de sécurité associés.", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "Profil de configuration", "providerDocumentation": "Documentation {{provider}}", + "configProfile": "Profil de configuration", "description": "Enregistrez différentes configurations d'API pour basculer rapidement entre les fournisseurs et les paramètres.", "apiProvider": "Fournisseur d'API", "model": "Modèle", @@ -118,14 +116,22 @@ "headerValue": "Valeur de l'en-tête", "noCustomHeaders": "Aucun en-tête personnalisé défini. Cliquez sur le bouton + pour en ajouter un.", "requestyApiKey": "Clé API Requesty", + "refreshModels": { + "label": "Actualiser les modèles", + "hint": "Veuillez rouvrir les paramètres pour voir les modèles les plus récents." + }, "getRequestyApiKey": "Obtenir la clé API Requesty", "openRouterTransformsText": "Compresser les prompts et chaînes de messages à la taille du contexte (Transformations OpenRouter)", "anthropicApiKey": "Clé API Anthropic", "getAnthropicApiKey": "Obtenir la clé API Anthropic", "anthropicUseAuthToken": "Passer la clé API Anthropic comme en-tête d'autorisation au lieu de X-Api-Key", + "chutesApiKey": "Clé API Chutes", + "getChutesApiKey": "Obtenir la clé API Chutes", "deepSeekApiKey": "Clé API DeepSeek", "getDeepSeekApiKey": "Obtenir la clé API DeepSeek", "geminiApiKey": "Clé API Gemini", + "getGroqApiKey": "Obtenir la clé API Groq", + "groqApiKey": "Clé API Groq", "getGeminiApiKey": "Obtenir la clé API Gemini", "openAiApiKey": "Clé API OpenAI", "openAiBaseUrl": "URL de base", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Définir une URL alternative pour le modèle Codestral.", "xaiApiKey": "Clé API xAI", "getXaiApiKey": "Obtenir la clé API xAI", + "litellmApiKey": "Clé API LiteLLM", + "litellmBaseUrl": "URL de base LiteLLM", "awsCredentials": "Identifiants AWS", "awsProfile": "Profil AWS", "awsProfileName": "Nom du profil AWS", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Activer l'outil de navigateur", - "description": "Lorsque cette option est activée, Roo peut utiliser un navigateur pour interagir avec des sites web lors de l'utilisation de modèles qui prennent en charge l'utilisation de l'ordinateur." + "description": "Lorsque cette option est activée, Roo peut utiliser un navigateur pour interagir avec des sites web lors de l'utilisation de modèles qui prennent en charge l'utilisation de l'ordinateur. <0>En savoir plus" }, "viewport": { "label": "Taille de la fenêtre d'affichage", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Activer les points de contrôle automatiques", - "description": "Lorsque cette option est activée, Roo créera automatiquement des points de contrôle pendant l'exécution des tâches, facilitant la révision des modifications ou le retour à des états antérieurs." + "description": "Lorsque cette option est activée, Roo créera automatiquement des points de contrôle pendant l'exécution des tâches, facilitant la révision des modifications ou le retour à des états antérieurs. <0>En savoir plus" } }, "notifications": { @@ -298,57 +306,69 @@ "label": "Limite de contexte des fichiers de l'espace de travail", "description": "Nombre maximum de fichiers à inclure dans les détails du répertoire de travail actuel. Des valeurs plus élevées fournissent plus de contexte mais augmentent l'utilisation de token." }, - "pearai-agent-ignore": { - "label": "Afficher les fichiers .pearai-agent-ignore dans les listes et recherches", - "description": "Lorsque cette option est activée, les fichiers correspondant aux modèles dans .pearai-agent-ignore seront affichés dans les listes avec un symbole de cadenas. Lorsqu'elle est désactivée, ces fichiers seront complètement masqués des listes de fichiers et des recherches." + "rooignore": { + "label": "Afficher les fichiers .rooignore dans les listes et recherches", + "description": "Lorsque cette option est activée, les fichiers correspondant aux modèles dans .rooignore seront affichés dans les listes avec un symbole de cadenas. Lorsqu'elle est désactivée, ces fichiers seront complètement masqués des listes de fichiers et des recherches." }, "maxReadFile": { "label": "Seuil d'auto-troncature de lecture de fichier", - "description": "Agent lit ce nombre de lignes lorsque le modèle omet les valeurs de début/fin. Si ce nombre est inférieur au total du fichier, Roo génère un index des numéros de ligne des définitions de code. Cas spéciaux : -1 indique à Roo de lire le fichier entier (sans indexation), et 0 indique de ne lire aucune ligne et de fournir uniquement les index de ligne pour un contexte minimal. Des valeurs plus basses minimisent l'utilisation initiale du contexte, permettant des lectures ultérieures de plages de lignes précises. Les requêtes avec début/fin explicites ne sont pas limitées par ce paramètre.", + "description": "Roo lit ce nombre de lignes lorsque le modèle omet les valeurs de début/fin. Si ce nombre est inférieur au total du fichier, Roo génère un index des numéros de ligne des définitions de code. Cas spéciaux : -1 indique à Roo de lire le fichier entier (sans indexation), et 0 indique de ne lire aucune ligne et de fournir uniquement les index de ligne pour un contexte minimal. Des valeurs plus basses minimisent l'utilisation initiale du contexte, permettant des lectures ultérieures de plages de lignes précises. Les requêtes avec début/fin explicites ne sont pas limitées par ce paramètre.", "lines": "lignes", "always_full_read": "Toujours lire le fichier entier" } }, "terminal": { + "basic": { + "label": "Paramètres du terminal : Base", + "description": "Paramètres de base du terminal" + }, + "advanced": { + "label": "Paramètres du terminal : Avancé", + "description": "Les options suivantes peuvent nécessiter un redémarrage du terminal pour appliquer le paramètre." + }, "outputLineLimit": { "label": "Limite de sortie du terminal", - "description": "Nombre maximum de lignes à inclure dans la sortie du terminal lors de l'exécution de commandes. Lorsque ce nombre est dépassé, les lignes seront supprimées du milieu, économisant des token." + "description": "Nombre maximum de lignes à inclure dans la sortie du terminal lors de l'exécution de commandes. Lorsque ce nombre est dépassé, les lignes seront supprimées du milieu, économisant des token. <0>En savoir plus" }, "shellIntegrationTimeout": { "label": "Délai d'intégration du shell du terminal", - "description": "Temps maximum d'attente pour l'initialisation de l'intégration du shell avant d'exécuter des commandes. Pour les utilisateurs avec des temps de démarrage de shell longs, cette valeur peut nécessiter d'être augmentée si vous voyez des erreurs \"Shell Integration Unavailable\" dans le terminal." + "description": "Temps maximum d'attente pour l'initialisation de l'intégration du shell avant d'exécuter des commandes. Pour les utilisateurs avec des temps de démarrage de shell longs, cette valeur peut nécessiter d'être augmentée si vous voyez des erreurs \"Shell Integration Unavailable\" dans le terminal. <0>En savoir plus" }, "shellIntegrationDisabled": { "label": "Désactiver l'intégration du shell du terminal", - "description": "Active ceci si les commandes du terminal ne fonctionnent pas correctement ou si tu vois des erreurs 'Shell Integration Unavailable'. Cela utilise une méthode plus simple pour exécuter les commandes, en contournant certaines fonctionnalités avancées du terminal." - }, - "compressProgressBar": { - "label": "Compresser la sortie des barres de progression", - "description": "Lorsque activé, traite la sortie du terminal avec des retours chariot (\\r) pour simuler l'affichage d'un terminal réel. Cela supprime les états intermédiaires des barres de progression, ne conservant que l'état final, ce qui économise de l'espace de contexte pour des informations plus pertinentes." - }, - "zdotdir": { - "label": "Activer la gestion ZDOTDIR", - "description": "Lorsque activé, crée un répertoire temporaire pour ZDOTDIR afin de gérer correctement l'intégration du shell zsh. Cela garantit le bon fonctionnement de l'intégration du shell VSCode avec zsh tout en préservant votre configuration zsh. (expérimental)" + "description": "Active ceci si les commandes du terminal ne fonctionnent pas correctement ou si tu vois des erreurs 'Shell Integration Unavailable'. Cela utilise une méthode plus simple pour exécuter les commandes, en contournant certaines fonctionnalités avancées du terminal. <0>En savoir plus" }, "commandDelay": { "label": "Délai de commande du terminal", - "description": "Délai en millisecondes à ajouter après l'exécution de la commande. Le paramètre par défaut de 0 désactive complètement le délai. Cela peut aider à garantir que la sortie de la commande est entièrement capturée dans les terminaux avec des problèmes de synchronisation. Dans la plupart des terminaux, cela est implémenté en définissant `PROMPT_COMMAND='sleep N'` et Powershell ajoute `start-sleep` à la fin de chaque commande. À l'origine, c'était une solution pour le bug VSCode#237208 et peut ne pas être nécessaire." + "description": "Délai en millisecondes à ajouter après l'exécution de la commande. Le paramètre par défaut de 0 désactive complètement le délai. Cela peut aider à garantir que la sortie de la commande est entièrement capturée dans les terminaux avec des problèmes de synchronisation. Dans la plupart des terminaux, cela est implémenté en définissant `PROMPT_COMMAND='sleep N'` et Powershell ajoute `start-sleep` à la fin de chaque commande. À l'origine, c'était une solution pour le bug VSCode#237208 et peut ne pas être nécessaire. <0>En savoir plus" + }, + "compressProgressBar": { + "label": "Compresser la sortie des barres de progression", + "description": "Lorsque activé, traite la sortie du terminal avec des retours chariot (\\r) pour simuler l'affichage d'un terminal réel. Cela supprime les états intermédiaires des barres de progression, ne conservant que l'état final, ce qui économise de l'espace de contexte pour des informations plus pertinentes. <0>En savoir plus" }, "powershellCounter": { "label": "Activer le contournement du compteur PowerShell", - "description": "Lorsqu'activé, ajoute un compteur aux commandes PowerShell pour assurer une exécution correcte des commandes. Cela aide avec les terminaux PowerShell qui peuvent avoir des problèmes de capture de sortie." + "description": "Lorsqu'activé, ajoute un compteur aux commandes PowerShell pour assurer une exécution correcte des commandes. Cela aide avec les terminaux PowerShell qui peuvent avoir des problèmes de capture de sortie. <0>En savoir plus" }, "zshClearEolMark": { "label": "Effacer la marque de fin de ligne ZSH", - "description": "Lorsqu'activé, efface la marque de fin de ligne ZSH en définissant PROMPT_EOL_MARK=''. Cela évite les problèmes d'interprétation de la sortie des commandes lorsqu'elle se termine par des caractères spéciaux comme '%'." + "description": "Lorsqu'activé, efface la marque de fin de ligne ZSH en définissant PROMPT_EOL_MARK=''. Cela évite les problèmes d'interprétation de la sortie des commandes lorsqu'elle se termine par des caractères spéciaux comme '%'. <0>En savoir plus" }, "zshOhMy": { "label": "Activer l'intégration Oh My Zsh", - "description": "Lorsqu'activé, définit ITERM_SHELL_INTEGRATION_INSTALLED=Yes pour activer les fonctionnalités d'intégration du shell Oh My Zsh. L'application de ce paramètre peut nécessiter le redémarrage de l'IDE. (expérimental)" + "description": "Lorsqu'activé, définit ITERM_SHELL_INTEGRATION_INSTALLED=Yes pour activer les fonctionnalités d'intégration du shell Oh My Zsh. L'application de ce paramètre peut nécessiter le redémarrage de l'IDE. <0>En savoir plus" }, "zshP10k": { "label": "Activer l'intégration Powerlevel10k", - "description": "Lorsqu'activé, définit POWERLEVEL9K_TERM_SHELL_INTEGRATION=true pour activer les fonctionnalités d'intégration du shell Powerlevel10k. (expérimental)" + "description": "Lorsqu'activé, définit POWERLEVEL9K_TERM_SHELL_INTEGRATION=true pour activer les fonctionnalités d'intégration du shell Powerlevel10k. <0>En savoir plus" + }, + "zdotdir": { + "label": "Activer la gestion ZDOTDIR", + "description": "Lorsque activé, crée un répertoire temporaire pour ZDOTDIR afin de gérer correctement l'intégration du shell zsh. Cela garantit le bon fonctionnement de l'intégration du shell VSCode avec zsh tout en préservant votre configuration zsh. <0>En savoir plus" + }, + "inheritEnv": { + "label": "Hériter des variables d'environnement", + "description": "Lorsqu'activé, le terminal hérite des variables d'environnement du processus parent VSCode, comme les paramètres d'intégration du shell définis dans le profil utilisateur. Cela bascule directement le paramètre global VSCode `terminal.integrated.inheritEnv`. <0>En savoir plus" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Condenser intelligemment la fenêtre de contexte", + "description": "Utilise un appel LLM pour résumer la conversation précédente lorsque la fenêtre de contexte de la tâche est presque pleine, plutôt que de supprimer les anciens messages. Avertissement : le coût de la synthèse n'est actuellement pas inclus dans les coûts API affichés dans l'interface." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Utiliser la stratégie diff unifiée expérimentale", "description": "Activer la stratégie diff unifiée expérimentale. Cette stratégie pourrait réduire le nombre de tentatives causées par des erreurs de modèle, mais peut provoquer des comportements inattendus ou des modifications incorrectes. Activez-la uniquement si vous comprenez les risques et êtes prêt à examiner attentivement tous les changements." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Activer la mise en cache des prompts", - "description": "Lorsque cette option est activée, Roo utilisera ce modèle avec la mise en cache des prompts activée afin de réduire les coûts." + "label": "Désactiver la mise en cache des prompts", + "description": "Lorsque cette option est cochée, Roo n'utilisera pas la mise en cache des prompts pour ce modèle." }, "temperature": { "useCustom": "Utiliser une température personnalisée", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Si vous avez des questions ou des commentaires, n'hésitez pas à ouvrir un problème sur github.com/RooVetGit/Roo-Code ou à rejoindre reddit.com/r/RooCode ou discord.gg/roocode", - "version": "Agent v{{version}}", "telemetry": { "label": "Autoriser les rapports anonymes d'erreurs et d'utilisation", "description": "Aidez à améliorer Roo Code en envoyant des données d'utilisation anonymes et des rapports d'erreurs. Aucun code, prompt ou information personnelle n'est jamais envoyé. Consultez notre politique de confidentialité pour plus de détails." diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index a9b1adcdacc..459216b92c9 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -1,5 +1,5 @@ { - "greeting": "Agent Code में आपका स्वागत है", + "greeting": "Roo Code में आपका स्वागत है", "task": { "title": "कार्य", "seeMore": "अधिक देखें", @@ -109,7 +109,7 @@ "diffError": { "title": "संपादन असफल" }, - "troubleMessage": "Agent को समस्या हो रही है...", + "troubleMessage": "Roo को समस्या हो रही है...", "apiRequest": { "title": "API अनुरोध", "failed": "API अनुरोध विफल हुआ", @@ -135,47 +135,47 @@ "current": "वर्तमान" }, "instructions": { - "wantsToFetch": "Agent को वर्तमान कार्य में सहायता के लिए विस्तृत निर्देश प्राप्त करना है" + "wantsToFetch": "Roo को वर्तमान कार्य में सहायता के लिए विस्तृत निर्देश प्राप्त करना है" }, "fileOperations": { - "wantsToRead": "Agent इस फ़ाइल को पढ़ना चाहता है:", - "wantsToReadOutsideWorkspace": "Agent कार्यक्षेत्र के बाहर इस फ़ाइल को पढ़ना चाहता है:", - "didRead": "Agent ने इस फ़ाइल को पढ़ा:", - "wantsToEdit": "Agent इस फ़ाइल को संपादित करना चाहता है:", - "wantsToEditOutsideWorkspace": "Agent कार्यक्षेत्र के बाहर इस फ़ाइल को संपादित करना चाहता है:", - "wantsToCreate": "Agent एक नई फ़ाइल बनाना चाहता है:", - "wantsToSearchReplace": "Agent इस फ़ाइल में खोज और प्रतिस्थापन करना चाहता है:", - "didSearchReplace": "Agent ने इस फ़ाइल में खोज और प्रतिस्थापन किया:", - "wantsToInsert": "Agent इस फ़ाइल में सामग्री डालना चाहता है:", - "wantsToInsertWithLineNumber": "Agent इस फ़ाइल की {{lineNumber}} लाइन पर सामग्री डालना चाहता है:", - "wantsToInsertAtEnd": "Agent इस फ़ाइल के अंत में सामग्री जोड़ना चाहता है:" + "wantsToRead": "Roo इस फ़ाइल को पढ़ना चाहता है:", + "wantsToReadOutsideWorkspace": "Roo कार्यक्षेत्र के बाहर इस फ़ाइल को पढ़ना चाहता है:", + "didRead": "Roo ने इस फ़ाइल को पढ़ा:", + "wantsToEdit": "Roo इस फ़ाइल को संपादित करना चाहता है:", + "wantsToEditOutsideWorkspace": "Roo कार्यक्षेत्र के बाहर इस फ़ाइल को संपादित करना चाहता है:", + "wantsToCreate": "Roo एक नई फ़ाइल बनाना चाहता है:", + "wantsToSearchReplace": "Roo इस फ़ाइल में खोज और प्रतिस्थापन करना चाहता है:", + "didSearchReplace": "Roo ने इस फ़ाइल में खोज और प्रतिस्थापन किया:", + "wantsToInsert": "Roo इस फ़ाइल में सामग्री डालना चाहता है:", + "wantsToInsertWithLineNumber": "Roo इस फ़ाइल की {{lineNumber}} लाइन पर सामग्री डालना चाहता है:", + "wantsToInsertAtEnd": "Roo इस फ़ाइल के अंत में सामग्री जोड़ना चाहता है:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखना चाहता है:", - "didViewTopLevel": "Agent ने इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखीं:", - "wantsToViewRecursive": "Agent इस निर्देशिका में सभी फ़ाइलों को पुनरावर्ती रूप से देखना चाहता है:", - "didViewRecursive": "Agent ने इस निर्देशिका में सभी फ़ाइलों को पुनरावर्ती रूप से देखा:", - "wantsToViewDefinitions": "Agent इस निर्देशिका में उपयोग किए गए सोर्स कोड परिभाषा नामों को देखना चाहता है:", - "didViewDefinitions": "Agent ने इस निर्देशिका में उपयोग किए गए सोर्स कोड परिभाषा नामों को देखा:", - "wantsToSearch": "Agent इस निर्देशिका में {{regex}} के लिए खोज करना चाहता है:", - "didSearch": "Agent ने इस निर्देशिका में {{regex}} के लिए खोज की:" + "wantsToViewTopLevel": "Roo इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखना चाहता है:", + "didViewTopLevel": "Roo ने इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखीं:", + "wantsToViewRecursive": "Roo इस निर्देशिका में सभी फ़ाइलों को पुनरावर्ती रूप से देखना चाहता है:", + "didViewRecursive": "Roo ने इस निर्देशिका में सभी फ़ाइलों को पुनरावर्ती रूप से देखा:", + "wantsToViewDefinitions": "Roo इस निर्देशिका में उपयोग किए गए सोर्स कोड परिभाषा नामों को देखना चाहता है:", + "didViewDefinitions": "Roo ने इस निर्देशिका में उपयोग किए गए सोर्स कोड परिभाषा नामों को देखा:", + "wantsToSearch": "Roo इस निर्देशिका में {{regex}} के लिए खोज करना चाहता है:", + "didSearch": "Roo ने इस निर्देशिका में {{regex}} के लिए खोज की:" }, "commandOutput": "कमांड आउटपुट", "response": "प्रतिक्रिया", "arguments": "आर्ग्युमेंट्स", "mcp": { - "wantsToUseTool": "Agent {{serverName}} MCP सर्वर पर एक टूल का उपयोग करना चाहता है:", - "wantsToAccessResource": "Agent {{serverName}} MCP सर्वर पर एक संसाधन का उपयोग करना चाहता है:" + "wantsToUseTool": "Roo {{serverName}} MCP सर्वर पर एक टूल का उपयोग करना चाहता है:", + "wantsToAccessResource": "Roo {{serverName}} MCP सर्वर पर एक संसाधन का उपयोग करना चाहता है:" }, "modes": { - "wantsToSwitch": "Agent {{mode}} मोड में स्विच करना चाहता है", - "wantsToSwitchWithReason": "Agent {{mode}} मोड में स्विच करना चाहता है क्योंकि: {{reason}}", - "didSwitch": "Agent {{mode}} मोड में स्विच कर गया", - "didSwitchWithReason": "Agent {{mode}} मोड में स्विच कर गया क्योंकि: {{reason}}" + "wantsToSwitch": "Roo {{mode}} मोड में स्विच करना चाहता है", + "wantsToSwitchWithReason": "Roo {{mode}} मोड में स्विच करना चाहता है क्योंकि: {{reason}}", + "didSwitch": "Roo {{mode}} मोड में स्विच कर गया", + "didSwitchWithReason": "Roo {{mode}} मोड में स्विच कर गया क्योंकि: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent {{mode}} मोड में एक नया उपकार्य बनाना चाहता है:", - "wantsToFinish": "Agent इस उपकार्य को समाप्त करना चाहता है", + "wantsToCreate": "Roo {{mode}} मोड में एक नया उपकार्य बनाना चाहता है:", + "wantsToFinish": "Roo इस उपकार्य को समाप्त करना चाहता है", "newTaskContent": "उपकार्य निर्देश", "completionContent": "उपकार्य पूर्ण", "resultContent": "उपकार्य परिणाम", @@ -183,7 +183,7 @@ "completionInstructions": "उपकार्य पूर्ण! आप परिणामों की समीक्षा कर सकते हैं और सुधार या अगले चरण सुझा सकते हैं। यदि सब कुछ ठीक लगता है, तो मुख्य कार्य को परिणाम वापस करने के लिए पुष्टि करें।" }, "questions": { - "hasQuestion": "Agent का एक प्रश्न है:" + "hasQuestion": "Roo का एक प्रश्न है:" }, "taskCompleted": "कार्य पूरा हुआ", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "इनपुट में कॉपी करें (या Shift + क्लिक)" }, "announcement": { - "title": "🎉 Roo Code 3.15 रिलीज़ हुआ", - "description": "Agent Code 3.15 आपके फीडबैक के आधार पर नई सुविधाएँ और सुधार लाता है।", + "title": "🎉 Roo Code 3.17 रिलीज़ हुआ", + "description": "Roo Code 3.17 आपके फीडबैक के आधार पर शक्तिशाली नई सुविधाएँ और सुधार लाता है।", "whatsNew": "नई सुविधाएँ", - "feature1": "Vertex के लिए प्रॉम्प्ट कैशिंग: Vertex AI के लिए प्रॉम्प्ट कैशिंग समर्थन जोड़ा गया, प्रतिक्रिया समय में सुधार और API लागत कम करता है", - "feature2": "टर्मिनल फॉलबैक: VSCode टर्मिनल शेल इंटीग्रेशन विफल होने पर फॉलबैक तंत्र लागू किया गया", - "feature3": "बेहतर कोड स्निपेट: चैट इंटरफेस में कोड स्निपेट रेंडरिंग और इंटरैक्शन में सुधार", + "feature1": "Gemini के लिए अंतर्निहित कैशिंग: Gemini API कॉल अब स्वचालित रूप से कैश किए जाते हैं, जिससे API लागत कम होती है", + "feature2": "स्मार्ट मोड चयन: मोड परिभाषाओं में अब यह मार्गदर्शन शामिल हो सकता है कि प्रत्येक मोड कब उपयोग किया जाना चाहिए, जिससे बेहतर समन्वय संभव हो", + "feature3": "बुद्धिमान संदर्भ संघनन: जब संदर्भ भर जाता है तो काटने के बजाय बातचीत इतिहास का बुद्धिमानी से सारांश प्रस्तुत करता है (सेटिंग्स -> प्रयोगात्मक में सक्षम करें)", "hideButton": "घोषणा छिपाएँ", "detailsDiscussLinks": "Discord और Reddit पर अधिक जानकारी प्राप्त करें और चर्चा में भाग लें 🚀" }, "browser": { - "rooWantsToUse": "Agent ब्राउज़र का उपयोग करना चाहता है:", + "rooWantsToUse": "Roo ब्राउज़र का उपयोग करना चाहता है:", "consoleLogs": "कंसोल लॉग", "noNewLogs": "(कोई नया लॉग नहीं)", "screenshot": "ब्राउज़र स्क्रीनशॉट", @@ -233,6 +233,15 @@ "close": "ब्राउज़र बंद करें" } }, + "codeblock": { + "tooltips": { + "expand": "कोड ब्लॉक का विस्तार करें", + "collapse": "कोड ब्लॉक को संकुचित करें", + "enable_wrap": "वर्ड रैप सक्षम करें", + "disable_wrap": "वर्ड रैप अक्षम करें", + "copy_code": "कोड कॉपी करें" + } + }, "systemPromptWarning": "चेतावनी: कस्टम सिस्टम प्रॉम्प्ट ओवरराइड सक्रिय है। यह कार्यक्षमता को गंभीर रूप से बाधित कर सकता है और अनियमित व्यवहार का कारण बन सकता है.", "shellIntegration": { "title": "कमांड निष्पादन चेतावनी", diff --git a/webview-ui/src/i18n/locales/hi/mcp.json b/webview-ui/src/i18n/locales/hi/mcp.json index 32aee49dc49..40e381f9d0c 100644 --- a/webview-ui/src/i18n/locales/hi/mcp.json +++ b/webview-ui/src/i18n/locales/hi/mcp.json @@ -1,33 +1,38 @@ { "title": "MCP सर्वर", "done": "हो गया", - "description": "<0>मॉडल कॉन्टेक्स्ट प्रोटोकॉल स्थानीय रूप से चल रहे MCP सर्वरों के साथ संचार को सक्षम बनाता है जो Roo की क्षमताओं का विस्तार करने के लिए अतिरिक्त उपकरण और संसाधन प्रदान करते हैं। आप <1>समुदाय द्वारा बनाए गए सर्वरों का उपयोग कर सकते हैं या Roo से अपने कार्यप्रवाह के लिए विशिष्ट नए उपकरण बनाने के लिए कह सकते हैं (जैसे, \"नवीनतम npm दस्तावेज़ प्राप्त करने वाला उपकरण जोड़ें\")।", + "description": "Model Context Protocol (MCP) सक्षम करें ताकि Roo Code बाहरी सर्वरों से अतिरिक्त टूल्स और सेवाएँ इस्तेमाल कर सके। इससे Roo तुम्हारे लिए और भी काम कर सकता है। <0>और जानें", "enableToggle": { "title": "MCP सर्वर सक्षम करें", - "description": "जब सक्षम होता है, तो Roo उन्नत कार्यक्षमता के लिए MCP सर्वरों के साथ बातचीत कर सकेगा। यदि आप MCP का उपयोग नहीं कर रहे हैं, तो आप Roo के token उपयोग को कम करने के लिए इसे अक्षम कर सकते हैं।" + "description": "इसे ON करो ताकि Roo जुड़े हुए MCP सर्वरों से टूल्स इस्तेमाल कर सके। इससे Roo को और क्षमताएँ मिलती हैं। अगर तुम ये अतिरिक्त टूल्स इस्तेमाल नहीं करना चाहते, तो इसे OFF करो ताकि API टोकन लागत कम हो सके।" }, "enableServerCreation": { - "title": "MCP सर्वर निर्माण सक्षम करें", - "description": "जब सक्षम होता है, तो Roo आपको \"में नया उपकरण जोड़ें...\" जैसे कमांड के माध्यम से नए MCP सर्वर बनाने में मदद कर सकता है। यदि आपको MCP सर्वर बनाने की आवश्यकता नहीं है, तो आप Roo के token उपयोग को कम करने के लिए इसे अक्षम कर सकते हैं।" + "title": "MCP सर्वर बनाना सक्षम करें", + "description": "इसे ON करो ताकि Roo तुम्हारी मदद से <1>नए कस्टम MCP सर्वर बना सके। <0>सर्वर बनाना जानें", + "hint": "टिप: API टोकन लागत कम करने के लिए, जब Roo से नया MCP सर्वर नहीं बनवा रहे हो तो इस सेटिंग को बंद कर दो।" }, - "editGlobalMCP": "वैश्विक MCP संपादित करें", - "editProjectMCP": "प्रोजेक्ट MCP संपादित करें", + "editGlobalMCP": "ग्लोबल MCP एडिट करें", + "editProjectMCP": "प्रोजेक्ट MCP एडिट करें", + "learnMoreEditingSettings": "MCP सेटिंग्स फाइल एडिट करने के बारे में जानें", "tool": { "alwaysAllow": "हमेशा अनुमति दें", "parameters": "पैरामीटर", "noDescription": "कोई विवरण नहीं" }, "tabs": { - "tools": "उपकरण", - "resources": "संसाधन" + "tools": "टूल्स", + "resources": "संसाधन", + "errors": "त्रुटियाँ" }, "emptyState": { - "noTools": "कोई उपकरण नहीं मिला", - "noResources": "कोई संसाधन नहीं मिला" + "noTools": "कोई टूल नहीं मिला", + "noResources": "कोई संसाधन नहीं मिला", + "noLogs": "कोई लॉग नहीं मिला", + "noErrors": "कोई त्रुटि नहीं मिली" }, "networkTimeout": { "label": "नेटवर्क टाइमआउट", - "description": "सर्वर प्रतिक्रियाओं के लिए प्रतीक्षा करने का अधिकतम समय", + "description": "सर्वर रिस्पॉन्स के लिए अधिकतम प्रतीक्षा समय", "options": { "15seconds": "15 सेकंड", "30seconds": "30 सेकंड", @@ -40,13 +45,13 @@ } }, "deleteDialog": { - "title": "MCP सर्वर हटाएं", - "description": "क्या आप वाकई MCP सर्वर \"{{serverName}}\" को हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।", + "title": "MCP सर्वर हटाएँ", + "description": "क्या तुम वाकई MCP सर्वर \"{{serverName}}\" हटाना चाहते हो? यह क्रिया वापस नहीं ली जा सकती।", "cancel": "रद्द करें", - "delete": "हटाएं" + "delete": "हटाएँ" }, "serverStatus": { - "retrying": "पुनः प्रयास कर रहे हैं...", - "retryConnection": "कनेक्शन पुनः प्रयास करें" + "retrying": "फिर से कोशिश कर रहा है...", + "retryConnection": "कनेक्शन फिर से आज़माएँ" } } diff --git a/webview-ui/src/i18n/locales/hi/prompts.json b/webview-ui/src/i18n/locales/hi/prompts.json index 83092ae6a6a..5114166d603 100644 --- a/webview-ui/src/i18n/locales/hi/prompts.json +++ b/webview-ui/src/i18n/locales/hi/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "नया मोड बनाएँ", "editModesConfig": "मोड कॉन्फ़िगरेशन संपादित करें", "editGlobalModes": "ग्लोबल मोड्स संपादित करें", - "editProjectModes": "प्रोजेक्ट मोड्स संपादित करें (.pearai-agent-ignore)", - "createModeHelpText": "नया कस्टम मोड बनाने के लिए + पर क्लिक करें, या बस चैट में Roo से आपके लिए एक बनाने को कहें!" + "editProjectModes": "प्रोजेक्ट मोड्स संपादित करें (.roomodes)", + "createModeHelpText": "मोड विशेष व्यक्तित्व हैं जो Roo के व्यवहार को अनुकूलित करते हैं। <0>मोड का उपयोग करने के बारे में जानें या <1>मोड को अनुकूलित करना।", + "selectMode": "मोड खोजें" }, "apiConfiguration": { "title": "API कॉन्फ़िगरेशन", @@ -33,16 +34,21 @@ "resetToDefault": "डिफ़ॉल्ट पर रीसेट करें", "description": "इस मोड के लिए Roo की विशेषज्ञता और व्यक्तित्व परिभाषित करें। यह विवरण Roo के स्वयं को प्रस्तुत करने और कार्यों से निपटने के तरीके को आकार देता है।" }, + "whenToUse": { + "title": "कब उपयोग करें (वैकल्पिक)", + "description": "बताएं कि इस मोड का उपयोग कब किया जाना चाहिए। यह Orchestrator को किसी कार्य के लिए सही मोड चुनने में मदद करता है।", + "resetToDefault": "'कब उपयोग करें' विवरण को डिफ़ॉल्ट पर रीसेट करें" + }, "customInstructions": { "title": "मोड-विशिष्ट कस्टम निर्देश (वैकल्पिक)", "resetToDefault": "डिफ़ॉल्ट पर रीसेट करें", "description": "{{modeName}} मोड के लिए विशिष्ट व्यवहार दिशानिर्देश जोड़ें।", - "loadFromFile": "{{mode}} मोड के लिए विशिष्ट कस्टम निर्देश आपके वर्कस्पेस में .pearai-agent/rules-{{slug}}/ फ़ोल्डर से भी लोड किए जा सकते हैं (.roorules-{{slug}} और .clinerules-{{slug}} पुराने हो गए हैं और जल्द ही काम करना बंद कर देंगे)।" + "loadFromFile": "{{mode}} मोड के लिए विशिष्ट कस्टम निर्देश आपके वर्कस्पेस में .roo/rules-{{slug}}/ फ़ोल्डर से भी लोड किए जा सकते हैं (.roorules-{{slug}} और .clinerules-{{slug}} पुराने हो गए हैं और जल्द ही काम करना बंद कर देंगे)।" }, "globalCustomInstructions": { "title": "सभी मोड्स के लिए कस्टम निर्देश", - "description": "ये निर्देश सभी मोड्स पर लागू होते हैं। वे व्यवहारों का एक आधार सेट प्रदान करते हैं जिन्हें नीचे दिए गए मोड-विशिष्ट निर्देशों द्वारा बढ़ाया जा सकता है।\nयदि आप चाहते हैं कि Roo आपके एडिटर की प्रदर्शन भाषा ({{language}}) से अलग भाषा में सोचे और बोले, तो आप यहां इसे निर्दिष्ट कर सकते हैं।", - "loadFromFile": "निर्देश आपके वर्कस्पेस में .pearai-agent/rules/ फ़ोल्डर से भी लोड किए जा सकते हैं (.roorules और .clinerules पुराने हो गए हैं और जल्द ही काम करना बंद कर देंगे)।" + "description": "ये निर्देश सभी मोड्स पर लागू होते हैं। वे व्यवहारों का एक आधार सेट प्रदान करते हैं जिन्हें नीचे दिए गए मोड-विशिष्ट निर्देशों द्वारा बढ़ाया जा सकता है। <0>और जानें", + "loadFromFile": "निर्देश आपके वर्कस्पेस में .roo/rules/ फ़ोल्डर से भी लोड किए जा सकते हैं (.roorules और .clinerules पुराने हो गए हैं और जल्द ही काम करना बंद कर देंगे)।" }, "systemPrompt": { "preview": "सिस्टम प्रॉम्प्ट का पूर्वावलोकन", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "उन्नत: सिस्टम प्रॉम्प्ट ओवरराइड करें", - "description": "आप अपने वर्कस्पेस में .pearai-agent/system-prompt-{{slug}} पर एक फाइल बनाकर इस मोड के लिए सिस्टम प्रॉम्प्ट को पूरी तरह से बदल सकते हैं (भूमिका परिभाषा और कस्टम निर्देशों को छोड़कर)। यह एक बहुत उन्नत सुविधा है जो अंतर्निहित सुरक्षा उपायों और सामंजस्यता जांचों को बायपास करती है (विशेष रूप से टूल उपयोग के आसपास), इसलिए सावधान रहें!" + "description": "<2>⚠️ चेतावनी: यह उन्नत सुविधा सुरक्षा उपायों को दरकिनार करती है। <1>उपयोग करने से पहले इसे पढ़ें!अपने वर्कस्पेस में .roo/system-prompt-{{slug}} पर एक फ़ाइल बनाकर डिफ़ॉल्ट सिस्टम प्रॉम्प्ट को ओवरराइड करें।" }, "createModeDialog": { "title": "नया मोड बनाएँ", @@ -122,7 +128,7 @@ "description": "सभी वर्कस्पेस में उपलब्ध" }, "project": { - "label": "प्रोजेक्ट-विशिष्ट (.pearai-agent-ignore)", + "label": "प्रोजेक्ट-विशिष्ट (.roomodes)", "description": "केवल इस वर्कस्पेस में उपलब्ध, ग्लोबल पर प्राथमिकता रखता है" } }, @@ -130,6 +136,10 @@ "label": "भूमिका परिभाषा", "description": "इस मोड के लिए Roo की विशेषज्ञता और व्यक्तित्व परिभाषित करें।" }, + "whenToUse": { + "label": "कब उपयोग करें (वैकल्पिक)", + "description": "स्पष्ट रूप से बताएं कि यह मोड कब सबसे प्रभावी है और किस प्रकार के कार्यों में यह उत्कृष्ट है।" + }, "tools": { "label": "उपलब्ध टूल्स", "description": "चुनें कि यह मोड कौन से टूल्स उपयोग कर सकता है।" diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index e40028cbdd8..0ddce26ba5e 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -22,26 +22,24 @@ }, "sections": { "providers": "प्रदाता", - "autoApprove": "स्वतः अनुमोदन", - "browser": "ब्राउज़र / कंप्यूटर उपयोग", + "autoApprove": "अनुमोदन", + "browser": "ब्राउज़र", "checkpoints": "चेकपॉइंट", "notifications": "सूचनाएँ", - "contextManagement": "संदर्भ प्रबंधन", + "contextManagement": "संदर्भ", "terminal": "टर्मिनल", - "advanced": "उन्नत", - "experimental": "प्रायोगिक सुविधाएँ", + "experimental": "प्रायोगिक", "language": "भाषा", - "about": "Agent के बारे में", - "interface": "इंटरफ़ेस" + "about": "परिचय" }, "autoApprove": { - "description": "Agent को अनुमोदन की आवश्यकता के बिना स्वचालित रूप से ऑपरेशन करने की अनुमति दें। इन सेटिंग्स को केवल तभी सक्षम करें जब आप AI पर पूरी तरह से भरोसा करते हों और संबंधित सुरक्षा जोखिमों को समझते हों।", + "description": "Roo को अनुमोदन की आवश्यकता के बिना स्वचालित रूप से ऑपरेशन करने की अनुमति दें। इन सेटिंग्स को केवल तभी सक्षम करें जब आप AI पर पूरी तरह से भरोसा करते हों और संबंधित सुरक्षा जोखिमों को समझते हों।", "readOnly": { "label": "पढ़ें", "description": "जब सक्षम होता है, तो Roo आपके अनुमोदित बटन पर क्लिक किए बिना स्वचालित रूप से निर्देशिका सामग्री देखेगा और फाइलें पढ़ेगा।", "outsideWorkspace": { "label": "वर्कस्पेस के बाहर की फाइलें शामिल करें", - "description": "Agent को अनुमोदन की आवश्यकता के बिना वर्तमान वर्कस्पेस के बाहर की फाइलें पढ़ने की अनुमति दें।" + "description": "Roo को अनुमोदन की आवश्यकता के बिना वर्तमान वर्कस्पेस के बाहर की फाइलें पढ़ने की अनुमति दें।" } }, "write": { @@ -50,7 +48,7 @@ "delayLabel": "लिखने के बाद विलंब ताकि डायग्नोस्टिक संभावित समस्याओं का पता लगा सकें", "outsideWorkspace": { "label": "वर्कस्पेस के बाहर की फाइलें शामिल करें", - "description": "Agent को अनुमोदन की आवश्यकता के बिना वर्तमान वर्कस्पेस के बाहर फाइलें बनाने और संपादित करने की अनुमति दें।" + "description": "Roo को अनुमोदन की आवश्यकता के बिना वर्तमान वर्कस्पेस के बाहर फाइलें बनाने और संपादित करने की अनुमति दें।" } }, "browser": { @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "कॉन्फिगरेशन प्रोफाइल", "providerDocumentation": "{{provider}} दस्तावेज़ीकरण", + "configProfile": "कॉन्फिगरेशन प्रोफाइल", "description": "विभिन्न API कॉन्फ़िगरेशन सहेजें ताकि प्रदाताओं और सेटिंग्स के बीच त्वरित रूप से स्विच कर सकें।", "apiProvider": "API प्रदाता", "model": "मॉडल", @@ -118,14 +116,22 @@ "headerValue": "हेडर मूल्य", "noCustomHeaders": "कोई कस्टम हेडर परिभाषित नहीं है। एक जोड़ने के लिए + बटन पर क्लिक करें।", "requestyApiKey": "Requesty API कुंजी", + "refreshModels": { + "label": "मॉडल रिफ्रेश करें", + "hint": "नवीनतम मॉडल देखने के लिए कृपया सेटिंग्स को फिर से खोलें।" + }, "getRequestyApiKey": "Requesty API कुंजी प्राप्त करें", "openRouterTransformsText": "संदर्भ आकार के लिए प्रॉम्प्ट और संदेश श्रृंखलाओं को संपीड़ित करें (OpenRouter ट्रांसफॉर्म)", "anthropicApiKey": "Anthropic API कुंजी", "getAnthropicApiKey": "Anthropic API कुंजी प्राप्त करें", "anthropicUseAuthToken": "X-Api-Key के बजाय Anthropic API कुंजी को Authorization हेडर के रूप में पास करें", + "chutesApiKey": "Chutes API कुंजी", + "getChutesApiKey": "Chutes API कुंजी प्राप्त करें", "deepSeekApiKey": "DeepSeek API कुंजी", "getDeepSeekApiKey": "DeepSeek API कुंजी प्राप्त करें", "geminiApiKey": "Gemini API कुंजी", + "getGroqApiKey": "Groq API कुंजी प्राप्त करें", + "groqApiKey": "Groq API कुंजी", "getGeminiApiKey": "Gemini API कुंजी प्राप्त करें", "openAiApiKey": "OpenAI API कुंजी", "openAiBaseUrl": "बेस URL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Codestral मॉडल के लिए वैकल्पिक URL सेट करें।", "xaiApiKey": "xAI API कुंजी", "getXaiApiKey": "xAI API कुंजी प्राप्त करें", + "litellmApiKey": "LiteLLM API कुंजी", + "litellmBaseUrl": "LiteLLM आधार URL", "awsCredentials": "AWS क्रेडेंशियल्स", "awsProfile": "AWS प्रोफाइल", "awsProfileName": "AWS प्रोफाइल नाम", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "ब्राउज़र टूल सक्षम करें", - "description": "जब सक्षम होता है, तो Roo कंप्यूटर उपयोग का समर्थन करने वाले मॉडल का उपयोग करते समय वेबसाइटों के साथ बातचीत करने के लिए ब्राउज़र का उपयोग कर सकता है।" + "description": "जब सक्षम होता है, तो Roo कंप्यूटर उपयोग का समर्थन करने वाले मॉडल का उपयोग करते समय वेबसाइटों के साथ बातचीत करने के लिए ब्राउज़र का उपयोग कर सकता है। <0>अधिक जानें" }, "viewport": { "label": "व्यूपोर्ट आकार", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "स्वचालित चेकपॉइंट सक्षम करें", - "description": "जब सक्षम होता है, तो Roo कार्य निष्पादन के दौरान स्वचालित रूप से चेकपॉइंट बनाएगा, जिससे परिवर्तनों की समीक्षा करना या पहले की स्थितियों पर वापस जाना आसान हो जाएगा।" + "description": "जब सक्षम होता है, तो Roo कार्य निष्पादन के दौरान स्वचालित रूप से चेकपॉइंट बनाएगा, जिससे परिवर्तनों की समीक्षा करना या पहले की स्थितियों पर वापस जाना आसान हो जाएगा। <0>अधिक जानें" } }, "notifications": { @@ -298,9 +306,9 @@ "label": "वर्कस्पेस फाइल संदर्भ सीमा", "description": "वर्तमान कार्य निर्देशिका विवरण में शामिल करने के लिए फाइलों की अधिकतम संख्या। उच्च मान अधिक संदर्भ प्रदान करते हैं लेकिन token उपयोग बढ़ाते हैं।" }, - "pearai-agent-ignore": { - "label": "सूचियों और खोजों में .pearai-agent-ignore फाइलें दिखाएँ", - "description": "जब सक्षम होता है, .pearai-agent-ignore में पैटर्न से मेल खाने वाली फाइलें लॉक प्रतीक के साथ सूचियों में दिखाई जाएंगी। जब अक्षम होता है, ये फाइलें फाइल सूचियों और खोजों से पूरी तरह छिपा दी जाएंगी।" + "rooignore": { + "label": "सूचियों और खोजों में .rooignore फाइलें दिखाएँ", + "description": "जब सक्षम होता है, .rooignore में पैटर्न से मेल खाने वाली फाइलें लॉक प्रतीक के साथ सूचियों में दिखाई जाएंगी। जब अक्षम होता है, ये फाइलें फाइल सूचियों और खोजों से पूरी तरह छिपा दी जाएंगी।" }, "maxReadFile": { "label": "फ़ाइल पढ़ने का स्वचालित काटने की सीमा", @@ -310,45 +318,57 @@ } }, "terminal": { + "basic": { + "label": "टर्मिनल सेटिंग्स: मूल", + "description": "मूल टर्मिनल सेटिंग्स" + }, + "advanced": { + "label": "टर्मिनल सेटिंग्स: उन्नत", + "description": "निम्नलिखित विकल्पों को लागू करने के लिए टर्मिनल को पुनरारंभ करने की आवश्यकता हो सकती है" + }, "outputLineLimit": { "label": "टर्मिनल आउटपुट सीमा", - "description": "कमांड निष्पादित करते समय टर्मिनल आउटपुट में शामिल करने के लिए पंक्तियों की अधिकतम संख्या। पार होने पर पंक्तियाँ मध्य से हटा दी जाएंगी, token बचाते हुए।" + "description": "कमांड निष्पादित करते समय टर्मिनल आउटपुट में शामिल करने के लिए पंक्तियों की अधिकतम संख्या। पार होने पर पंक्तियाँ मध्य से हटा दी जाएंगी, token बचाते हुए। <0>अधिक जानें" }, "shellIntegrationTimeout": { "label": "टर्मिनल शेल एकीकरण टाइमआउट", - "description": "कमांड निष्पादित करने से पहले शेल एकीकरण के आरंभ होने के लिए प्रतीक्षा का अधिकतम समय। लंबे शेल स्टार्टअप समय वाले उपयोगकर्ताओं के लिए, यदि आप टर्मिनल में \"Shell Integration Unavailable\" त्रुटियाँ देखते हैं तो इस मान को बढ़ाने की आवश्यकता हो सकती है।" + "description": "कमांड निष्पादित करने से पहले शेल एकीकरण के आरंभ होने के लिए प्रतीक्षा का अधिकतम समय। लंबे शेल स्टार्टअप समय वाले उपयोगकर्ताओं के लिए, यदि आप टर्मिनल में \"Shell Integration Unavailable\" त्रुटियाँ देखते हैं तो इस मान को बढ़ाने की आवश्यकता हो सकती है। <0>अधिक जानें" }, "shellIntegrationDisabled": { "label": "टर्मिनल शेल एकीकरण अक्षम करें", - "description": "इसे सक्षम करें यदि टर्मिनल कमांड सही ढंग से काम नहीं कर रहे हैं या आपको 'शेल एकीकरण अनुपलब्ध' त्रुटियाँ दिखाई देती हैं। यह कमांड चलाने के लिए एक सरल विधि का उपयोग करता है, कुछ उन्नत टर्मिनल सुविधाओं को दरकिनार करते हुए।" - }, - "compressProgressBar": { - "label": "प्रगति बार आउटपुट संपीड़ित करें", - "description": "जब सक्षम किया जाता है, तो कैरिज रिटर्न (\\r) के साथ टर्मिनल आउटपुट को संसाधित करता है, जो वास्तविक टर्मिनल द्वारा सामग्री प्रदर्शित करने के तरीके का अनुकरण करता है। यह प्रगति बार के मध्यवर्ती स्थितियों को हटाता है, केवल अंतिम स्थिति को बनाए रखता है, जिससे अधिक प्रासंगिक जानकारी के लिए संदर्भ स्थान संरक्षित होता है।" - }, - "zdotdir": { - "label": "ZDOTDIR प्रबंधन सक्षम करें", - "description": "सक्षम होने पर, zsh शेल एकीकरण को सही ढंग से संभालने के लिए ZDOTDIR के लिए एक अस्थायी डायरेक्टरी बनाता है। यह आपके zsh कॉन्फ़िगरेशन को बनाए रखते हुए VSCode शेल एकीकरण को zsh के साथ सही ढंग से काम करने की सुनिश्चितता करता है। (प्रयोगात्मक)" + "description": "इसे सक्षम करें यदि टर्मिनल कमांड सही ढंग से काम नहीं कर रहे हैं या आपको 'शेल एकीकरण अनुपलब्ध' त्रुटियाँ दिखाई देती हैं। यह कमांड चलाने के लिए एक सरल विधि का उपयोग करता है, कुछ उन्नत टर्मिनल सुविधाओं को दरकिनार करते हुए। <0>अधिक जानें" }, "commandDelay": { "label": "टर्मिनल कमांड विलंब", - "description": "कमांड निष्पादन के बाद जोड़ने के लिए मिलीसेकंड में विलंब। 0 का डिफ़ॉल्ट सेटिंग विलंब को पूरी तरह से अक्षम कर देता है। यह टाइमिंग समस्याओं वाले टर्मिनलों में कमांड आउटपुट को पूरी तरह से कैप्चर करने में मदद कर सकता है। अधिकांश टर्मिनलों में यह `PROMPT_COMMAND='sleep N'` सेट करके कार्यान्वित किया जाता है और Powershell प्रत्येक कमांड के अंत में `start-sleep` जोड़ता है। मूल रूप से यह VSCode बग#237208 के लिए एक समाधान था और इसकी आवश्यकता नहीं हो सकती है।" + "description": "कमांड निष्पादन के बाद जोड़ने के लिए मिलीसेकंड में विलंब। 0 का डिफ़ॉल्ट सेटिंग विलंब को पूरी तरह से अक्षम कर देता है। यह टाइमिंग समस्याओं वाले टर्मिनलों में कमांड आउटपुट को पूरी तरह से कैप्चर करने में मदद कर सकता है। अधिकांश टर्मिनलों में यह `PROMPT_COMMAND='sleep N'` सेट करके कार्यान्वित किया जाता है और Powershell प्रत्येक कमांड के अंत में `start-sleep` जोड़ता है। मूल रूप से यह VSCode बग#237208 के लिए एक समाधान था और इसकी आवश्यकता नहीं हो सकती है। <0>अधिक जानें" + }, + "compressProgressBar": { + "label": "प्रगति बार आउटपुट संपीड़ित करें", + "description": "जब सक्षम किया जाता है, तो कैरिज रिटर्न (\\r) के साथ टर्मिनल आउटपुट को संसाधित करता है, जो वास्तविक टर्मिनल द्वारा सामग्री प्रदर्शित करने के तरीके का अनुकरण करता है। यह प्रगति बार के मध्यवर्ती स्थितियों को हटाता है, केवल अंतिम स्थिति को बनाए रखता है, जिससे अधिक प्रासंगिक जानकारी के लिए संदर्भ स्थान संरक्षित होता है। <0>अधिक जानें" }, "powershellCounter": { "label": "PowerShell काउंटर समाधान सक्षम करें", - "description": "सक्षम होने पर, कमांड के सही निष्पादन को सुनिश्चित करने के लिए PowerShell कमांड में एक काउंटर जोड़ता है। यह उन PowerShell टर्मिनलों के साथ मदद करता है जिनमें आउटपुट कैप्चर करने में समस्याएं हो सकती हैं।" + "description": "सक्षम होने पर, कमांड के सही निष्पादन को सुनिश्चित करने के लिए PowerShell कमांड में एक काउंटर जोड़ता है। यह उन PowerShell टर्मिनलों के साथ मदद करता है जिनमें आउटपुट कैप्चर करने में समस्याएं हो सकती हैं। <0>अधिक जानें" }, "zshClearEolMark": { "label": "ZSH EOL मार्क साफ़ करें", - "description": "सक्षम होने पर, PROMPT_EOL_MARK='' सेट करके ZSH लाइन-समाप्ति मार्क को साफ़ करता है। यह कमांड आउटपुट की व्याख्या में समस्याओं को रोकता है जब आउटपुट '%' जैसे विशेष वर्णों के साथ समाप्त होता है।" + "description": "सक्षम होने पर, PROMPT_EOL_MARK='' सेट करके ZSH लाइन-समाप्ति मार्क को साफ़ करता है। यह कमांड आउटपुट की व्याख्या में समस्याओं को रोकता है जब आउटपुट '%' जैसे विशेष वर्णों के साथ समाप्त होता है। <0>अधिक जानें" }, "zshOhMy": { "label": "Oh My Zsh एकीकरण सक्षम करें", - "description": "सक्षम होने पर, Oh My Zsh शेल एकीकरण सुविधाओं को सक्षम करने के लिए ITERM_SHELL_INTEGRATION_INSTALLED=Yes सेट करता है। इस सेटिंग को लागू करने के लिए IDE को पुनरारंभ करने की आवश्यकता हो सकती है। (प्रयोगात्मक)" + "description": "सक्षम होने पर, Oh My Zsh शेल एकीकरण सुविधाओं को सक्षम करने के लिए ITERM_SHELL_INTEGRATION_INSTALLED=Yes सेट करता है। इस सेटिंग को लागू करने के लिए IDE को पुनरारंभ करने की आवश्यकता हो सकती है। <0>अधिक जानें" }, "zshP10k": { "label": "Powerlevel10k एकीकरण सक्षम करें", - "description": "सक्षम होने पर, Powerlevel10k शेल एकीकरण सुविधाओं को सक्षम करने के लिए POWERLEVEL9K_TERM_SHELL_INTEGRATION=true सेट करता है। (प्रयोगात्मक)" + "description": "सक्षम होने पर, Powerlevel10k शेल एकीकरण सुविधाओं को सक्षम करने के लिए POWERLEVEL9K_TERM_SHELL_INTEGRATION=true सेट करता है। <0>अधिक जानें" + }, + "zdotdir": { + "label": "ZDOTDIR प्रबंधन सक्षम करें", + "description": "सक्षम होने पर, zsh शेल एकीकरण को सही ढंग से संभालने के लिए ZDOTDIR के लिए एक अस्थायी डायरेक्टरी बनाता है। यह आपके zsh कॉन्फ़िगरेशन को बनाए रखते हुए VSCode शेल एकीकरण को zsh के साथ सही ढंग से काम करने की सुनिश्चितता करता है। <0>अधिक जानें" + }, + "inheritEnv": { + "label": "पर्यावरण चर विरासत में लें", + "description": "सक्षम होने पर, टर्मिनल VSCode के मूल प्रक्रिया से पर्यावरण चर विरासत में लेता है, जैसे उपयोगकर्ता प्रोफ़ाइल में परिभाषित शेल एकीकरण सेटिंग्स। यह VSCode की वैश्विक सेटिंग `terminal.integrated.inheritEnv` को सीधे टॉगल करता है। <0>अधिक जानें" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "संदर्भ विंडो को बुद्धिमानी से संघनित करें", + "description": "जब कार्य का संदर्भ विंडो लगभग भर जाता है, तो पुराने संदेशों को हटाने के बजाय पिछली बातचीत को संक्षेप में प्रस्तुत करने के लिए LLM कॉल का उपयोग करता है। अस्वीकरण: संक्षेपण की लागत वर्तमान में UI में दिखाए गए API लागतों में शामिल नहीं है।" + }, "DIFF_STRATEGY_UNIFIED": { "name": "प्रायोगिक एकीकृत diff रणनीति का उपयोग करें", "description": "प्रायोगिक एकीकृत diff रणनीति सक्षम करें। यह रणनीति मॉडल त्रुटियों के कारण पुनः प्रयासों की संख्या को कम कर सकती है, लेकिन अप्रत्याशित व्यवहार या गलत संपादन का कारण बन सकती है। केवल तभी सक्षम करें जब आप जोखिमों को समझते हों और सभी परिवर्तनों की सावधानीपूर्वक समीक्षा करने के लिए तैयार हों।" @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "प्रॉम्प्ट कैशिंग सक्षम करें", - "description": "जब सक्षम किया जाता है, तो Roo लागत को कम करने के लिए प्रॉम्प्ट कैशिंग चालू के साथ इस मॉडल का उपयोग करेगा।" + "label": "प्रॉम्प्ट कैशिंग अक्षम करें", + "description": "जब चेक किया जाता है, तो Roo इस मॉडल के लिए प्रॉम्प्ट कैशिंग का उपयोग नहीं करेगा।" }, "temperature": { "useCustom": "कस्टम तापमान का उपयोग करें", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "यदि आपके कोई प्रश्न या प्रतिक्रिया है, तो github.com/RooVetGit/Roo-Code पर एक मुद्दा खोलने या reddit.com/r/RooCode या discord.gg/roocode में शामिल होने में संकोच न करें", - "version": "Agent v{{version}}", "telemetry": { "label": "गुमनाम त्रुटि और उपयोग रिपोर्टिंग की अनुमति दें", "description": "गुमनाम उपयोग डेटा और त्रुटि रिपोर्ट भेजकर Roo Code को बेहतर बनाने में मदद करें। कोड, प्रॉम्प्ट, या व्यक्तिगत जानकारी कभी भी नहीं भेजी जाती है। अधिक विवरण के लिए हमारी गोपनीयता नीति देखें।" diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index e34e370db1e..3d282c3a276 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -106,13 +106,13 @@ "edit": "Modifica...", "forNextMode": "per la prossima modalità", "instructions": { - "wantsToFetch": "Agent vuole recuperare istruzioni dettagliate per aiutare con l'attività corrente" + "wantsToFetch": "Roo vuole recuperare istruzioni dettagliate per aiutare con l'attività corrente" }, "error": "Errore", "diffError": { "title": "Modifica non riuscita" }, - "troubleMessage": "Agent sta avendo problemi...", + "troubleMessage": "Roo sta avendo problemi...", "apiRequest": { "title": "Richiesta API", "failed": "Richiesta API fallita", @@ -138,44 +138,44 @@ "current": "Corrente" }, "fileOperations": { - "wantsToRead": "Agent vuole leggere questo file:", - "wantsToReadOutsideWorkspace": "Agent vuole leggere questo file al di fuori dell'area di lavoro:", - "didRead": "Agent ha letto questo file:", - "wantsToEdit": "Agent vuole modificare questo file:", - "wantsToEditOutsideWorkspace": "Agent vuole modificare questo file al di fuori dell'area di lavoro:", - "wantsToCreate": "Agent vuole creare un nuovo file:", - "wantsToSearchReplace": "Agent vuole eseguire ricerca e sostituzione in questo file:", - "didSearchReplace": "Agent ha eseguito ricerca e sostituzione in questo file:", - "wantsToInsert": "Agent vuole inserire contenuto in questo file:", - "wantsToInsertWithLineNumber": "Agent vuole inserire contenuto in questo file alla riga {{lineNumber}}:", - "wantsToInsertAtEnd": "Agent vuole aggiungere contenuto alla fine di questo file:" + "wantsToRead": "Roo vuole leggere questo file:", + "wantsToReadOutsideWorkspace": "Roo vuole leggere questo file al di fuori dell'area di lavoro:", + "didRead": "Roo ha letto questo file:", + "wantsToEdit": "Roo vuole modificare questo file:", + "wantsToEditOutsideWorkspace": "Roo vuole modificare questo file al di fuori dell'area di lavoro:", + "wantsToCreate": "Roo vuole creare un nuovo file:", + "wantsToSearchReplace": "Roo vuole eseguire ricerca e sostituzione in questo file:", + "didSearchReplace": "Roo ha eseguito ricerca e sostituzione in questo file:", + "wantsToInsert": "Roo vuole inserire contenuto in questo file:", + "wantsToInsertWithLineNumber": "Roo vuole inserire contenuto in questo file alla riga {{lineNumber}}:", + "wantsToInsertAtEnd": "Roo vuole aggiungere contenuto alla fine di questo file:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent vuole visualizzare i file di primo livello in questa directory:", - "didViewTopLevel": "Agent ha visualizzato i file di primo livello in questa directory:", - "wantsToViewRecursive": "Agent vuole visualizzare ricorsivamente tutti i file in questa directory:", - "didViewRecursive": "Agent ha visualizzato ricorsivamente tutti i file in questa directory:", - "wantsToViewDefinitions": "Agent vuole visualizzare i nomi delle definizioni di codice sorgente utilizzate in questa directory:", - "didViewDefinitions": "Agent ha visualizzato i nomi delle definizioni di codice sorgente utilizzate in questa directory:", - "wantsToSearch": "Agent vuole cercare in questa directory {{regex}}:", - "didSearch": "Agent ha cercato in questa directory {{regex}}:" + "wantsToViewTopLevel": "Roo vuole visualizzare i file di primo livello in questa directory:", + "didViewTopLevel": "Roo ha visualizzato i file di primo livello in questa directory:", + "wantsToViewRecursive": "Roo vuole visualizzare ricorsivamente tutti i file in questa directory:", + "didViewRecursive": "Roo ha visualizzato ricorsivamente tutti i file in questa directory:", + "wantsToViewDefinitions": "Roo vuole visualizzare i nomi delle definizioni di codice sorgente utilizzate in questa directory:", + "didViewDefinitions": "Roo ha visualizzato i nomi delle definizioni di codice sorgente utilizzate in questa directory:", + "wantsToSearch": "Roo vuole cercare in questa directory {{regex}}:", + "didSearch": "Roo ha cercato in questa directory {{regex}}:" }, "commandOutput": "Output del comando", "response": "Risposta", "arguments": "Argomenti", "mcp": { - "wantsToUseTool": "Agent vuole utilizzare uno strumento sul server MCP {{serverName}}:", - "wantsToAccessResource": "Agent vuole accedere a una risorsa sul server MCP {{serverName}}:" + "wantsToUseTool": "Roo vuole utilizzare uno strumento sul server MCP {{serverName}}:", + "wantsToAccessResource": "Roo vuole accedere a una risorsa sul server MCP {{serverName}}:" }, "modes": { - "wantsToSwitch": "Agent vuole passare alla modalità {{mode}}", - "wantsToSwitchWithReason": "Agent vuole passare alla modalità {{mode}} perché: {{reason}}", - "didSwitch": "Agent è passato alla modalità {{mode}}", - "didSwitchWithReason": "Agent è passato alla modalità {{mode}} perché: {{reason}}" + "wantsToSwitch": "Roo vuole passare alla modalità {{mode}}", + "wantsToSwitchWithReason": "Roo vuole passare alla modalità {{mode}} perché: {{reason}}", + "didSwitch": "Roo è passato alla modalità {{mode}}", + "didSwitchWithReason": "Roo è passato alla modalità {{mode}} perché: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent vuole creare una nuova sottoattività in modalità {{mode}}:", - "wantsToFinish": "Agent vuole completare questa sottoattività", + "wantsToCreate": "Roo vuole creare una nuova sottoattività in modalità {{mode}}:", + "wantsToFinish": "Roo vuole completare questa sottoattività", "newTaskContent": "Istruzioni sottoattività", "completionContent": "Sottoattività completata", "resultContent": "Risultati sottoattività", @@ -183,7 +183,7 @@ "completionInstructions": "Sottoattività completata! Puoi rivedere i risultati e suggerire correzioni o prossimi passi. Se tutto sembra a posto, conferma per restituire il risultato all'attività principale." }, "questions": { - "hasQuestion": "Agent ha una domanda:" + "hasQuestion": "Roo ha una domanda:" }, "taskCompleted": "Attività completata", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "Copia nell'input (o Shift + clic)" }, "announcement": { - "title": "🎉 Rilasciato Roo Code 3.15", - "description": "Agent Code 3.15 porta nuove funzionalità e miglioramenti basati sui tuoi feedback.", + "title": "🎉 Rilasciato Roo Code 3.17", + "description": "Roo Code 3.17 introduce potenti nuove funzionalità e miglioramenti basati sui tuoi feedback.", "whatsNew": "Novità", - "feature1": "Caching dei prompt per Vertex: Aggiunto supporto per il caching dei prompt per Vertex AI, migliorando i tempi di risposta e riducendo i costi API", - "feature2": "Fallback del Terminale: Implementato un meccanismo di fallback quando l'integrazione shell del terminale VSCode fallisce", - "feature3": "Frammenti di codice migliorati: Migliorato il rendering e l'interazione dei frammenti di codice nell'interfaccia di chat", + "feature1": "Caching implicito per Gemini: Le chiamate API Gemini vengono ora memorizzate automaticamente nella cache, riducendo i costi API", + "feature2": "Selezione della modalità più intelligente: Le definizioni delle modalità possono ora includere indicazioni su quando ogni modalità dovrebbe essere utilizzata, permettendo una migliore orchestrazione", + "feature3": "Condensazione intelligente del contesto: Riassume in modo intelligente la cronologia delle conversazioni quando il contesto si riempie invece di troncarla (abilitare in Impostazioni -> Sperimentale)", "hideButton": "Nascondi annuncio", "detailsDiscussLinks": "Ottieni maggiori dettagli e partecipa alle discussioni su Discord e Reddit 🚀" }, "browser": { - "rooWantsToUse": "Agent vuole utilizzare il browser:", + "rooWantsToUse": "Roo vuole utilizzare il browser:", "consoleLogs": "Log della console", "noNewLogs": "(Nessun nuovo log)", "screenshot": "Screenshot del browser", @@ -233,6 +233,15 @@ "close": "Chiudi browser" } }, + "codeblock": { + "tooltips": { + "expand": "Espandi blocco di codice", + "collapse": "Comprimi blocco di codice", + "enable_wrap": "Attiva a capo automatico", + "disable_wrap": "Disattiva a capo automatico", + "copy_code": "Copia codice" + } + }, "systemPromptWarning": "ATTENZIONE: Sovrascrittura personalizzata delle istruzioni di sistema attiva. Questo può compromettere gravemente le funzionalità e causare comportamenti imprevedibili.", "shellIntegration": { "title": "Avviso di esecuzione comando", diff --git a/webview-ui/src/i18n/locales/it/mcp.json b/webview-ui/src/i18n/locales/it/mcp.json index 18bc9b487ec..efd7131db05 100644 --- a/webview-ui/src/i18n/locales/it/mcp.json +++ b/webview-ui/src/i18n/locales/it/mcp.json @@ -1,17 +1,19 @@ { "title": "Server MCP", "done": "Fatto", - "description": "Il <0>Model Context Protocol permette la comunicazione con server MCP in esecuzione locale che forniscono strumenti e risorse aggiuntive per estendere le capacità di Roo. Puoi utilizzare <1>server creati dalla comunità o chiedere a Roo di creare nuovi strumenti specifici per il tuo flusso di lavoro (ad esempio, \"aggiungi uno strumento che ottiene la documentazione npm più recente\").", + "description": "Abilita il Model Context Protocol (MCP) per permettere a Roo Code di usare strumenti e servizi extra da server esterni. Questo amplia ciò che Roo può fare per te. <0>Scopri di più", "enableToggle": { "title": "Abilita server MCP", - "description": "Quando abilitato, Roo sarà in grado di interagire con i server MCP per funzionalità avanzate. Se non stai utilizzando MCP, puoi disabilitare questa opzione per ridurre l'utilizzo di token da parte di Roo." + "description": "Attiva questa opzione per permettere a Roo di usare strumenti dai server MCP collegati. Questo dà a Roo più capacità. Se non vuoi usare questi strumenti extra, disattiva per ridurre i costi dei token API." }, "enableServerCreation": { "title": "Abilita creazione server MCP", - "description": "Quando abilitato, Roo può aiutarti a creare nuovi server MCP tramite comandi come \"aggiungi un nuovo strumento per...\". Se non hai bisogno di creare server MCP, puoi disabilitare questa opzione per ridurre l'utilizzo di token da parte di Roo." + "description": "Abilita questa opzione per farti aiutare da Roo a creare <1>nuovi server MCP personalizzati. <0>Scopri di più sulla creazione di server", + "hint": "Suggerimento: Per ridurre i costi dei token API, disattiva questa impostazione quando non chiedi a Roo di creare un nuovo server MCP." }, - "editGlobalMCP": "Modifica MCP Globale", - "editProjectMCP": "Modifica MCP del Progetto", + "editGlobalMCP": "Modifica MCP globale", + "editProjectMCP": "Modifica MCP del progetto", + "learnMoreEditingSettings": "Scopri di più sulla modifica dei file di configurazione MCP", "tool": { "alwaysAllow": "Consenti sempre", "parameters": "Parametri", @@ -19,11 +21,14 @@ }, "tabs": { "tools": "Strumenti", - "resources": "Risorse" + "resources": "Risorse", + "errors": "Errori" }, "emptyState": { "noTools": "Nessuno strumento trovato", - "noResources": "Nessuna risorsa trovata" + "noResources": "Nessuna risorsa trovata", + "noLogs": "Nessun log trovato", + "noErrors": "Nessun errore trovato" }, "networkTimeout": { "label": "Timeout di rete", @@ -46,7 +51,7 @@ "delete": "Elimina" }, "serverStatus": { - "retrying": "Nuovo tentativo...", + "retrying": "Riprovo...", "retryConnection": "Riprova connessione" } } diff --git a/webview-ui/src/i18n/locales/it/prompts.json b/webview-ui/src/i18n/locales/it/prompts.json index d9e4ce12966..525f14c5c93 100644 --- a/webview-ui/src/i18n/locales/it/prompts.json +++ b/webview-ui/src/i18n/locales/it/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Crea nuova modalità", "editModesConfig": "Modifica configurazione modalità", "editGlobalModes": "Modifica modalità globali", - "editProjectModes": "Modifica modalità di progetto (.pearai-agent-ignore)", - "createModeHelpText": "Clicca sul + per creare una nuova modalità personalizzata, o chiedi semplicemente a Roo nella chat di crearne una per te!" + "editProjectModes": "Modifica modalità di progetto (.roomodes)", + "createModeHelpText": "Le modalità sono personas specializzate che personalizzano il comportamento di Roo. <0>Scopri di più sull'uso delle modalità o <1>sulla personalizzazione delle modalità.", + "selectMode": "Cerca modalità" }, "apiConfiguration": { "title": "Configurazione API", @@ -33,16 +34,21 @@ "resetToDefault": "Ripristina predefiniti", "description": "Definisci l'esperienza e la personalità di Roo per questa modalità. Questa descrizione modella come Roo si presenta e affronta i compiti." }, + "whenToUse": { + "title": "Quando utilizzare (opzionale)", + "description": "Descrivi quando questa modalità dovrebbe essere utilizzata. Questo aiuta l'Orchestrator a scegliere la modalità giusta per un compito.", + "resetToDefault": "Ripristina la descrizione 'Quando utilizzare' ai valori predefiniti" + }, "customInstructions": { "title": "Istruzioni personalizzate specifiche per la modalità (opzionale)", "resetToDefault": "Ripristina predefiniti", "description": "Aggiungi linee guida comportamentali specifiche per la modalità {{modeName}}.", - "loadFromFile": "Le istruzioni personalizzate specifiche per la modalità {{mode}} possono essere caricate anche dalla cartella .pearai-agent/rules-{{slug}}/ nel tuo spazio di lavoro (.roorules-{{slug}} e .clinerules-{{slug}} sono obsoleti e smetteranno di funzionare presto)." + "loadFromFile": "Le istruzioni personalizzate specifiche per la modalità {{mode}} possono essere caricate anche dalla cartella .roo/rules-{{slug}}/ nel tuo spazio di lavoro (.roorules-{{slug}} e .clinerules-{{slug}} sono obsoleti e smetteranno di funzionare presto)." }, "globalCustomInstructions": { "title": "Istruzioni personalizzate per tutte le modalità", - "description": "Queste istruzioni si applicano a tutte le modalità. Forniscono un insieme base di comportamenti che possono essere migliorati dalle istruzioni specifiche per modalità qui sotto.\nSe desideri che Roo pensi e parli in una lingua diversa dalla lingua di visualizzazione del tuo editor ({{language}}), puoi specificarlo qui.", - "loadFromFile": "Le istruzioni possono essere caricate anche dalla cartella .pearai-agent/rules/ nel tuo spazio di lavoro (.roorules e .clinerules sono obsoleti e smetteranno di funzionare presto)." + "description": "Queste istruzioni si applicano a tutte le modalità. Forniscono un insieme base di comportamenti che possono essere migliorati dalle istruzioni specifiche per modalità qui sotto. <0>Scopri di più", + "loadFromFile": "Le istruzioni possono essere caricate anche dalla cartella .roo/rules/ nel tuo spazio di lavoro (.roorules e .clinerules sono obsoleti e smetteranno di funzionare presto)." }, "systemPrompt": { "preview": "Anteprima prompt di sistema", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Avanzato: Sovrascrivi prompt di sistema", - "description": "Puoi sostituire completamente il prompt di sistema per questa modalità (a parte la definizione del ruolo e le istruzioni personalizzate) creando un file in .pearai-agent/system-prompt-{{slug}} nel tuo spazio di lavoro. Questa è una funzionalità molto avanzata che bypassa le protezioni integrate e i controlli di coerenza (specialmente riguardo all'uso degli strumenti), quindi fai attenzione!" + "description": "<2>⚠️ Attenzione: Questa funzionalità avanzata bypassa le misure di sicurezza. <1>LEGGI QUESTO PRIMA DI USARE!Sovrascrivi il prompt di sistema predefinito creando un file in .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Crea nuova modalità", @@ -122,7 +128,7 @@ "description": "Disponibile in tutti gli spazi di lavoro" }, "project": { - "label": "Specifico del progetto (.pearai-agent-ignore)", + "label": "Specifico del progetto (.roomodes)", "description": "Disponibile solo in questo spazio di lavoro, ha la precedenza sul globale" } }, @@ -130,6 +136,10 @@ "label": "Definizione del ruolo", "description": "Definisci l'esperienza e la personalità di Roo per questa modalità." }, + "whenToUse": { + "label": "Quando utilizzare (opzionale)", + "description": "Fornisci una chiara descrizione di quando questa modalità è più efficace e per quali tipi di compiti eccelle." + }, "tools": { "label": "Strumenti disponibili", "description": "Seleziona quali strumenti questa modalità può utilizzare." diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 941c3beb481..4502149e8ff 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Fornitori", - "autoApprove": "Approvazione automatica", - "browser": "Browser / Uso del computer", + "autoApprove": "Auto-approvazione", + "browser": "Accesso computer", "checkpoints": "Punti di controllo", "notifications": "Notifiche", - "contextManagement": "Gestione del contesto", + "contextManagement": "Contesto", "terminal": "Terminal", - "advanced": "Avanzate", - "experimental": "Funzionalità sperimentali", + "experimental": "Sperimentale", "language": "Lingua", - "about": "Informazioni su Roo Code", - "interface": "Interfaccia" + "about": "Informazioni su Roo Code" }, "autoApprove": { "description": "Permetti a Roo di eseguire automaticamente operazioni senza richiedere approvazione. Abilita queste impostazioni solo se ti fidi completamente dell'IA e comprendi i rischi di sicurezza associati.", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "Profilo di configurazione", "providerDocumentation": "Documentazione {{provider}}", + "configProfile": "Profilo di configurazione", "description": "Salva diverse configurazioni API per passare rapidamente tra fornitori e impostazioni.", "apiProvider": "Fornitore API", "model": "Modello", @@ -118,14 +116,22 @@ "headerValue": "Valore intestazione", "noCustomHeaders": "Nessuna intestazione personalizzata definita. Fai clic sul pulsante + per aggiungerne una.", "requestyApiKey": "Chiave API Requesty", + "refreshModels": { + "label": "Aggiorna modelli", + "hint": "Riapri le impostazioni per vedere i modelli più recenti." + }, "getRequestyApiKey": "Ottieni chiave API Requesty", "openRouterTransformsText": "Comprimi prompt e catene di messaggi alla dimensione del contesto (Trasformazioni OpenRouter)", "anthropicApiKey": "Chiave API Anthropic", "getAnthropicApiKey": "Ottieni chiave API Anthropic", "anthropicUseAuthToken": "Passa la chiave API Anthropic come header di autorizzazione invece di X-Api-Key", + "chutesApiKey": "Chiave API Chutes", + "getChutesApiKey": "Ottieni chiave API Chutes", "deepSeekApiKey": "Chiave API DeepSeek", "getDeepSeekApiKey": "Ottieni chiave API DeepSeek", "geminiApiKey": "Chiave API Gemini", + "getGroqApiKey": "Ottieni chiave API Groq", + "groqApiKey": "Chiave API Groq", "getGeminiApiKey": "Ottieni chiave API Gemini", "openAiApiKey": "Chiave API OpenAI", "openAiBaseUrl": "URL base", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Imposta un URL opzionale per i modelli Codestral.", "xaiApiKey": "Chiave API xAI", "getXaiApiKey": "Ottieni chiave API xAI", + "litellmApiKey": "Chiave API LiteLLM", + "litellmBaseUrl": "URL base LiteLLM", "awsCredentials": "Credenziali AWS", "awsProfile": "Profilo AWS", "awsProfileName": "Nome profilo AWS", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Abilita strumento browser", - "description": "Quando abilitato, Roo può utilizzare un browser per interagire con siti web quando si utilizzano modelli che supportano l'uso del computer." + "description": "Quando abilitato, Roo può utilizzare un browser per interagire con siti web quando si utilizzano modelli che supportano l'uso del computer. <0>Scopri di più" }, "viewport": { "label": "Dimensione viewport", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Abilita punti di controllo automatici", - "description": "Quando abilitato, Roo creerà automaticamente punti di controllo durante l'esecuzione dei compiti, facilitando la revisione delle modifiche o il ritorno a stati precedenti." + "description": "Quando abilitato, Roo creerà automaticamente punti di controllo durante l'esecuzione dei compiti, facilitando la revisione delle modifiche o il ritorno a stati precedenti. <0>Scopri di più" } }, "notifications": { @@ -298,57 +306,69 @@ "label": "Limite contesto file area di lavoro", "description": "Numero massimo di file da includere nei dettagli della directory di lavoro corrente. Valori più alti forniscono più contesto ma aumentano l'utilizzo di token." }, - "pearai-agent-ignore": { - "label": "Mostra file .pearai-agent-ignore negli elenchi e nelle ricerche", - "description": "Quando abilitato, i file che corrispondono ai pattern in .pearai-agent-ignore verranno mostrati negli elenchi con un simbolo di blocco. Quando disabilitato, questi file saranno completamente nascosti dagli elenchi di file e dalle ricerche." + "rooignore": { + "label": "Mostra file .rooignore negli elenchi e nelle ricerche", + "description": "Quando abilitato, i file che corrispondono ai pattern in .rooignore verranno mostrati negli elenchi con un simbolo di blocco. Quando disabilitato, questi file saranno completamente nascosti dagli elenchi di file e dalle ricerche." }, "maxReadFile": { "label": "Soglia di auto-troncamento lettura file", - "description": "Agent legge questo numero di righe quando il modello omette i valori di inizio/fine. Se questo numero è inferiore al totale del file, Roo genera un indice dei numeri di riga delle definizioni di codice. Casi speciali: -1 indica a Roo di leggere l'intero file (senza indicizzazione), e 0 indica di non leggere righe e fornire solo indici di riga per un contesto minimo. Valori più bassi minimizzano l'utilizzo iniziale del contesto, permettendo successive letture precise di intervalli di righe. Le richieste con inizio/fine espliciti non sono limitate da questa impostazione.", + "description": "Roo legge questo numero di righe quando il modello omette i valori di inizio/fine. Se questo numero è inferiore al totale del file, Roo genera un indice dei numeri di riga delle definizioni di codice. Casi speciali: -1 indica a Roo di leggere l'intero file (senza indicizzazione), e 0 indica di non leggere righe e fornire solo indici di riga per un contesto minimo. Valori più bassi minimizzano l'utilizzo iniziale del contesto, permettendo successive letture precise di intervalli di righe. Le richieste con inizio/fine espliciti non sono limitate da questa impostazione.", "lines": "righe", "always_full_read": "Leggi sempre l'intero file" } }, "terminal": { + "basic": { + "label": "Impostazioni terminale: Base", + "description": "Impostazioni base del terminale" + }, + "advanced": { + "label": "Impostazioni terminale: Avanzate", + "description": "Le seguenti opzioni potrebbero richiedere il riavvio del terminale per applicare l'impostazione." + }, "outputLineLimit": { "label": "Limite output terminale", - "description": "Numero massimo di righe da includere nell'output del terminale durante l'esecuzione dei comandi. Quando superato, le righe verranno rimosse dal centro, risparmiando token." + "description": "Numero massimo di righe da includere nell'output del terminale durante l'esecuzione dei comandi. Quando superato, le righe verranno rimosse dal centro, risparmiando token. <0>Scopri di più" }, "shellIntegrationTimeout": { "label": "Timeout integrazione shell del terminale", - "description": "Tempo massimo di attesa per l'inizializzazione dell'integrazione della shell prima di eseguire i comandi. Per gli utenti con tempi di avvio della shell lunghi, questo valore potrebbe dover essere aumentato se si vedono errori \"Shell Integration Unavailable\" nel terminale." + "description": "Tempo massimo di attesa per l'inizializzazione dell'integrazione della shell prima di eseguire i comandi. Per gli utenti con tempi di avvio della shell lunghi, questo valore potrebbe dover essere aumentato se si vedono errori \"Shell Integration Unavailable\" nel terminale. <0>Scopri di più" }, "shellIntegrationDisabled": { "label": "Disabilita l'integrazione della shell del terminale", - "description": "Abilita questa opzione se i comandi del terminale non funzionano correttamente o se vedi errori 'Shell Integration Unavailable'. Questo utilizza un metodo più semplice per eseguire i comandi, bypassando alcune funzionalità avanzate del terminale." - }, - "compressProgressBar": { - "label": "Comprimi output barre di progresso", - "description": "Quando abilitato, elabora l'output del terminale con ritorni a capo (\\r) per simulare come un terminale reale visualizzerebbe il contenuto. Questo rimuove gli stati intermedi delle barre di progresso, mantenendo solo lo stato finale, il che conserva spazio di contesto per informazioni più rilevanti." - }, - "zdotdir": { - "label": "Abilita gestione ZDOTDIR", - "description": "Quando abilitato, crea una directory temporanea per ZDOTDIR per gestire correttamente l'integrazione della shell zsh. Questo assicura che l'integrazione della shell VSCode funzioni correttamente con zsh mantenendo la tua configurazione zsh. (sperimentale)" + "description": "Abilita questa opzione se i comandi del terminale non funzionano correttamente o se vedi errori 'Shell Integration Unavailable'. Questo utilizza un metodo più semplice per eseguire i comandi, bypassando alcune funzionalità avanzate del terminale. <0>Scopri di più" }, "commandDelay": { "label": "Ritardo comando terminale", - "description": "Ritardo in millisecondi da aggiungere dopo l'esecuzione del comando. L'impostazione predefinita di 0 disabilita completamente il ritardo. Questo può aiutare a garantire che l'output del comando sia catturato completamente nei terminali con problemi di temporizzazione. Nella maggior parte dei terminali viene implementato impostando `PROMPT_COMMAND='sleep N'` e Powershell aggiunge `start-sleep` alla fine di ogni comando. In origine era una soluzione per il bug VSCode#237208 e potrebbe non essere necessario." + "description": "Ritardo in millisecondi da aggiungere dopo l'esecuzione del comando. L'impostazione predefinita di 0 disabilita completamente il ritardo. Questo può aiutare a garantire che l'output del comando sia catturato completamente nei terminali con problemi di temporizzazione. Nella maggior parte dei terminali viene implementato impostando `PROMPT_COMMAND='sleep N'` e Powershell aggiunge `start-sleep` alla fine di ogni comando. In origine era una soluzione per il bug VSCode#237208 e potrebbe non essere necessario. <0>Scopri di più" + }, + "compressProgressBar": { + "label": "Comprimi output barre di progresso", + "description": "Quando abilitato, elabora l'output del terminale con ritorni a capo (\\r) per simulare come un terminale reale visualizzerebbe il contenuto. Questo rimuove gli stati intermedi delle barre di progresso, mantenendo solo lo stato finale, il che conserva spazio di contesto per informazioni più rilevanti. <0>Scopri di più" }, "powershellCounter": { "label": "Abilita soluzione temporanea contatore PowerShell", - "description": "Quando abilitato, aggiunge un contatore ai comandi PowerShell per garantire la corretta esecuzione dei comandi. Questo aiuta con i terminali PowerShell che potrebbero avere problemi con la cattura dell'output." + "description": "Quando abilitato, aggiunge un contatore ai comandi PowerShell per garantire la corretta esecuzione dei comandi. Questo aiuta con i terminali PowerShell che potrebbero avere problemi con la cattura dell'output. <0>Scopri di più" }, "zshClearEolMark": { "label": "Cancella marcatore fine riga ZSH", - "description": "Quando abilitato, cancella il marcatore di fine riga ZSH impostando PROMPT_EOL_MARK=''. Questo previene problemi con l'interpretazione dell'output dei comandi quando termina con caratteri speciali come '%'." + "description": "Quando abilitato, cancella il marcatore di fine riga ZSH impostando PROMPT_EOL_MARK=''. Questo previene problemi con l'interpretazione dell'output dei comandi quando termina con caratteri speciali come '%'. <0>Scopri di più" }, "zshOhMy": { "label": "Abilita integrazione Oh My Zsh", - "description": "Quando abilitato, imposta ITERM_SHELL_INTEGRATION_INSTALLED=Yes per abilitare le funzionalità di integrazione della shell Oh My Zsh. L'applicazione di questa impostazione potrebbe richiedere il riavvio dell'IDE. (sperimentale)" + "description": "Quando abilitato, imposta ITERM_SHELL_INTEGRATION_INSTALLED=Yes per abilitare le funzionalità di integrazione della shell Oh My Zsh. L'applicazione di questa impostazione potrebbe richiedere il riavvio dell'IDE. <0>Scopri di più" }, "zshP10k": { "label": "Abilita integrazione Powerlevel10k", - "description": "Quando abilitato, imposta POWERLEVEL9K_TERM_SHELL_INTEGRATION=true per abilitare le funzionalità di integrazione della shell Powerlevel10k. (sperimentale)" + "description": "Quando abilitato, imposta POWERLEVEL9K_TERM_SHELL_INTEGRATION=true per abilitare le funzionalità di integrazione della shell Powerlevel10k. <0>Scopri di più" + }, + "zdotdir": { + "label": "Abilita gestione ZDOTDIR", + "description": "Quando abilitato, crea una directory temporanea per ZDOTDIR per gestire correttamente l'integrazione della shell zsh. Questo assicura che l'integrazione della shell VSCode funzioni correttamente con zsh mantenendo la tua configurazione zsh. <0>Scopri di più" + }, + "inheritEnv": { + "label": "Eredita variabili d'ambiente", + "description": "Quando abilitato, il terminale eredita le variabili d'ambiente dal processo padre di VSCode, come le impostazioni di integrazione della shell definite nel profilo utente. Questo attiva direttamente l'impostazione globale di VSCode `terminal.integrated.inheritEnv`. <0>Scopri di più" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Condensa intelligentemente la finestra di contesto", + "description": "Utilizza una chiamata LLM per riassumere la conversazione precedente quando la finestra di contesto dell'attività è quasi piena, invece di eliminare i messaggi vecchi. Avviso: il costo della sintesi non è attualmente incluso nei costi API mostrati nell'interfaccia." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Usa strategia diff unificata sperimentale", "description": "Abilita la strategia diff unificata sperimentale. Questa strategia potrebbe ridurre il numero di tentativi causati da errori del modello, ma può causare comportamenti imprevisti o modifiche errate. Abilitala solo se comprendi i rischi e sei disposto a rivedere attentamente tutte le modifiche." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Abilita cache dei prompt", - "description": "Quando abilitato, Roo utilizzerà questo modello con la cache dei prompt attivata per ridurre i costi." + "label": "Disattiva la cache dei prompt", + "description": "Quando selezionato, Roo non utilizzerà la cache dei prompt per questo modello." }, "temperature": { "useCustom": "Usa temperatura personalizzata", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Se hai domande o feedback, sentiti libero di aprire un issue su github.com/RooVetGit/Roo-Code o unirti a reddit.com/r/RooCode o discord.gg/roocode", - "version": "Agent v{{version}}", "telemetry": { "label": "Consenti segnalazioni anonime di errori e utilizzo", "description": "Aiuta a migliorare Roo Code inviando dati di utilizzo anonimi e segnalazioni di errori. Non vengono mai inviati codice, prompt o informazioni personali. Consulta la nostra politica sulla privacy per maggiori dettagli." diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index dd34567bf3a..7b5fc2aebbd 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -1,5 +1,5 @@ { - "greeting": "Agent Code へようこそ", + "greeting": "Roo Code へようこそ", "task": { "title": "タスク", "seeMore": "もっと見る", @@ -202,12 +202,12 @@ "copyToInput": "入力欄にコピー(またはShift + クリック)" }, "announcement": { - "title": "🎉 Roo Code 3.15 リリース", - "description": "Agent Code 3.15は新機能とあなたのフィードバックに基づく改善をもたらします。", + "title": "🎉 Roo Code 3.17 リリース", + "description": "Roo Code 3.17は、あなたのフィードバックに基づく強力な新機能と改善をもたらします。", "whatsNew": "新機能", - "feature1": "Vertex用プロンプトキャッシング: Vertex AI向けのプロンプトキャッシングサポートを追加し、応答時間を改善しAPIコストを削減", - "feature2": "ターミナルフォールバック: VSCodeターミナルシェル統合が失敗した場合のフォールバックメカニズムを実装", - "feature3": "コードスニペットの改善: チャットインターフェースでのコードスニペットのレンダリングと操作性を向上", + "feature1": "Geminiの暗黙的キャッシング: Gemini APIコールが自動的にキャッシュされるようになり、APIコストが削減されます", + "feature2": "よりスマートなモード選択: モード定義に各モードをいつ使用すべきかの指針を含めることができるようになり、より優れた調整が可能になります", + "feature3": "インテリジェントなコンテキスト圧縮: コンテキストが一杯になったときに切り捨てる代わりに、会話履歴をインテリジェントに要約します(設定 -> 実験的機能で有効化)", "hideButton": "通知を非表示", "detailsDiscussLinks": "詳細はDiscordRedditでご確認・ディスカッションください 🚀" }, @@ -233,6 +233,15 @@ "close": "ブラウザを閉じる" } }, + "codeblock": { + "tooltips": { + "expand": "コードブロックを展開", + "collapse": "コードブロックを折りたたむ", + "enable_wrap": "折り返しを有効化", + "disable_wrap": "折り返しを無効化", + "copy_code": "コードをコピー" + } + }, "systemPromptWarning": "警告:カスタムシステムプロンプトの上書きが有効です。これにより機能が深刻に損なわれ、予測不可能な動作が発生する可能性があります。", "shellIntegration": { "title": "コマンド実行警告", diff --git a/webview-ui/src/i18n/locales/ja/mcp.json b/webview-ui/src/i18n/locales/ja/mcp.json index d5920c1c46f..a36530c1595 100644 --- a/webview-ui/src/i18n/locales/ja/mcp.json +++ b/webview-ui/src/i18n/locales/ja/mcp.json @@ -1,33 +1,38 @@ { "title": "MCPサーバー", "done": "完了", - "description": "<0>Model Context Protocolは、ローカルで実行されているMCPサーバーとの通信を可能にし、Rooの機能を拡張するための追加ツールやリソースを提供します。<1>コミュニティによって作成されたサーバーを使用したり、Rooにワークフロー専用の新しいツールを作成するよう依頼したりできます(例:「最新のnpmドキュメントを取得するツールを追加する」)。", + "description": "Model Context Protocol (MCP) を有効にすると、Roo Code が外部サーバーから追加のツールやサービスを利用できるようになります。これで Roo ができることが広がるよ。<0>詳細はこちら", "enableToggle": { - "title": "MCPサーバーを有効にする", - "description": "有効にすると、Rooは高度な機能のためにMCPサーバーと対話できるようになります。MCPを使用していない場合は、これを無効にしてRooのtoken使用量を減らすことができます。" + "title": "MCPサーバーを有効化", + "description": "これをONにすると、Rooが接続されたMCPサーバーのツールを使えるようになるよ。Rooの機能が増える!追加ツールを使わないなら、APIトークンのコストを抑えるためにOFFにしてね。" }, "enableServerCreation": { - "title": "MCPサーバー作成を有効にする", - "description": "有効にすると、Rooは「新しいツールを追加する...」などのコマンドを通じて新しいMCPサーバーの作成を支援できます。MCPサーバーを作成する必要がない場合は、これを無効にしてRooのtoken使用量を減らすことができます。" + "title": "MCPサーバー作成を有効化", + "description": "これをONにすると、Rooが<1>新しいカスタムMCPサーバーを作るのを手伝ってくれるよ。<0>サーバー作成について詳しく", + "hint": "ヒント: APIトークンのコストを抑えたいときは、Rooに新しいMCPサーバーを作らせないときにこの設定をOFFにしてね。" }, "editGlobalMCP": "グローバルMCPを編集", "editProjectMCP": "プロジェクトMCPを編集", + "learnMoreEditingSettings": "MCP設定ファイルの編集方法を詳しく見る", "tool": { "alwaysAllow": "常に許可", - "parameters": "パラメータ", + "parameters": "パラメーター", "noDescription": "説明なし" }, "tabs": { "tools": "ツール", - "resources": "リソース" + "resources": "リソース", + "errors": "エラー" }, "emptyState": { "noTools": "ツールが見つかりません", - "noResources": "リソースが見つかりません" + "noResources": "リソースが見つかりません", + "noLogs": "ログが見つかりません", + "noErrors": "エラーが見つかりません" }, "networkTimeout": { "label": "ネットワークタイムアウト", - "description": "サーバー応答を待つ最大時間", + "description": "サーバー応答の最大待機時間", "options": { "15seconds": "15秒", "30seconds": "30秒", @@ -41,12 +46,12 @@ }, "deleteDialog": { "title": "MCPサーバーを削除", - "description": "MCPサーバー「{{serverName}}」を削除してもよろしいですか?この操作は元に戻せません。", + "description": "本当にMCPサーバー「{{serverName}}」を削除する?この操作は元に戻せないよ。", "cancel": "キャンセル", "delete": "削除" }, "serverStatus": { "retrying": "再試行中...", - "retryConnection": "接続を再試行" + "retryConnection": "再接続" } } diff --git a/webview-ui/src/i18n/locales/ja/prompts.json b/webview-ui/src/i18n/locales/ja/prompts.json index 48c2a030d02..72d256a8baf 100644 --- a/webview-ui/src/i18n/locales/ja/prompts.json +++ b/webview-ui/src/i18n/locales/ja/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "新しいモードを作成", "editModesConfig": "モード設定を編集", "editGlobalModes": "グローバルモードを編集", - "editProjectModes": "プロジェクトモードを編集 (.pearai-agent-ignore)", - "createModeHelpText": "+ をクリックして新しいカスタムモードを作成するか、チャットで Roo に作成を依頼してください!" + "editProjectModes": "プロジェクトモードを編集 (.roomodes)", + "createModeHelpText": "モードはRooの振る舞いを調整する専門的なペルソナです。<0>モードの使用について学ぶか、<1>モードのカスタマイズについて学ぶ。", + "selectMode": "モードを検索" }, "apiConfiguration": { "title": "API設定", @@ -33,16 +34,21 @@ "resetToDefault": "デフォルトにリセット", "description": "このモードのRooの専門知識と個性を定義します。この説明は、Rooが自身をどのように表現し、タスクにどのように取り組むかを形作ります。" }, + "whenToUse": { + "title": "使用タイミング(オプション)", + "description": "このモードをいつ使用すべきかを説明します。これはOrchestratorがタスクに適切なモードを選択するのに役立ちます。", + "resetToDefault": "「使用タイミング」説明をデフォルトにリセット" + }, "customInstructions": { "title": "モード固有のカスタム指示(オプション)", "resetToDefault": "デフォルトにリセット", "description": "{{modeName}}モードに特化した行動ガイドラインを追加します。", - "loadFromFile": "{{mode}}モード固有のカスタム指示は、ワークスペースの.pearai-agent/rules-{{slug}}/フォルダからも読み込めます(.roorules-{{slug}}と.clinerules-{{slug}}は非推奨であり、まもなく機能しなくなります)。" + "loadFromFile": "{{mode}}モード固有のカスタム指示は、ワークスペースの.roo/rules-{{slug}}/フォルダからも読み込めます(.roorules-{{slug}}と.clinerules-{{slug}}は非推奨であり、まもなく機能しなくなります)。" }, "globalCustomInstructions": { "title": "すべてのモードのカスタム指示", - "description": "これらの指示はすべてのモードに適用されます。モード固有の指示で強化できる基本的な動作セットを提供します。\nRooにエディタの表示言語({{language}})とは異なる言語で考えたり話したりさせたい場合は、ここで指定できます。", - "loadFromFile": "指示はワークスペースの.pearai-agent/rules/フォルダからも読み込めます(.roorules と .clinerules は非推奨であり、まもなく機能しなくなります)。" + "description": "これらの指示はすべてのモードに適用されます。モード固有の指示で強化できる基本的な動作セットを提供します。<0>詳細はこちら", + "loadFromFile": "指示はワークスペースの.roo/rules/フォルダからも読み込めます(.roorules と .clinerules は非推奨であり、まもなく機能しなくなります)。" }, "systemPrompt": { "preview": "システムプロンプトのプレビュー", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "詳細設定:システムプロンプトの上書き", - "description": "ワークスペースの.pearai-agent/system-prompt-{{slug}}にファイルを作成することで、このモードのシステムプロンプト(役割定義とカスタム指示以外)を完全に置き換えることができます。これは組み込みの安全対策と一貫性チェック(特にツールの使用に関して)をバイパスする非常に高度な機能なので、注意して使用してください!" + "description": "<2>⚠️ 警告: この高度な機能は安全対策をバイパスします。<1>使用前にこれを読んでください!ワークスペースの.roo/system-prompt-{{slug}}にファイルを作成することで、デフォルトのシステムプロンプトを上書きします。" }, "createModeDialog": { "title": "新しいモードを作成", @@ -122,7 +128,7 @@ "description": "すべてのワークスペースで利用可能" }, "project": { - "label": "プロジェクト固有 (.pearai-agent-ignore)", + "label": "プロジェクト固有 (.roomodes)", "description": "このワークスペースでのみ使用可能、グローバルよりも優先" } }, @@ -130,6 +136,10 @@ "label": "役割の定義", "description": "このモードのRooの専門知識と個性を定義します。" }, + "whenToUse": { + "label": "使用タイミング(オプション)", + "description": "このモードが最も効果的なタイミングと、どのようなタイプのタスクに適しているかを明確に説明します。" + }, "tools": { "label": "利用可能なツール", "description": "このモードが使用できるツールを選択します。" diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index e28c0f65a93..b96053e587e 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -23,16 +23,14 @@ "sections": { "providers": "プロバイダー", "autoApprove": "自動承認", - "browser": "ブラウザ / コンピューター使用", + "browser": "コンピューターアクセス", "checkpoints": "チェックポイント", "notifications": "通知", - "contextManagement": "コンテキスト管理", + "contextManagement": "コンテキスト", "terminal": "ターミナル", - "advanced": "詳細設定", - "experimental": "実験的機能", + "experimental": "実験的", "language": "言語", - "about": "Agentについて", - "interface": "インターフェース" + "about": "Roo Codeについて" }, "autoApprove": { "description": "Rooが承認なしで自動的に操作を実行できるようにします。AIを完全に信頼し、関連するセキュリティリスクを理解している場合にのみ、これらの設定を有効にしてください。", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "設定プロファイル", "providerDocumentation": "{{provider}}のドキュメント", + "configProfile": "設定プロファイル", "description": "異なるAPI設定を保存して、プロバイダーと設定をすばやく切り替えることができます。", "apiProvider": "APIプロバイダー", "model": "モデル", @@ -118,14 +116,22 @@ "headerValue": "ヘッダー値", "noCustomHeaders": "カスタムヘッダーが定義されていません。+ ボタンをクリックして追加してください。", "requestyApiKey": "Requesty APIキー", + "refreshModels": { + "label": "モデルを更新", + "hint": "最新のモデルを表示するには設定を再度開いてください。" + }, "getRequestyApiKey": "Requesty APIキーを取得", "openRouterTransformsText": "プロンプトとメッセージチェーンをコンテキストサイズに圧縮 (OpenRouter Transforms)", "anthropicApiKey": "Anthropic APIキー", "getAnthropicApiKey": "Anthropic APIキーを取得", "anthropicUseAuthToken": "Anthropic APIキーをX-Api-Keyの代わりにAuthorizationヘッダーとして渡す", + "chutesApiKey": "Chutes APIキー", + "getChutesApiKey": "Chutes APIキーを取得", "deepSeekApiKey": "DeepSeek APIキー", "getDeepSeekApiKey": "DeepSeek APIキーを取得", "geminiApiKey": "Gemini APIキー", + "getGroqApiKey": "Groq APIキーを取得", + "groqApiKey": "Groq APIキー", "getGeminiApiKey": "Gemini APIキーを取得", "openAiApiKey": "OpenAI APIキー", "openAiBaseUrl": "ベースURL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Codestralモデルの代替URLを設定します。", "xaiApiKey": "xAI APIキー", "getXaiApiKey": "xAI APIキーを取得", + "litellmApiKey": "LiteLLM APIキー", + "litellmBaseUrl": "LiteLLM ベースURL", "awsCredentials": "AWS認証情報", "awsProfile": "AWSプロファイル", "awsProfileName": "AWSプロファイル名", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "ブラウザツールを有効化", - "description": "有効にすると、コンピューター使用をサポートするモデルを使用する際に、Rooはウェブサイトとのやり取りにブラウザを使用できます。" + "description": "有効にすると、コンピューター使用をサポートするモデルを使用する際に、Rooはウェブサイトとのやり取りにブラウザを使用できます。 <0>詳細情報" }, "viewport": { "label": "ビューポートサイズ", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "自動チェックポイントを有効化", - "description": "有効にすると、Rooはタスク実行中に自動的にチェックポイントを作成し、変更の確認や以前の状態への復帰を容易にします。" + "description": "有効にすると、Rooはタスク実行中に自動的にチェックポイントを作成し、変更の確認や以前の状態への復帰を容易にします。 <0>詳細情報" } }, "notifications": { @@ -298,7 +306,7 @@ "label": "ワークスペースファイルコンテキスト制限", "description": "現在の作業ディレクトリの詳細に含めるファイルの最大数。高い値はより多くのコンテキストを提供しますが、token使用量が増加します。" }, - "pearai-agent-ignore": { + "rooignore": { "label": "リストと検索で.rooignoreファイルを表示", "description": "有効にすると、.rooignoreのパターンに一致するファイルがロックシンボル付きでリストに表示されます。無効にすると、これらのファイルはファイルリストや検索から完全に非表示になります。" }, @@ -310,45 +318,57 @@ } }, "terminal": { + "basic": { + "label": "ターミナル設定:基本", + "description": "基本的なターミナル設定" + }, + "advanced": { + "label": "ターミナル設定:詳細", + "description": "以下のオプションは設定を適用するためにターミナルの再起動が必要な場合があります" + }, "outputLineLimit": { "label": "ターミナル出力制限", - "description": "コマンド実行時にターミナル出力に含める最大行数。超過すると中央から行が削除され、tokenを節約します。" + "description": "コマンド実行時にターミナル出力に含める最大行数。超過すると中央から行が削除され、tokenを節約します。 <0>詳細情報" }, "shellIntegrationTimeout": { "label": "ターミナルシェル統合タイムアウト", - "description": "コマンドを実行する前にシェル統合の初期化を待つ最大時間。シェルの起動時間が長いユーザーの場合、ターミナルで「Shell Integration Unavailable」エラーが表示される場合は、この値を増やす必要があるかもしれません。" + "description": "コマンドを実行する前にシェル統合の初期化を待つ最大時間。シェルの起動時間が長いユーザーの場合、ターミナルで「Shell Integration Unavailable」エラーが表示される場合は、この値を増やす必要があるかもしれません。 <0>詳細情報" }, "shellIntegrationDisabled": { "label": "ターミナルシェル統合を無効にする", - "description": "ターミナルコマンドが正しく機能しない場合や、「シェル統合が利用できません」というエラーが表示される場合は、これを有効にします。これにより、一部の高度なターミナル機能をバイパスして、コマンドを実行するより簡単な方法が使用されます。" - }, - "compressProgressBar": { - "label": "プログレスバー出力を圧縮", - "description": "有効にすると、キャリッジリターン(\\r)を含むターミナル出力を処理して、実際のターミナルがコンテンツを表示する方法をシミュレートします。これによりプログレスバーの中間状態が削除され、最終状態のみが保持されるため、より関連性の高い情報のためのコンテキスト空間が節約されます。" - }, - "zdotdir": { - "label": "ZDOTDIR 処理を有効化", - "description": "有効にすると、zsh シェル統合を適切に処理するために ZDOTDIR 用の一時ディレクトリを作成します。これにより、zsh の設定を保持しながら VSCode のシェル統合が正しく機能します。(実験的)" + "description": "ターミナルコマンドが正しく機能しない場合や、「シェル統合が利用できません」というエラーが表示される場合は、これを有効にします。これにより、一部の高度なターミナル機能をバイパスして、コマンドを実行するより簡単な方法が使用されます。 <0>詳細情報" }, "commandDelay": { "label": "ターミナルコマンド遅延", - "description": "コマンド実行後に追加する遅延時間(ミリ秒)。デフォルト設定の0は遅延を完全に無効にします。これはタイミングの問題があるターミナルでコマンド出力を完全にキャプチャするのに役立ちます。ほとんどのターミナルでは`PROMPT_COMMAND='sleep N'`を設定することで実装され、PowerShellは各コマンドの最後に`start-sleep`を追加します。元々はVSCodeバグ#237208の回避策で、必要ない場合があります。" + "description": "コマンド実行後に追加する遅延時間(ミリ秒)。デフォルト設定の0は遅延を完全に無効にします。これはタイミングの問題があるターミナルでコマンド出力を完全にキャプチャするのに役立ちます。ほとんどのターミナルでは`PROMPT_COMMAND='sleep N'`を設定することで実装され、PowerShellは各コマンドの最後に`start-sleep`を追加します。元々はVSCodeバグ#237208の回避策で、必要ない場合があります。 <0>詳細情報" + }, + "compressProgressBar": { + "label": "プログレスバー出力を圧縮", + "description": "有効にすると、キャリッジリターン(\\r)を含むターミナル出力を処理して、実際のターミナルがコンテンツを表示する方法をシミュレートします。これによりプログレスバーの中間状態が削除され、最終状態のみが保持されるため、より関連性の高い情報のためのコンテキスト空間が節約されます。 <0>詳細情報" }, "powershellCounter": { "label": "PowerShellカウンター回避策を有効化", - "description": "有効にすると、PowerShellコマンドにカウンターを追加して、コマンドの正しい実行を確保します。これは出力のキャプチャに問題がある可能性のあるPowerShellターミナルで役立ちます。" + "description": "有効にすると、PowerShellコマンドにカウンターを追加して、コマンドの正しい実行を確保します。これは出力のキャプチャに問題がある可能性のあるPowerShellターミナルで役立ちます。 <0>詳細情報" }, "zshClearEolMark": { "label": "ZSH行末マークをクリア", - "description": "有効にすると、PROMPT_EOL_MARK=''を設定してZSHの行末マークをクリアします。これにより、'%'などの特殊文字で終わるコマンド出力の解釈に関する問題を防ぎます。" + "description": "有効にすると、PROMPT_EOL_MARK=''を設定してZSHの行末マークをクリアします。これにより、'%'などの特殊文字で終わるコマンド出力の解釈に関する問題を防ぎます。 <0>詳細情報" }, "zshOhMy": { "label": "Oh My Zsh 統合を有効化", - "description": "有効にすると、ITERM_SHELL_INTEGRATION_INSTALLED=Yes を設定して Oh My Zsh シェル統合機能を有効にします。この設定を適用するには、IDEの再起動が必要な場合があります。(実験的)" + "description": "有効にすると、ITERM_SHELL_INTEGRATION_INSTALLED=Yes を設定して Oh My Zsh シェル統合機能を有効にします。この設定を適用するには、IDEの再起動が必要な場合があります。 <0>詳細情報" }, "zshP10k": { "label": "Powerlevel10k 統合を有効化", - "description": "有効にすると、POWERLEVEL9K_TERM_SHELL_INTEGRATION=true を設定して Powerlevel10k シェル統合機能を有効にします。(実験的)" + "description": "有効にすると、POWERLEVEL9K_TERM_SHELL_INTEGRATION=true を設定して Powerlevel10k シェル統合機能を有効にします。 <0>詳細情報" + }, + "zdotdir": { + "label": "ZDOTDIR 処理を有効化", + "description": "有効にすると、zsh シェル統合を適切に処理するために ZDOTDIR 用の一時ディレクトリを作成します。これにより、zsh の設定を保持しながら VSCode のシェル統合が正しく機能します。 <0>詳細情報" + }, + "inheritEnv": { + "label": "環境変数を継承", + "description": "有効にすると、ターミナルは VSCode の親プロセスから環境変数を継承します。ユーザープロファイルで定義されたシェル統合設定などが含まれます。これは VSCode のグローバル設定 `terminal.integrated.inheritEnv` を直接切り替えます。 <0>詳細情報" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "コンテキストウィンドウをインテリジェントに圧縮する", + "description": "タスクのコンテキストウィンドウがほぼいっぱいになったとき、古いメッセージを削除する代わりに、LLM呼び出しを使用して過去の会話を要約します。免責事項:要約のコストは現在UIに表示されるAPIコストには含まれていません。" + }, "DIFF_STRATEGY_UNIFIED": { "name": "実験的な統合diff戦略を使用する", "description": "実験的な統合diff戦略を有効にします。この戦略はモデルエラーによる再試行の回数を減らす可能性がありますが、予期しない動作や不正確な編集を引き起こす可能性があります。リスクを理解し、すべての変更を注意深く確認する準備がある場合にのみ有効にしてください。" @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "プロンプトキャッシングを有効化", - "description": "有効にすると、Rooはコスト削減のためにプロンプトキャッシングを有効にしてこのモデルを使用します。" + "label": "プロンプトキャッシュを無効化", + "description": "チェックすると、Rooはこのモデルに対してプロンプトキャッシュを使用しません。" }, "temperature": { "useCustom": "カスタム温度を使用", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "質問やフィードバックがある場合は、github.com/RooVetGit/Roo-Codeで問題を開くか、reddit.com/r/RooCodediscord.gg/roocodeに参加してください", - "version": "Agent v{{version}}", "telemetry": { "label": "匿名のエラーと使用状況レポートを許可", "description": "匿名の使用データとエラーレポートを送信してRoo Codeの改善にご協力ください。コード、プロンプト、個人情報が送信されることはありません。詳細については、プライバシーポリシーをご覧ください。" diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index c4d52b41803..a04ee7f0712 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -1,5 +1,5 @@ { - "greeting": "Agent Code에 오신 것을 환영합니다", + "greeting": "Roo Code에 오신 것을 환영합니다", "task": { "title": "작업", "seeMore": "더 보기", @@ -202,12 +202,12 @@ "copyToInput": "입력창에 복사 (또는 Shift + 클릭)" }, "announcement": { - "title": "🎉 Roo Code 3.15 출시", - "description": "Agent Code 3.15는 사용자 피드백을 기반으로 새로운 기능과 개선사항을 제공합니다.", + "title": "🎉 Roo Code 3.17 출시", + "description": "Roo Code 3.17은 사용자 피드백을 기반으로 강력한 새로운 기능과 개선사항을 제공합니다.", "whatsNew": "새로운 기능", - "feature1": "Vertex용 프롬프트 캐싱: Vertex AI에 프롬프트 캐싱 지원이 추가되어 응답 시간 개선 및 API 비용 절감", - "feature2": "터미널 대체 메커니즘: VSCode 터미널 쉘 통합이 실패할 때 작동하는 대체 메커니즘 구현", - "feature3": "개선된 코드 스니펫: 채팅 인터페이스에서 코드 스니펫 렌더링 및 상호작용 향상", + "feature1": "Gemini용 암시적 캐싱: Gemini API 호출이 이제 자동으로 캐시되어 API 비용이 절감됩니다", + "feature2": "더 스마트한 모드 선택: 모드 정의에 각 모드를 언제 사용해야 하는지에 대한 지침을 포함할 수 있어 더 나은 오케스트레이션이 가능해졌습니다", + "feature3": "지능적인 컨텍스트 압축: 컨텍스트가 가득 찼을 때 잘라내는 대신 대화 기록을 지능적으로 요약합니다(설정 -> 실험적 기능에서 활성화)", "hideButton": "공지 숨기기", "detailsDiscussLinks": "DiscordReddit에서 더 자세한 정보를 확인하고 논의하세요 🚀" }, @@ -233,6 +233,15 @@ "close": "브라우저 닫기" } }, + "codeblock": { + "tooltips": { + "expand": "코드 블록 확장", + "collapse": "코드 블록 축소", + "enable_wrap": "자동 줄바꿈 활성화", + "disable_wrap": "자동 줄바꿈 비활성화", + "copy_code": "코드 복사" + } + }, "systemPromptWarning": "경고: 사용자 정의 시스템 프롬프트 재정의가 활성화되었습니다. 이로 인해 기능이 심각하게 손상되고 예측할 수 없는 동작이 발생할 수 있습니다.", "shellIntegration": { "title": "명령 실행 경고", diff --git a/webview-ui/src/i18n/locales/ko/mcp.json b/webview-ui/src/i18n/locales/ko/mcp.json index 5d45eb0fca2..66bed3c4690 100644 --- a/webview-ui/src/i18n/locales/ko/mcp.json +++ b/webview-ui/src/i18n/locales/ko/mcp.json @@ -1,29 +1,34 @@ { "title": "MCP 서버", "done": "완료", - "description": "<0>Model Context Protocol은 로컬에서 실행되는 MCP 서버와 통신하여 Roo의 기능을 확장하는 추가 도구 및 리소스를 제공합니다. <1>커뮤니티에서 만든 서버를 사용하거나 Roo에게 작업 흐름에 맞는 새로운 도구를 만들도록 요청할 수 있습니다 (예: \"최신 npm 문서를 가져오는 도구 추가\").", + "description": "Model Context Protocol(MCP)를 활성화하면 Roo Code가 외부 서버에서 추가 도구와 서비스를 사용할 수 있어. Roo가 할 수 있는 일이 더 많아져! <0>자세히 알아보기", "enableToggle": { "title": "MCP 서버 활성화", - "description": "활성화하면 Roo가 고급 기능을 위해 MCP 서버와 상호 작용할 수 있습니다. MCP를 사용하지 않는 경우 비활성화하여 Roo의 token 사용량을 줄일 수 있습니다." + "description": "이걸 켜면 Roo가 연결된 MCP 서버의 도구를 쓸 수 있어. Roo의 능력이 더 늘어나! 추가 도구를 쓸 생각이 없다면, API 토큰 비용을 줄이기 위해 꺼 두는 게 좋아." }, "enableServerCreation": { "title": "MCP 서버 생성 활성화", - "description": "활성화하면 Roo가 \"새 도구 추가...\"와 같은 명령을 통해 새 MCP 서버를 만드는 데 도움을 줄 수 있습니다. MCP 서버를 만들 필요가 없다면 이 기능을 비활성화하여 Roo의 token 사용량을 줄일 수 있습니다." + "description": "이걸 켜면 Roo가 <1>새로운 맞춤형 MCP 서버를 만드는 걸 도와줄 수 있어. <0>서버 생성에 대해 알아보기", + "hint": "팁: API 토큰 비용을 줄이고 싶으면, Roo에게 새 MCP 서버를 만들라고 하지 않을 때 이 설정을 꺼 둬." }, - "editGlobalMCP": "전역 MCP 편집", + "editGlobalMCP": "글로벌 MCP 편집", "editProjectMCP": "프로젝트 MCP 편집", + "learnMoreEditingSettings": "MCP 설정 파일 편집 방법 더 알아보기", "tool": { "alwaysAllow": "항상 허용", - "parameters": "매개변수", + "parameters": "파라미터", "noDescription": "설명 없음" }, "tabs": { "tools": "도구", - "resources": "리소스" + "resources": "리소스", + "errors": "오류" }, "emptyState": { "noTools": "도구를 찾을 수 없음", - "noResources": "리소스를 찾을 수 없음" + "noResources": "리소스를 찾을 수 없음", + "noLogs": "로그를 찾을 수 없음", + "noErrors": "오류를 찾을 수 없음" }, "networkTimeout": { "label": "네트워크 타임아웃", @@ -41,12 +46,12 @@ }, "deleteDialog": { "title": "MCP 서버 삭제", - "description": "MCP 서버 \"{{serverName}}\"을(를) 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.", + "description": "정말로 MCP 서버 \"{{serverName}}\"을(를) 삭제할까? 이 작업은 되돌릴 수 없어.", "cancel": "취소", "delete": "삭제" }, "serverStatus": { - "retrying": "재시도 중...", - "retryConnection": "연결 재시도" + "retrying": "다시 시도 중...", + "retryConnection": "연결 다시 시도" } } diff --git a/webview-ui/src/i18n/locales/ko/prompts.json b/webview-ui/src/i18n/locales/ko/prompts.json index 3c20d6f7cdb..809486a0fd1 100644 --- a/webview-ui/src/i18n/locales/ko/prompts.json +++ b/webview-ui/src/i18n/locales/ko/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "새 모드 만들기", "editModesConfig": "모드 구성 편집", "editGlobalModes": "전역 모드 편집", - "editProjectModes": "프로젝트 모드 편집 (.pearai-agent-ignore)", - "createModeHelpText": "새 커스텀 모드를 만들려면 + 버튼을 클릭하거나, 채팅에서 Roo에게 만들어달라고 요청하세요!" + "editProjectModes": "프로젝트 모드 편집 (.roomodes)", + "createModeHelpText": "모드는 Roo의 행동을 맞춤화하는 특화된 페르소나입니다. <0>모드 사용에 대해 알아보기 또는 <1>모드 사용자 정의하기.", + "selectMode": "모드 검색" }, "apiConfiguration": { "title": "API 구성", @@ -33,16 +34,21 @@ "resetToDefault": "기본값으로 재설정", "description": "이 모드에 대한 Roo의 전문성과 성격을 정의하세요. 이 설명은 Roo가 자신을 어떻게 표현하고 작업에 접근하는지 형성합니다." }, + "whenToUse": { + "title": "사용 시기 (선택 사항)", + "description": "이 모드를 언제 사용해야 하는지 설명합니다. 이는 Orchestrator가 작업에 적합한 모드를 선택하는 데 도움이 됩니다.", + "resetToDefault": "'사용 시기' 설명을 기본값으로 재설정" + }, "customInstructions": { "title": "모드별 사용자 지정 지침 (선택 사항)", "resetToDefault": "기본값으로 재설정", "description": "{{modeName}} 모드에 대한 특정 행동 지침을 추가하세요.", - "loadFromFile": "{{mode}} 모드에 대한 사용자 지정 지침은 작업 공간의 .pearai-agent/rules-{{slug}}/ 폴더에서도 로드할 수 있습니다(.roorules-{{slug}}와 .clinerules-{{slug}}는 더 이상 사용되지 않으며 곧 작동을 중단합니다)." + "loadFromFile": "{{mode}} 모드에 대한 사용자 지정 지침은 작업 공간의 .roo/rules-{{slug}}/ 폴더에서도 로드할 수 있습니다(.roorules-{{slug}}와 .clinerules-{{slug}}는 더 이상 사용되지 않으며 곧 작동을 중단합니다)." }, "globalCustomInstructions": { "title": "모든 모드에 대한 사용자 지정 지침", - "description": "이 지침은 모든 모드에 적용됩니다. 아래의 모드별 지침으로 향상될 수 있는 기본 동작 세트를 제공합니다.\nRoo가 에디터 표시 언어({{language}})와 다른 언어로 생각하고 말하기를 원하시면, 여기에 지정할 수 있습니다.", - "loadFromFile": "지침은 작업 공간의 .pearai-agent/rules/ 폴더에서도 로드할 수 있습니다(.roorules와 .clinerules는 더 이상 사용되지 않으며 곧 작동을 중단합니다)." + "description": "이 지침은 모든 모드에 적용됩니다. 아래의 모드별 지침으로 향상될 수 있는 기본 동작 세트를 제공합니다. <0>더 알아보기", + "loadFromFile": "지침은 작업 공간의 .roo/rules/ 폴더에서도 로드할 수 있습니다(.roorules와 .clinerules는 더 이상 사용되지 않으며 곧 작동을 중단합니다)." }, "systemPrompt": { "preview": "시스템 프롬프트 미리보기", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "고급: 시스템 프롬프트 재정의", - "description": "작업 공간의 .pearai-agent/system-prompt-{{slug}}에 파일을 생성하여 이 모드의 시스템 프롬프트(역할 정의 및 사용자 지정 지침 제외)를 완전히 대체할 수 있습니다. 이는 내장된 안전 장치와 일관성 검사(특히 도구 사용 관련)를 우회하는 매우 고급 기능이므로 주의하세요!" + "description": "<2>⚠️ 경고: 이 고급 기능은 안전 장치를 우회합니다. <1>사용하기 전에 이것을 읽으십시오!작업 공간의 .roo/system-prompt-{{slug}}에 파일을 생성하여 기본 시스템 프롬프트를 재정의합니다." }, "createModeDialog": { "title": "새 모드 만들기", @@ -122,7 +128,7 @@ "description": "모든 작업 공간에서 사용 가능" }, "project": { - "label": "프로젝트별 (.pearai-agent-ignore)", + "label": "프로젝트별 (.roomodes)", "description": "이 작업 공간에서만 사용 가능, 전역보다 우선" } }, @@ -130,6 +136,10 @@ "label": "역할 정의", "description": "이 모드에 대한 Roo의 전문성과 성격을 정의하세요." }, + "whenToUse": { + "label": "사용 시기 (선택 사항)", + "description": "이 모드가 가장 효과적인 시기와 어떤 유형의 작업에 적합한지에 대한 명확한 설명을 제공하세요." + }, "tools": { "label": "사용 가능한 도구", "description": "이 모드가 사용할 수 있는 도구를 선택하세요." diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 8f35891bb26..310addc6a20 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -23,16 +23,14 @@ "sections": { "providers": "공급자", "autoApprove": "자동 승인", - "browser": "브라우저 / 컴퓨터 사용", + "browser": "컴퓨터 접근", "checkpoints": "체크포인트", "notifications": "알림", - "contextManagement": "컨텍스트 관리", + "contextManagement": "컨텍스트", "terminal": "터미널", - "advanced": "고급", - "experimental": "실험적 기능", + "experimental": "실험적", "language": "언어", - "about": "Agent 정보", - "interface": "인터페이스" + "about": "Roo Code 정보" }, "autoApprove": { "description": "Roo가 승인 없이 자동으로 작업을 수행할 수 있도록 허용합니다. AI를 완전히 신뢰하고 관련 보안 위험을 이해하는 경우에만 이러한 설정을 활성화하세요.", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "구성 프로필", "providerDocumentation": "{{provider}} 문서", + "configProfile": "구성 프로필", "description": "다양한 API 구성을 저장하여 제공자와 설정 간에 빠르게 전환할 수 있습니다.", "apiProvider": "API 제공자", "model": "모델", @@ -118,14 +116,22 @@ "headerValue": "헤더 값", "noCustomHeaders": "정의된 사용자 정의 헤더가 없습니다. + 버튼을 클릭하여 추가하세요.", "requestyApiKey": "Requesty API 키", + "refreshModels": { + "label": "모델 새로고침", + "hint": "최신 모델을 보려면 설정을 다시 열어주세요." + }, "getRequestyApiKey": "Requesty API 키 받기", "openRouterTransformsText": "프롬프트와 메시지 체인을 컨텍스트 크기로 압축 (OpenRouter Transforms)", "anthropicApiKey": "Anthropic API 키", "getAnthropicApiKey": "Anthropic API 키 받기", "anthropicUseAuthToken": "X-Api-Key 대신 Authorization 헤더로 Anthropic API 키 전달", + "chutesApiKey": "Chutes API 키", + "getChutesApiKey": "Chutes API 키 받기", "deepSeekApiKey": "DeepSeek API 키", "getDeepSeekApiKey": "DeepSeek API 키 받기", "geminiApiKey": "Gemini API 키", + "getGroqApiKey": "Groq API 키 받기", + "groqApiKey": "Groq API 키", "getGeminiApiKey": "Gemini API 키 받기", "openAiApiKey": "OpenAI API 키", "openAiBaseUrl": "기본 URL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Codestral 모델의 대체 URL을 설정합니다.", "xaiApiKey": "xAI API 키", "getXaiApiKey": "xAI API 키 받기", + "litellmApiKey": "LiteLLM API 키", + "litellmBaseUrl": "LiteLLM 기본 URL", "awsCredentials": "AWS 자격 증명", "awsProfile": "AWS 프로필", "awsProfileName": "AWS 프로필 이름", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "브라우저 도구 활성화", - "description": "활성화되면 Roo는 컴퓨터 사용을 지원하는 모델을 사용할 때 웹사이트와 상호 작용하기 위해 브라우저를 사용할 수 있습니다." + "description": "활성화되면 Roo는 컴퓨터 사용을 지원하는 모델을 사용할 때 웹사이트와 상호 작용하기 위해 브라우저를 사용할 수 있습니다. <0>더 알아보기" }, "viewport": { "label": "뷰포트 크기", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "자동 체크포인트 활성화", - "description": "활성화되면 Roo는 작업 실행 중에 자동으로 체크포인트를 생성하여 변경 사항을 검토하거나 이전 상태로 되돌리기 쉽게 합니다." + "description": "활성화되면 Roo는 작업 실행 중에 자동으로 체크포인트를 생성하여 변경 사항을 검토하거나 이전 상태로 되돌리기 쉽게 합니다. <0>더 알아보기" } }, "notifications": { @@ -298,8 +306,8 @@ "label": "작업 공간 파일 컨텍스트 제한", "description": "현재 작업 디렉토리 세부 정보에 포함할 파일의 최대 수. 높은 값은 더 많은 컨텍스트를 제공하지만 token 사용량이 증가합니다." }, - "pearai-agent-ignore": { - "label": "목록 및 검색에서 .pearai-agent-ignore 파일 표시", + "rooignore": { + "label": "목록 및 검색에서 .rooignore 파일 표시", "description": "활성화되면 .rooignore의 패턴과 일치하는 파일이 잠금 기호와 함께 목록에 표시됩니다. 비활성화되면 이러한 파일은 파일 목록 및 검색에서 완전히 숨겨집니다." }, "maxReadFile": { @@ -310,45 +318,57 @@ } }, "terminal": { + "basic": { + "label": "터미널 설정: 기본", + "description": "기본 터미널 설정" + }, + "advanced": { + "label": "터미널 설정: 고급", + "description": "다음 옵션들은 설정을 적용하기 위해 터미널 재시작이 필요할 수 있습니다" + }, "outputLineLimit": { "label": "터미널 출력 제한", - "description": "명령 실행 시 터미널 출력에 포함할 최대 라인 수. 초과 시 중간에서 라인이 제거되어 token이 절약됩니다." + "description": "명령 실행 시 터미널 출력에 포함할 최대 라인 수. 초과 시 중간에서 라인이 제거되어 token이 절약됩니다. <0>더 알아보기" }, "shellIntegrationTimeout": { "label": "터미널 쉘 통합 타임아웃", - "description": "명령을 실행하기 전에 쉘 통합이 초기화될 때까지 기다리는 최대 시간. 쉘 시작 시간이 긴 사용자의 경우, 터미널에서 \"Shell Integration Unavailable\" 오류가 표시되면 이 값을 늘려야 할 수 있습니다." + "description": "명령을 실행하기 전에 쉘 통합이 초기화될 때까지 기다리는 최대 시간. 쉘 시작 시간이 긴 사용자의 경우, 터미널에서 \"Shell Integration Unavailable\" 오류가 표시되면 이 값을 늘려야 할 수 있습니다. <0>더 알아보기" }, "shellIntegrationDisabled": { "label": "터미널 셸 통합 비활성화", - "description": "터미널 명령이 올바르게 작동하지 않거나 '셸 통합을 사용할 수 없음' 오류가 표시되는 경우 이 옵션을 활성화합니다. 이렇게 하면 일부 고급 터미널 기능을 우회하여 명령을 실행하는 더 간단한 방법을 사용합니다." - }, - "compressProgressBar": { - "label": "진행 표시줄 출력 압축", - "description": "활성화하면 캐리지 리턴(\\r)이 포함된 터미널 출력을 처리하여 실제 터미널이 콘텐츠를 표시하는 방식을 시뮬레이션합니다. 이는 진행 표시줄의 중간 상태를 제거하고 최종 상태만 유지하여 더 관련성 있는 정보를 위한 컨텍스트 공간을 절약합니다." - }, - "zdotdir": { - "label": "ZDOTDIR 처리 활성화", - "description": "활성화하면 zsh 셸 통합을 올바르게 처리하기 위한 ZDOTDIR용 임시 디렉터리를 생성합니다. 이를 통해 zsh 구성을 유지하면서 VSCode 셸 통합이 zsh와 올바르게 작동합니다. (실험적)" + "description": "터미널 명령이 올바르게 작동하지 않거나 '셸 통합을 사용할 수 없음' 오류가 표시되는 경우 이 옵션을 활성화합니다. 이렇게 하면 일부 고급 터미널 기능을 우회하여 명령을 실행하는 더 간단한 방법을 사용합니다. <0>더 알아보기" }, "commandDelay": { "label": "터미널 명령 지연", - "description": "명령 실행 후 추가할 지연 시간(밀리초). 기본값 0은 지연을 완전히 비활성화합니다. 이는 타이밍 문제가 있는 터미널에서 명령 출력을 완전히 캡처하는 데 도움이 될 수 있습니다. 대부분의 터미널에서는 `PROMPT_COMMAND='sleep N'`을 설정하여 구현되며, PowerShell은 각 명령 끝에 `start-sleep`을 추가합니다. 원래는 VSCode 버그#237208에 대한 해결책이었으며 필요하지 않을 수 있습니다." + "description": "명령 실행 후 추가할 지연 시간(밀리초). 기본값 0은 지연을 완전히 비활성화합니다. 이는 타이밍 문제가 있는 터미널에서 명령 출력을 완전히 캡처하는 데 도움이 될 수 있습니다. 대부분의 터미널에서는 `PROMPT_COMMAND='sleep N'`을 설정하여 구현되며, PowerShell은 각 명령 끝에 `start-sleep`을 추가합니다. 원래는 VSCode 버그#237208에 대한 해결책이었으며 필요하지 않을 수 있습니다. <0>더 알아보기" + }, + "compressProgressBar": { + "label": "진행 표시줄 출력 압축", + "description": "활성화하면 캐리지 리턴(\\r)이 포함된 터미널 출력을 처리하여 실제 터미널이 콘텐츠를 표시하는 방식을 시뮬레이션합니다. 이는 진행 표시줄의 중간 상태를 제거하고 최종 상태만 유지하여 더 관련성 있는 정보를 위한 컨텍스트 공간을 절약합니다. <0>더 알아보기" }, "powershellCounter": { "label": "PowerShell 카운터 해결 방법 활성화", - "description": "활성화하면 PowerShell 명령에 카운터를 추가하여 명령이 올바르게 실행되도록 합니다. 이는 명령 출력 캡처에 문제가 있을 수 있는 PowerShell 터미널에서 도움이 됩니다." + "description": "활성화하면 PowerShell 명령에 카운터를 추가하여 명령이 올바르게 실행되도록 합니다. 이는 명령 출력 캡처에 문제가 있을 수 있는 PowerShell 터미널에서 도움이 됩니다. <0>더 알아보기" }, "zshClearEolMark": { "label": "ZSH 줄 끝 표시 지우기", - "description": "활성화하면 PROMPT_EOL_MARK=''를 설정하여 ZSH 줄 끝 표시를 지웁니다. 이는 '%'와 같은 특수 문자로 끝나는 명령 출력 해석의 문제를 방지합니다." + "description": "활성화하면 PROMPT_EOL_MARK=''를 설정하여 ZSH 줄 끝 표시를 지웁니다. 이는 '%'와 같은 특수 문자로 끝나는 명령 출력 해석의 문제를 방지합니다. <0>더 알아보기" }, "zshOhMy": { "label": "Oh My Zsh 통합 활성화", - "description": "활성화하면 ITERM_SHELL_INTEGRATION_INSTALLED=Yes를 설정하여 Oh My Zsh 셸 통합 기능을 활성화합니다. 이 설정을 적용하려면 IDE를 다시 시작해야 할 수 있습니다. (실험적)" + "description": "활성화하면 ITERM_SHELL_INTEGRATION_INSTALLED=Yes를 설정하여 Oh My Zsh 셸 통합 기능을 활성화합니다. 이 설정을 적용하려면 IDE를 다시 시작해야 할 수 있습니다. <0>더 알아보기" }, "zshP10k": { "label": "Powerlevel10k 통합 활성화", - "description": "활성화하면 POWERLEVEL9K_TERM_SHELL_INTEGRATION=true를 설정하여 Powerlevel10k 셸 통합 기능을 활성화합니다. (실험적)" + "description": "활성화하면 POWERLEVEL9K_TERM_SHELL_INTEGRATION=true를 설정하여 Powerlevel10k 셸 통합 기능을 활성화합니다. <0>더 알아보기" + }, + "zdotdir": { + "label": "ZDOTDIR 처리 활성화", + "description": "활성화하면 zsh 셸 통합을 올바르게 처리하기 위한 ZDOTDIR용 임시 디렉터리를 생성합니다. 이를 통해 zsh 구성을 유지하면서 VSCode 셸 통합이 zsh와 올바르게 작동합니다. <0>더 알아보기" + }, + "inheritEnv": { + "label": "환경 변수 상속", + "description": "활성화하면 터미널이 VSCode 부모 프로세스로부터 환경 변수를 상속받습니다. 사용자 프로필에 정의된 셸 통합 설정 등이 포함됩니다. 이는 VSCode 전역 설정 `terminal.integrated.inheritEnv`를 직접 전환합니다. <0>더 알아보기" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "컨텍스트 창을 지능적으로 압축", + "description": "작업의 컨텍스트 창이 거의 가득 찼을 때 이전 메시지를 삭제하는 대신 LLM 호출을 사용하여 이전 대화를 요약합니다. 참고: 요약 비용은 현재 UI에 표시된 API 비용에 포함되지 않습니다." + }, "DIFF_STRATEGY_UNIFIED": { "name": "실험적 통합 diff 전략 사용", "description": "실험적 통합 diff 전략을 활성화합니다. 이 전략은 모델 오류로 인한 재시도 횟수를 줄일 수 있지만 예기치 않은 동작이나 잘못된 편집을 일으킬 수 있습니다. 위험을 이해하고 모든 변경 사항을 신중하게 검토할 의향이 있는 경우에만 활성화하십시오." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "프롬프트 캐싱 활성화", - "description": "활성화하면 Roo는 비용 절감을 위해 프롬프트 캐싱을 켠 상태로 이 모델을 사용합니다." + "label": "프롬프트 캐싱 비활성화", + "description": "체크하면 Roo가 이 모델에 대해 프롬프트 캐싱을 사용하지 않습니다." }, "temperature": { "useCustom": "사용자 정의 온도 사용", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "질문이나 피드백이 있으시면 github.com/RooVetGit/Roo-Code에서 이슈를 열거나 reddit.com/r/RooCode 또는 discord.gg/roocode에 가입하세요", - "version": "Agent v{{version}}", "telemetry": { "label": "익명 오류 및 사용 보고 허용", "description": "익명 사용 데이터 및 오류 보고서를 보내 Roo Code 개선에 도움을 주세요. 코드, 프롬프트 또는 개인 정보는 절대 전송되지 않습니다. 자세한 내용은 개인정보 보호정책을 참조하세요." diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json new file mode 100644 index 00000000000..f13a153042d --- /dev/null +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -0,0 +1,251 @@ +{ + "greeting": "Welkom bij Roo Code", + "task": { + "title": "Taak", + "seeMore": "Meer weergeven", + "seeLess": "Minder weergeven", + "tokens": "Tokens:", + "cache": "Cache:", + "apiCost": "API-kosten:", + "contextWindow": "Contextlengte:", + "closeAndStart": "Taak sluiten en een nieuwe starten", + "export": "Taakgeschiedenis exporteren", + "delete": "Taak verwijderen (Shift + Klik om bevestiging over te slaan)" + }, + "unpin": "Losmaken", + "pin": "Vastmaken", + "retry": { + "title": "Opnieuw proberen", + "tooltip": "Probeer de bewerking opnieuw" + }, + "startNewTask": { + "title": "Nieuwe taak starten", + "tooltip": "Begin een nieuwe taak" + }, + "proceedAnyways": { + "title": "Toch doorgaan", + "tooltip": "Ga door terwijl het commando wordt uitgevoerd" + }, + "save": { + "title": "Opslaan", + "tooltip": "Bestandswijzigingen opslaan" + }, + "tokenProgress": { + "availableSpace": "Beschikbare ruimte: {{amount}} tokens", + "tokensUsed": "Gebruikte tokens: {{used}} van {{total}}", + "reservedForResponse": "Gereserveerd voor modelantwoord: {{amount}} tokens" + }, + "reject": { + "title": "Weigeren", + "tooltip": "Deze actie weigeren" + }, + "completeSubtaskAndReturn": "Subtaak voltooien en terugkeren", + "approve": { + "title": "Goedkeuren", + "tooltip": "Deze actie goedkeuren" + }, + "runCommand": { + "title": "Commando uitvoeren", + "tooltip": "Voer dit commando uit" + }, + "proceedWhileRunning": { + "title": "Doorgaan tijdens uitvoeren", + "tooltip": "Ga door ondanks waarschuwingen" + }, + "killCommand": { + "title": "Commando stoppen", + "tooltip": "Huidig commando stoppen" + }, + "resumeTask": { + "title": "Taak hervatten", + "tooltip": "Ga door met de huidige taak" + }, + "terminate": { + "title": "Beëindigen", + "tooltip": "Beëindig de huidige taak" + }, + "cancel": { + "title": "Annuleren", + "tooltip": "Annuleer de huidige bewerking" + }, + "scrollToBottom": "Scroll naar onderaan de chat", + "about": "Genereer, refactor en debug code met AI-assistentie.
Bekijk onze documentatie voor meer informatie.", + "onboarding": "Je takenlijst in deze werkruimte is leeg.", + "rooTips": { + "boomerangTasks": { + "title": "Boomerang-taken", + "description": "Splits taken op in kleinere, beheersbare delen" + }, + "stickyModels": { + "title": "Vastgezette modellen", + "description": "Elke modus onthoudt je laatst gebruikte model" + }, + "tools": { + "title": "Tools", + "description": "Laat de AI problemen oplossen door te browsen, commando's uit te voeren en meer" + }, + "customizableModes": { + "title": "Aanpasbare modi", + "description": "Gespecialiseerde persona's met hun eigen gedrag en toegewezen modellen" + } + }, + "selectMode": "Selecteer modus voor interactie", + "selectApiConfig": "Selecteer API-configuratie", + "enhancePrompt": "Prompt verbeteren met extra context", + "enhancePromptDescription": "De knop 'Prompt verbeteren' helpt je prompt te verbeteren door extra context, verduidelijking of herformulering te bieden. Probeer hier een prompt te typen en klik opnieuw op de knop om te zien hoe het werkt.", + "addImages": "Afbeeldingen toevoegen aan bericht", + "sendMessage": "Bericht verzenden", + "typeMessage": "Typ een bericht...", + "typeTask": "Typ hier je taak...", + "addContext": "@ om context toe te voegen, / om van modus te wisselen", + "dragFiles": "houd shift ingedrukt om bestanden te slepen", + "dragFilesImages": "houd shift ingedrukt om bestanden/afbeeldingen te slepen", + "errorReadingFile": "Fout bij het lezen van bestand:", + "noValidImages": "Er zijn geen geldige afbeeldingen verwerkt", + "separator": "Scheidingsteken", + "edit": "Bewerken...", + "forNextMode": "voor volgende modus", + "apiRequest": { + "title": "API-verzoek", + "failed": "API-verzoek mislukt", + "streaming": "API-verzoek...", + "cancelled": "API-verzoek geannuleerd", + "streamingFailed": "API-streaming mislukt" + }, + "checkpoint": { + "initial": "Initiële checkpoint", + "regular": "Checkpoint", + "initializingWarning": "Checkpoint wordt nog steeds geïnitialiseerd... Als dit te lang duurt, kun je checkpoints uitschakelen in de instellingen en je taak opnieuw starten.", + "menu": { + "viewDiff": "Bekijk verschil", + "restore": "Herstel checkpoint", + "restoreFiles": "Bestanden herstellen", + "restoreFilesDescription": "Herstelt de bestanden van je project naar een momentopname die op dit punt is gemaakt.", + "restoreFilesAndTask": "Bestanden & taak herstellen", + "confirm": "Bevestigen", + "cancel": "Annuleren", + "cannotUndo": "Deze actie kan niet ongedaan worden gemaakt.", + "restoreFilesAndTaskDescription": "Herstelt de bestanden van je project naar een momentopname die op dit punt is gemaakt en verwijdert alle berichten na dit punt." + }, + "current": "Huidig" + }, + "instructions": { + "wantsToFetch": "Roo wil gedetailleerde instructies ophalen om te helpen met de huidige taak" + }, + "fileOperations": { + "wantsToRead": "Roo wil dit bestand lezen:", + "wantsToReadOutsideWorkspace": "Roo wil dit bestand buiten de werkruimte lezen:", + "didRead": "Roo heeft dit bestand gelezen:", + "wantsToEdit": "Roo wil dit bestand bewerken:", + "wantsToEditOutsideWorkspace": "Roo wil dit bestand buiten de werkruimte bewerken:", + "wantsToCreate": "Roo wil een nieuw bestand aanmaken:", + "wantsToSearchReplace": "Roo wil zoeken en vervangen in dit bestand:", + "didSearchReplace": "Roo heeft zoeken en vervangen uitgevoerd op dit bestand:", + "wantsToInsert": "Roo wil inhoud invoegen in dit bestand:", + "wantsToInsertWithLineNumber": "Roo wil inhoud invoegen in dit bestand op regel {{lineNumber}}:", + "wantsToInsertAtEnd": "Roo wil inhoud toevoegen aan het einde van dit bestand:" + }, + "directoryOperations": { + "wantsToViewTopLevel": "Roo wil de bovenliggende bestanden in deze map bekijken:", + "didViewTopLevel": "Roo heeft de bovenliggende bestanden in deze map bekeken:", + "wantsToViewRecursive": "Roo wil alle bestanden in deze map recursief bekijken:", + "didViewRecursive": "Roo heeft alle bestanden in deze map recursief bekeken:", + "wantsToViewDefinitions": "Roo wil broncode-definitienamen bekijken die in deze map worden gebruikt:", + "didViewDefinitions": "Roo heeft broncode-definitienamen bekeken die in deze map worden gebruikt:", + "wantsToSearch": "Roo wil deze map doorzoeken op {{regex}}:", + "didSearch": "Roo heeft deze map doorzocht op {{regex}}:" + }, + "commandOutput": "Commando-uitvoer", + "response": "Antwoord", + "arguments": "Argumenten", + "mcp": { + "wantsToUseTool": "Roo wil een tool gebruiken op de {{serverName}} MCP-server:", + "wantsToAccessResource": "Roo wil een bron benaderen op de {{serverName}} MCP-server:" + }, + "modes": { + "wantsToSwitch": "Roo wil overschakelen naar {{mode}} modus", + "wantsToSwitchWithReason": "Roo wil overschakelen naar {{mode}} modus omdat: {{reason}}", + "didSwitch": "Roo is overgeschakeld naar {{mode}} modus", + "didSwitchWithReason": "Roo is overgeschakeld naar {{mode}} modus omdat: {{reason}}" + }, + "subtasks": { + "wantsToCreate": "Roo wil een nieuwe subtaak aanmaken in {{mode}} modus:", + "wantsToFinish": "Roo wil deze subtaak voltooien", + "newTaskContent": "Subtaak-instructies", + "completionContent": "Subtaak voltooid", + "resultContent": "Subtaakresultaten", + "defaultResult": "Ga verder met de volgende taak.", + "completionInstructions": "Subtaak voltooid! Je kunt de resultaten bekijken en eventuele correcties of volgende stappen voorstellen. Als alles goed is, bevestig dan om het resultaat terug te sturen naar de hoofdtaak." + }, + "questions": { + "hasQuestion": "Roo heeft een vraag:" + }, + "taskCompleted": "Taak voltooid", + "error": "Fout", + "diffError": { + "title": "Bewerking mislukt" + }, + "troubleMessage": "Roo ondervindt problemen...", + "powershell": { + "issues": "Het lijkt erop dat je problemen hebt met Windows PowerShell, zie deze" + }, + "autoApprove": { + "title": "Automatisch goedkeuren:", + "none": "Geen", + "description": "Met automatisch goedkeuren kan Roo Code acties uitvoeren zonder om toestemming te vragen. Schakel dit alleen in voor acties die je volledig vertrouwt. Meer gedetailleerde configuratie beschikbaar in de Instellingen." + }, + "announcement": { + "title": "🎉 Roo Code 3.17 uitgebracht", + "description": "Roo Code 3.17 brengt krachtige nieuwe functies en verbeteringen op basis van jouw feedback.", + "feature1": "Impliciete caching voor Gemini: Gemini API-aanroepen worden nu automatisch gecachet, waardoor API-kosten worden verlaagd", + "feature2": "Slimmere modusselectie: Modusdefinities kunnen nu richtlijnen bevatten over wanneer elke modus moet worden gebruikt, wat betere orchestratie mogelijk maakt", + "feature3": "Intelligente contextcompressie: Vat gespreksgeschiedenis intelligent samen wanneer de context vol raakt in plaats van deze af te kappen (inschakelen in Instellingen -> Experimenteel)", + "hideButton": "Aankondiging verbergen", + "detailsDiscussLinks": "Meer details en discussie in Discord en Reddit 🚀", + "whatsNew": "Wat is er nieuw" + }, + "reasoning": { + "thinking": "Denkt na", + "seconds": "{{count}}s" + }, + "followUpSuggest": { + "copyToInput": "Kopiëren naar invoer (zelfde als shift + klik)" + }, + "browser": { + "rooWantsToUse": "Roo wil de browser gebruiken:", + "consoleLogs": "Console-logboeken", + "noNewLogs": "(Geen nieuwe logboeken)", + "screenshot": "Browserschermopname", + "cursor": "cursor", + "navigation": { + "step": "Stap {{current}} van {{total}}", + "previous": "Vorige", + "next": "Volgende" + }, + "sessionStarted": "Browsersessie gestart", + "actions": { + "title": "Browse-actie: ", + "launch": "Browser starten op {{url}}", + "click": "Klik ({{coordinate}})", + "type": "Typ \"{{text}}\"", + "scrollDown": "Scroll naar beneden", + "scrollUp": "Scroll naar boven", + "close": "Browser sluiten" + } + }, + "codeblock": { + "tooltips": { + "expand": "Codeblok uitvouwen", + "collapse": "Codeblok samenvouwen", + "enable_wrap": "Regelafbreking inschakelen", + "disable_wrap": "Regelafbreking uitschakelen", + "copy_code": "Code kopiëren" + } + }, + "systemPromptWarning": "WAARSCHUWING: Aangepaste systeemprompt actief. Dit kan de functionaliteit ernstig verstoren en onvoorspelbaar gedrag veroorzaken.", + "shellIntegration": { + "title": "Waarschuwing commando-uitvoering", + "description": "Je commando wordt uitgevoerd zonder VSCode-terminal shell-integratie. Om deze waarschuwing te onderdrukken kun je shell-integratie uitschakelen in het gedeelte Terminal van de Roo Code-instellingen of de VSCode-terminalintegratie oplossen via de onderstaande link.", + "troubleshooting": "Klik hier voor shell-integratie documentatie." + } +} diff --git a/webview-ui/src/i18n/locales/nl/common.json b/webview-ui/src/i18n/locales/nl/common.json new file mode 100644 index 00000000000..38259b02ff1 --- /dev/null +++ b/webview-ui/src/i18n/locales/nl/common.json @@ -0,0 +1,14 @@ +{ + "number_format": { + "thousand_suffix": "k", + "million_suffix": "m", + "billion_suffix": "mrd" + }, + "ui": { + "search_placeholder": "Zoeken..." + }, + "mermaid": { + "loading": "Mermaid-diagram genereren...", + "render_error": "Kan diagram niet weergeven" + } +} diff --git a/webview-ui/src/i18n/locales/nl/history.json b/webview-ui/src/i18n/locales/nl/history.json new file mode 100644 index 00000000000..2addee0d163 --- /dev/null +++ b/webview-ui/src/i18n/locales/nl/history.json @@ -0,0 +1,39 @@ +{ + "recentTasks": "Taken", + "viewAll": "Alle taken weergeven", + "tokens": "Tokens: ↑{{in}} ↓{{out}}", + "cache": "Cache: +{{writes}} → {{reads}}", + "apiCost": "API-kosten: ${{cost}}", + "history": "Geschiedenis", + "exitSelectionMode": "Selectiemodus verlaten", + "enterSelectionMode": "Selectiemodus starten", + "done": "Gereed", + "searchPlaceholder": "Geschiedenis doorzoeken...", + "newest": "Nieuwste", + "oldest": "Oudste", + "mostExpensive": "Duurste", + "mostTokens": "Meeste tokens", + "mostRelevant": "Meest relevant", + "deleteTaskTitle": "Taak verwijderen (Shift + Klik om bevestiging over te slaan)", + "tokensLabel": "Tokens:", + "cacheLabel": "Cache:", + "apiCostLabel": "API-kosten:", + "copyPrompt": "Prompt kopiëren", + "exportTask": "Taak exporteren", + "deleteTask": "Taak verwijderen", + "deleteTaskMessage": "Weet je zeker dat je deze taak wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.", + "cancel": "Annuleren", + "delete": "Verwijderen", + "exitSelection": "Selectie verlaten", + "selectionMode": "Selectiemodus", + "deselectAll": "Alles deselecteren", + "selectAll": "Alles selecteren", + "selectedItems": "Geselecteerd {{selected}}/{{total}} items", + "clearSelection": "Selectie wissen", + "deleteSelected": "Geselecteerde verwijderen", + "deleteTasks": "Taken verwijderen", + "confirmDeleteTasks": "Weet je zeker dat je {{count}} taken wilt verwijderen?", + "deleteTasksWarning": "Verwijderde taken kunnen niet worden hersteld. Zorg ervoor dat je wilt doorgaan.", + "deleteItems": "Verwijder {{count}} items", + "showAllWorkspaces": "Toon taken van alle werkruimtes" +} diff --git a/webview-ui/src/i18n/locales/nl/humanRelay.json b/webview-ui/src/i18n/locales/nl/humanRelay.json new file mode 100644 index 00000000000..f01f9c479b0 --- /dev/null +++ b/webview-ui/src/i18n/locales/nl/humanRelay.json @@ -0,0 +1,13 @@ +{ + "dialogTitle": "Human Relay - Help alstublieft met kopiëren/plakken", + "dialogDescription": "Kopieer de volgende prompt naar de web-AI en plak het antwoord van de AI in het onderstaande invoerveld.", + "copiedToClipboard": "Gekopieerd naar klembord", + "aiResponse": { + "label": "Voer het antwoord van de AI in:", + "placeholder": "Plak hier het antwoord van de AI..." + }, + "actions": { + "cancel": "Annuleren", + "submit": "Verzenden" + } +} diff --git a/webview-ui/src/i18n/locales/nl/mcp.json b/webview-ui/src/i18n/locales/nl/mcp.json new file mode 100644 index 00000000000..f8d4f2628bc --- /dev/null +++ b/webview-ui/src/i18n/locales/nl/mcp.json @@ -0,0 +1,57 @@ +{ + "title": "MCP-servers", + "done": "Klaar", + "description": "Schakel het Model Context Protocol (MCP) in zodat Roo Code extra tools en diensten van externe servers kan gebruiken. Zo kan Roo meer voor je doen. <0>Meer informatie", + "enableToggle": { + "title": "MCP-servers inschakelen", + "description": "Zet dit AAN zodat Roo tools van verbonden MCP-servers kan gebruiken. Dit geeft Roo meer mogelijkheden. Gebruik je deze extra tools niet, zet het dan UIT om API-tokenkosten te besparen." + }, + "enableServerCreation": { + "title": "MCP-server aanmaken inschakelen", + "description": "Schakel dit in zodat Roo je kan helpen <1>nieuwe aangepaste MCP-servers te bouwen. <0>Meer over server aanmaken", + "hint": "Tip: Zet deze instelling uit als je Roo niet actief vraagt om een nieuwe MCP-server te maken, om API-tokenkosten te besparen." + }, + "editGlobalMCP": "Globale MCP bewerken", + "editProjectMCP": "Project-MCP bewerken", + "learnMoreEditingSettings": "Meer over het bewerken van MCP-instellingen", + "tool": { + "alwaysAllow": "Altijd toestaan", + "parameters": "Parameters", + "noDescription": "Geen beschrijving" + }, + "tabs": { + "tools": "Tools", + "resources": "Bronnen", + "errors": "Fouten" + }, + "emptyState": { + "noTools": "Geen tools gevonden", + "noResources": "Geen bronnen gevonden", + "noLogs": "Geen logs gevonden", + "noErrors": "Geen fouten gevonden" + }, + "networkTimeout": { + "label": "Netwerk-timeout", + "description": "Maximale wachttijd op serverreacties", + "options": { + "15seconds": "15 seconden", + "30seconds": "30 seconden", + "1minute": "1 minuut", + "5minutes": "5 minuten", + "10minutes": "10 minuten", + "15minutes": "15 minuten", + "30minutes": "30 minuten", + "60minutes": "60 minuten" + } + }, + "deleteDialog": { + "title": "MCP-server verwijderen", + "description": "Weet je zeker dat je de MCP-server \"{{serverName}}\" wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.", + "cancel": "Annuleren", + "delete": "Verwijderen" + }, + "serverStatus": { + "retrying": "Opnieuw proberen...", + "retryConnection": "Verbinding opnieuw proberen" + } +} diff --git a/webview-ui/src/i18n/locales/nl/prompts.json b/webview-ui/src/i18n/locales/nl/prompts.json new file mode 100644 index 00000000000..41946afd8de --- /dev/null +++ b/webview-ui/src/i18n/locales/nl/prompts.json @@ -0,0 +1,158 @@ +{ + "title": "Prompts", + "done": "Gereed", + "modes": { + "title": "Modi", + "createNewMode": "Nieuwe modus aanmaken", + "editModesConfig": "Modusconfiguratie bewerken", + "editGlobalModes": "Globale modi bewerken", + "editProjectModes": "Projectmodi bewerken (.roomodes)", + "createModeHelpText": "Modi zijn gespecialiseerde persona's die het gedrag van Roo aanpassen. <0>Meer informatie over het gebruik van modi of <1>het aanpassen van modi.", + "selectMode": "Modus zoeken" + }, + "apiConfiguration": { + "title": "API-configuratie", + "select": "Selecteer welke API-configuratie voor deze modus gebruikt moet worden" + }, + "tools": { + "title": "Beschikbare tools", + "builtInModesText": "Tools voor ingebouwde modi kunnen niet worden aangepast", + "editTools": "Tools bewerken", + "doneEditing": "Bewerken voltooid", + "allowedFiles": "Toegestane bestanden:", + "toolNames": { + "read": "Bestanden lezen", + "edit": "Bestanden bewerken", + "browser": "Browser gebruiken", + "command": "Commando's uitvoeren", + "mcp": "MCP gebruiken" + }, + "noTools": "Geen" + }, + "roleDefinition": { + "title": "Roldefinitie", + "resetToDefault": "Terugzetten naar standaard", + "description": "Definieer Roo's expertise en persoonlijkheid voor deze modus. Deze beschrijving bepaalt hoe Roo zich presenteert en taken benadert." + }, + "whenToUse": { + "title": "Wanneer te gebruiken (optioneel)", + "description": "Beschrijf wanneer deze modus gebruikt moet worden. Dit helpt de Orchestrator om de juiste modus voor een taak te kiezen.", + "resetToDefault": "Beschrijving 'Wanneer te gebruiken' terugzetten naar standaard" + }, + "customInstructions": { + "title": "Modusspecifieke instructies (optioneel)", + "resetToDefault": "Terugzetten naar standaard", + "description": "Voeg gedragsrichtlijnen toe die specifiek zijn voor de modus {{modeName}}.", + "loadFromFile": "Modusspecifieke instructies voor {{mode}} kunnen ook worden geladen uit de map .roo/rules-{{slug}}/ in je werkruimte (.roorules-{{slug}} en .clinerules-{{slug}} zijn verouderd en werken binnenkort niet meer)." + }, + "globalCustomInstructions": { + "title": "Aangepaste instructies voor alle modi", + "description": "Deze instructies gelden voor alle modi. Ze bieden een basisset aan gedragingen die kunnen worden uitgebreid met modusspecifieke instructies hieronder. <0>Meer informatie", + "loadFromFile": "Instructies kunnen ook worden geladen uit de map .roo/rules/ in je werkruimte (.roorules en .clinerules zijn verouderd en werken binnenkort niet meer)." + }, + "systemPrompt": { + "preview": "Systeemprompt bekijken", + "copy": "Systeemprompt kopiëren naar klembord", + "title": "Systeemprompt ({{modeName}} modus)" + }, + "supportPrompts": { + "title": "Ondersteuningsprompts", + "resetPrompt": "Reset {{promptType}} prompt naar standaard", + "prompt": "Prompt", + "enhance": { + "apiConfiguration": "API-configuratie", + "apiConfigDescription": "Je kunt een API-configuratie selecteren die altijd wordt gebruikt voor het verbeteren van prompts, of gewoon de huidige selectie gebruiken", + "useCurrentConfig": "Huidige API-configuratie gebruiken", + "testPromptPlaceholder": "Voer een prompt in om de verbetering te testen", + "previewButton": "Voorbeeld promptverbetering" + }, + "types": { + "ENHANCE": { + "label": "Prompt verbeteren", + "description": "Gebruik promptverbetering om op maat gemaakte suggesties of verbeteringen voor je invoer te krijgen. Zo begrijpt Roo je intentie en krijg je de best mogelijke antwoorden. Beschikbaar via het ✨-icoon in de chat." + }, + "EXPLAIN": { + "label": "Code uitleggen", + "description": "Krijg gedetailleerde uitleg over codefragmenten, functies of hele bestanden. Handig om complexe code te begrijpen of nieuwe patronen te leren. Beschikbaar via codeacties (lampje in de editor) en het contextmenu (rechtsklik op geselecteerde code)." + }, + "FIX": { + "label": "Problemen oplossen", + "description": "Krijg hulp bij het identificeren en oplossen van bugs, fouten of codekwaliteitsproblemen. Biedt stapsgewijze begeleiding bij het oplossen van problemen. Beschikbaar via codeacties (lampje in de editor) en het contextmenu (rechtsklik op geselecteerde code)." + }, + "IMPROVE": { + "label": "Code verbeteren", + "description": "Ontvang suggesties voor codeoptimalisatie, betere praktijken en architecturale verbeteringen met behoud van functionaliteit. Beschikbaar via codeacties (lampje in de editor) en het contextmenu (rechtsklik op geselecteerde code)." + }, + "ADD_TO_CONTEXT": { + "label": "Aan context toevoegen", + "description": "Voeg context toe aan je huidige taak of gesprek. Handig voor extra informatie of verduidelijkingen. Beschikbaar via codeacties (lampje in de editor) en het contextmenu (rechtsklik op geselecteerde code)." + }, + "TERMINAL_ADD_TO_CONTEXT": { + "label": "Terminalinhoud aan context toevoegen", + "description": "Voeg terminaluitvoer toe aan je huidige taak of gesprek. Handig voor commando-uitvoer of logboeken. Beschikbaar in het terminalcontextmenu (rechtsklik op geselecteerde terminalinhoud)." + }, + "TERMINAL_FIX": { + "label": "Terminalcommando repareren", + "description": "Krijg hulp bij het repareren van terminalcommando's die zijn mislukt of verbetering nodig hebben. Beschikbaar in het terminalcontextmenu (rechtsklik op geselecteerde terminalinhoud)." + }, + "TERMINAL_EXPLAIN": { + "label": "Terminalcommando uitleggen", + "description": "Krijg gedetailleerde uitleg over terminalcommando's en hun uitvoer. Beschikbaar in het terminalcontextmenu (rechtsklik op geselecteerde terminalinhoud)." + }, + "NEW_TASK": { + "label": "Nieuwe taak starten", + "description": "Start een nieuwe taak met gebruikersinvoer. Beschikbaar via de Command Palette." + } + } + }, + "advancedSystemPrompt": { + "title": "Geavanceerd: Systeemprompt overschrijven", + "description": "<2>⚠️ Waarschuwing: Deze geavanceerde functie omzeilt beveiligingen. <1>LEES DIT VOOR GEBRUIK!Overschrijf de standaard systeemprompt door een bestand aan te maken op .roo/system-prompt-{{slug}}." + }, + "createModeDialog": { + "title": "Nieuwe modus aanmaken", + "close": "Sluiten", + "name": { + "label": "Naam", + "placeholder": "Voer de naam van de modus in" + }, + "slug": { + "label": "Slug", + "description": "De slug wordt gebruikt in URL's en bestandsnamen. Moet kleine letters, cijfers en koppeltekens bevatten." + }, + "saveLocation": { + "label": "Opslaglocatie", + "description": "Kies waar je deze modus wilt opslaan. Projectspecifieke modi hebben voorrang op globale modi.", + "global": { + "label": "Globaal", + "description": "Beschikbaar in alle werkruimtes" + }, + "project": { + "label": "Projectspecifiek (.roomodes)", + "description": "Alleen beschikbaar in deze werkruimte, heeft voorrang op globaal" + } + }, + "roleDefinition": { + "label": "Roldefinitie", + "description": "Definieer Roo's expertise en persoonlijkheid voor deze modus." + }, + "whenToUse": { + "label": "Wanneer te gebruiken (optioneel)", + "description": "Geef een duidelijke beschrijving van wanneer deze modus het meest effectief is en voor welke soorten taken deze uitblinkt." + }, + "tools": { + "label": "Beschikbare tools", + "description": "Selecteer welke tools deze modus kan gebruiken." + }, + "customInstructions": { + "label": "Aangepaste instructies (optioneel)", + "description": "Voeg gedragsrichtlijnen toe die specifiek zijn voor deze modus." + }, + "buttons": { + "cancel": "Annuleren", + "create": "Modus aanmaken" + }, + "deleteMode": "Modus verwijderen" + }, + "allFiles": "alle bestanden" +} diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json new file mode 100644 index 00000000000..cb8442fdba1 --- /dev/null +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -0,0 +1,525 @@ +{ + "common": { + "save": "Opslaan", + "done": "Gereed", + "cancel": "Annuleren", + "reset": "Resetten", + "select": "Selecteren", + "add": "Header toevoegen", + "remove": "Verwijderen" + }, + "header": { + "title": "Instellingen", + "saveButtonTooltip": "Wijzigingen opslaan", + "nothingChangedTooltip": "Niets gewijzigd", + "doneButtonTooltip": "Niet-opgeslagen wijzigingen negeren en instellingen sluiten" + }, + "unsavedChangesDialog": { + "title": "Niet-opgeslagen wijzigingen", + "description": "Wil je de wijzigingen negeren en doorgaan?", + "cancelButton": "Annuleren", + "discardButton": "Wijzigingen negeren" + }, + "sections": { + "providers": "Providers", + "autoApprove": "Auto-goedkeuren", + "browser": "Browser", + "checkpoints": "Checkpoints", + "notifications": "Meldingen", + "contextManagement": "Context", + "terminal": "Terminal", + "experimental": "Experimenteel", + "language": "Taal", + "about": "Over Roo Code" + }, + "autoApprove": { + "description": "Sta Roo toe om automatisch handelingen uit te voeren zonder goedkeuring. Schakel deze instellingen alleen in als je de AI volledig vertrouwt en de bijbehorende beveiligingsrisico's begrijpt.", + "readOnly": { + "label": "Lezen", + "description": "Indien ingeschakeld, bekijkt Roo automatisch de inhoud van mappen en leest bestanden zonder dat je op de Goedkeuren-knop hoeft te klikken.", + "outsideWorkspace": { + "label": "Inclusief bestanden buiten werkruimte", + "description": "Sta Roo toe om bestanden buiten de huidige werkruimte te lezen zonder goedkeuring." + } + }, + "write": { + "label": "Schrijven", + "description": "Automatisch bestanden aanmaken en bewerken zonder goedkeuring", + "delayLabel": "Vertraging na schrijven om diagnostiek de kans te geven mogelijke problemen te detecteren", + "outsideWorkspace": { + "label": "Inclusief bestanden buiten werkruimte", + "description": "Sta Roo toe om bestanden buiten de huidige werkruimte aan te maken en te bewerken zonder goedkeuring." + } + }, + "browser": { + "label": "Browser", + "description": "Automatisch browseracties uitvoeren zonder goedkeuring. Let op: geldt alleen als het model computergebruik ondersteunt." + }, + "retry": { + "label": "Opnieuw proberen", + "description": "Automatisch mislukte API-verzoeken opnieuw proberen wanneer de server een foutmelding geeft", + "delayLabel": "Vertraging voordat het verzoek opnieuw wordt geprobeerd" + }, + "mcp": { + "label": "MCP", + "description": "Automatische goedkeuring van individuele MCP-tools in het MCP-serversoverzicht inschakelen (vereist zowel deze instelling als het selectievakje 'Altijd toestaan' bij de tool)" + }, + "modeSwitch": { + "label": "Modus", + "description": "Automatisch tussen verschillende modi schakelen zonder goedkeuring" + }, + "subtasks": { + "label": "Subtaken", + "description": "Subtaken aanmaken en afronden zonder goedkeuring" + }, + "execute": { + "label": "Uitvoeren", + "description": "Automatisch toegestane terminalcommando's uitvoeren zonder goedkeuring", + "allowedCommands": "Toegestane automatisch uit te voeren commando's", + "allowedCommandsDescription": "Commando-prefixen die automatisch kunnen worden uitgevoerd als 'Altijd goedkeuren voor uitvoeren' is ingeschakeld. Voeg * toe om alle commando's toe te staan (gebruik met voorzichtigheid).", + "commandPlaceholder": "Voer commando-prefix in (bijv. 'git ')", + "addButton": "Toevoegen" + } + }, + "providers": { + "providerDocumentation": "{{provider}} documentatie", + "configProfile": "Configuratieprofiel", + "description": "Sla verschillende API-configuraties op om snel te wisselen tussen providers en instellingen.", + "apiProvider": "API-provider", + "model": "Model", + "nameEmpty": "Naam mag niet leeg zijn", + "nameExists": "Er bestaat al een profiel met deze naam", + "deleteProfile": "Profiel verwijderen", + "invalidArnFormat": "Ongeldig ARN-formaat. Controleer de bovenstaande voorbeelden.", + "enterNewName": "Voer een nieuwe naam in", + "addProfile": "Profiel toevoegen", + "renameProfile": "Profiel hernoemen", + "newProfile": "Nieuw configuratieprofiel", + "enterProfileName": "Voer profielnaam in", + "createProfile": "Profiel aanmaken", + "cannotDeleteOnlyProfile": "Kan het enige profiel niet verwijderen", + "searchPlaceholder": "Zoek profielen", + "noMatchFound": "Geen overeenkomende profielen gevonden", + "vscodeLmDescription": "De VS Code Language Model API stelt je in staat modellen te draaien die door andere VS Code-extensies worden geleverd (waaronder GitHub Copilot). De eenvoudigste manier om te beginnen is door de Copilot- en Copilot Chat-extensies te installeren vanuit de VS Code Marketplace.", + "awsCustomArnUse": "Voer een geldige Amazon Bedrock ARN in voor het model dat je wilt gebruiken. Voorbeeldformaten:", + "awsCustomArnDesc": "Zorg ervoor dat de regio in de ARN overeenkomt met je geselecteerde AWS-regio hierboven.", + "openRouterApiKey": "OpenRouter API-sleutel", + "getOpenRouterApiKey": "OpenRouter API-sleutel ophalen", + "apiKeyStorageNotice": "API-sleutels worden veilig opgeslagen in de geheime opslag van VSCode", + "glamaApiKey": "Glama API-sleutel", + "getGlamaApiKey": "Glama API-sleutel ophalen", + "useCustomBaseUrl": "Aangepaste basis-URL gebruiken", + "useHostHeader": "Aangepaste Host-header gebruiken", + "useLegacyFormat": "Verouderd OpenAI API-formaat gebruiken", + "customHeaders": "Aangepaste headers", + "headerName": "Headernaam", + "headerValue": "Headerwaarde", + "noCustomHeaders": "Geen aangepaste headers gedefinieerd. Klik op de + knop om er een toe te voegen.", + "requestyApiKey": "Requesty API-sleutel", + "refreshModels": { + "label": "Modellen verversen", + "hint": "Open de instellingen opnieuw om de nieuwste modellen te zien." + }, + "getRequestyApiKey": "Requesty API-sleutel ophalen", + "openRouterTransformsText": "Comprimeer prompts en berichtreeksen tot de contextgrootte (OpenRouter Transforms)", + "anthropicApiKey": "Anthropic API-sleutel", + "getAnthropicApiKey": "Anthropic API-sleutel ophalen", + "anthropicUseAuthToken": "Anthropic API-sleutel als Authorization-header doorgeven in plaats van X-Api-Key", + "chutesApiKey": "Chutes API-sleutel", + "getChutesApiKey": "Chutes API-sleutel ophalen", + "deepSeekApiKey": "DeepSeek API-sleutel", + "getDeepSeekApiKey": "DeepSeek API-sleutel ophalen", + "geminiApiKey": "Gemini API-sleutel", + "getGroqApiKey": "Groq API-sleutel ophalen", + "groqApiKey": "Groq API-sleutel", + "getGeminiApiKey": "Gemini API-sleutel ophalen", + "openAiApiKey": "OpenAI API-sleutel", + "openAiBaseUrl": "Basis-URL", + "getOpenAiApiKey": "OpenAI API-sleutel ophalen", + "mistralApiKey": "Mistral API-sleutel", + "getMistralApiKey": "Mistral / Codestral API-sleutel ophalen", + "codestralBaseUrl": "Codestral basis-URL (optioneel)", + "codestralBaseUrlDesc": "Stel een alternatieve URL in voor het Codestral-model.", + "xaiApiKey": "xAI API-sleutel", + "getXaiApiKey": "xAI API-sleutel ophalen", + "litellmApiKey": "LiteLLM API-sleutel", + "litellmBaseUrl": "LiteLLM basis-URL", + "awsCredentials": "AWS-inloggegevens", + "awsProfile": "AWS-profiel", + "awsProfileName": "AWS-profielnaam", + "awsAccessKey": "AWS-toegangssleutel", + "awsSecretKey": "AWS-geheime sleutel", + "awsSessionToken": "AWS-sessietoken", + "awsRegion": "AWS-regio", + "awsCrossRegion": "Gebruik cross-region inference", + "enablePromptCaching": "Prompt caching inschakelen", + "enablePromptCachingTitle": "Schakel prompt caching in om de prestaties te verbeteren en de kosten te verlagen voor ondersteunde modellen.", + "cacheUsageNote": "Let op: als je geen cachegebruik ziet, probeer dan een ander model te selecteren en vervolgens weer je gewenste model.", + "vscodeLmModel": "Taalmodel", + "vscodeLmWarning": "Let op: dit is een zeer experimentele integratie en ondersteuning door providers kan variëren. Krijg je een foutmelding dat een model niet wordt ondersteund, dan ligt dat aan de provider.", + "googleCloudSetup": { + "title": "Om Google Cloud Vertex AI te gebruiken, moet je:", + "step1": "1. Maak een Google Cloud-account aan, schakel de Vertex AI API in en activeer de gewenste Claude-modellen.", + "step2": "2. Installeer de Google Cloud CLI en configureer standaardreferenties voor applicaties.", + "step3": "3. Of maak een serviceaccount met referenties." + }, + "googleCloudCredentials": "Google Cloud-referenties", + "googleCloudKeyFile": "Google Cloud-sleutelbestandspad", + "googleCloudProjectId": "Google Cloud-project-ID", + "googleCloudRegion": "Google Cloud-regio", + "lmStudio": { + "baseUrl": "Basis-URL (optioneel)", + "modelId": "Model-ID", + "speculativeDecoding": "Speculatieve decodering inschakelen", + "draftModelId": "Draft Model-ID", + "draftModelDesc": "Draft-model moet uit dezelfde modelfamilie komen voor correcte speculatieve decodering.", + "selectDraftModel": "Selecteer draft-model", + "noModelsFound": "Geen draft-modellen gevonden. Zorg dat LM Studio draait met Server Mode ingeschakeld.", + "description": "LM Studio laat je modellen lokaal op je computer draaien. Zie hun quickstart-gids voor instructies. Je moet ook de lokale server-functie van LM Studio starten om het met deze extensie te gebruiken. Let op: Roo Code gebruikt complexe prompts en werkt het beste met Claude-modellen. Minder krachtige modellen werken mogelijk niet zoals verwacht." + }, + "ollama": { + "baseUrl": "Basis-URL (optioneel)", + "modelId": "Model-ID", + "description": "Ollama laat je modellen lokaal op je computer draaien. Zie hun quickstart-gids voor instructies.", + "warning": "Let op: Roo Code gebruikt complexe prompts en werkt het beste met Claude-modellen. Minder krachtige modellen werken mogelijk niet zoals verwacht." + }, + "unboundApiKey": "Unbound API-sleutel", + "getUnboundApiKey": "Unbound API-sleutel ophalen", + "humanRelay": { + "description": "Geen API-sleutel vereist, maar de gebruiker moet helpen met kopiëren en plakken naar de webchat-AI.", + "instructions": "Tijdens gebruik verschijnt een dialoogvenster en wordt het huidige bericht automatisch naar het klembord gekopieerd. Je moet deze plakken in webversies van AI (zoals ChatGPT of Claude), vervolgens het antwoord van de AI terugkopiëren naar het dialoogvenster en op bevestigen klikken." + }, + "openRouter": { + "providerRouting": { + "title": "OpenRouter-providerroutering", + "description": "OpenRouter stuurt verzoeken naar de best beschikbare providers voor je model. Standaard worden verzoeken gebalanceerd over de beste providers voor maximale uptime. Je kunt echter een specifieke provider kiezen voor dit model.", + "learnMore": "Meer informatie over providerroutering" + } + }, + "customModel": { + "capabilities": "Stel de mogelijkheden en prijzen in voor je aangepaste OpenAI-compatibele model. Wees voorzichtig met het opgeven van de modelmogelijkheden, want deze kunnen de prestaties van Roo Code beïnvloeden.", + "maxTokens": { + "label": "Maximaal aantal outputtokens", + "description": "Maximaal aantal tokens dat het model in een antwoord kan genereren. (Geef -1 op om de server het maximum te laten bepalen.)" + }, + "contextWindow": { + "label": "Contextvenstergrootte", + "description": "Totaal aantal tokens (input + output) dat het model kan verwerken." + }, + "imageSupport": { + "label": "Ondersteuning voor afbeeldingen", + "description": "Kan dit model afbeeldingen verwerken en begrijpen?" + }, + "computerUse": { + "label": "Computergebruik", + "description": "Kan dit model met een browser werken? (bijv. Claude 3.7 Sonnet)." + }, + "promptCache": { + "label": "Prompt caching", + "description": "Kan dit model prompts cachen?" + }, + "pricing": { + "input": { + "label": "Invoerprijs", + "description": "Kosten per miljoen tokens in de input/prompt. Dit beïnvloedt de kosten van het verzenden van context en instructies naar het model." + }, + "output": { + "label": "Uitvoerprijs", + "description": "Kosten per miljoen tokens in het antwoord van het model. Dit beïnvloedt de kosten van gegenereerde inhoud en voltooiingen." + }, + "cacheReads": { + "label": "Cache-leesprijs", + "description": "Kosten per miljoen tokens voor het lezen uit de cache. Dit is de prijs die wordt gerekend wanneer een gecachte reactie wordt opgehaald." + }, + "cacheWrites": { + "label": "Cache-schrijfprijs", + "description": "Kosten per miljoen tokens voor het schrijven naar de cache. Dit is de prijs die wordt gerekend wanneer een prompt voor het eerst wordt gecachet." + } + }, + "resetDefaults": "Standaardwaarden herstellen" + }, + "rateLimitSeconds": { + "label": "Snelheidslimiet", + "description": "Minimale tijd tussen API-verzoeken." + }, + "reasoningEffort": { + "label": "Model redeneervermogen", + "high": "Hoog", + "medium": "Middel", + "low": "Laag" + }, + "setReasoningLevel": "Redeneervermogen inschakelen" + }, + "browser": { + "enable": { + "label": "Browserhulpmiddel inschakelen", + "description": "Indien ingeschakeld, kan Roo een browser gebruiken om te interageren met websites wanneer modellen computergebruik ondersteunen. <0>Meer informatie" + }, + "viewport": { + "label": "Viewport-grootte", + "description": "Selecteer de viewport-grootte voor browserinteracties. Dit beïnvloedt hoe websites worden weergegeven en gebruikt.", + "options": { + "largeDesktop": "Groot bureaublad (1280x800)", + "smallDesktop": "Klein bureaublad (900x600)", + "tablet": "Tablet (768x1024)", + "mobile": "Mobiel (360x640)" + } + }, + "screenshotQuality": { + "label": "Screenshotkwaliteit", + "description": "Pas de WebP-kwaliteit van browserscreenshots aan. Hogere waarden geven duidelijkere screenshots maar verhogen het tokengebruik." + }, + "remote": { + "label": "Gebruik externe browserverbinding", + "description": "Verbind met een Chrome-browser die draait met remote debugging ingeschakeld (--remote-debugging-port=9222).", + "urlPlaceholder": "Aangepaste URL (bijv. http://localhost:9222)", + "testButton": "Verbinding testen", + "testingButton": "Bezig met testen...", + "instructions": "Voer het DevTools Protocol hostadres in of laat leeg om lokale Chrome-instanties automatisch te detecteren. De knop Verbinding testen probeert de aangepaste URL als opgegeven, of detecteert automatisch als het veld leeg is." + } + }, + "checkpoints": { + "enable": { + "label": "Automatische checkpoints inschakelen", + "description": "Indien ingeschakeld, maakt Roo automatisch checkpoints tijdens het uitvoeren van taken, zodat je eenvoudig wijzigingen kunt bekijken of terugzetten. <0>Meer informatie" + } + }, + "notifications": { + "sound": { + "label": "Geluidseffecten inschakelen", + "description": "Indien ingeschakeld, speelt Roo geluidseffecten af voor meldingen en gebeurtenissen.", + "volumeLabel": "Volume" + }, + "tts": { + "label": "Tekst-naar-spraak inschakelen", + "description": "Indien ingeschakeld, leest Roo zijn antwoorden hardop voor via tekst-naar-spraak.", + "speedLabel": "Snelheid" + } + }, + "contextManagement": { + "description": "Bepaal welke informatie wordt opgenomen in het contextvenster van de AI, wat invloed heeft op tokengebruik en antwoordkwaliteit", + "openTabs": { + "label": "Limiet geopende tabbladen in context", + "description": "Maximaal aantal geopende VSCode-tabbladen dat in de context wordt opgenomen. Hogere waarden geven meer context maar verhogen het tokengebruik." + }, + "workspaceFiles": { + "label": "Limiet werkruimtebestanden in context", + "description": "Maximaal aantal bestanden dat wordt opgenomen in details van de huidige werkmap. Hogere waarden geven meer context maar verhogen het tokengebruik." + }, + "rooignore": { + "label": ".rooignore-bestanden tonen in lijsten en zoekopdrachten", + "description": "Indien ingeschakeld, worden bestanden die overeenkomen met patronen in .rooignore getoond in lijsten met een slotje. Indien uitgeschakeld, worden deze bestanden volledig verborgen in lijsten en zoekopdrachten." + }, + "maxReadFile": { + "label": "Automatisch afkappen bij bestandslezen", + "description": "Roo leest dit aantal regels wanneer het model geen begin/eindwaarden opgeeft. Als dit aantal lager is dan het totaal, genereert Roo een index van codelijnen. Speciale gevallen: -1 laat Roo het hele bestand lezen (zonder indexering), 0 leest geen regels en geeft alleen een minimale index. Lagere waarden minimaliseren het initiële contextgebruik en maken precieze vervolg-leesopdrachten mogelijk. Expliciete begin/eind-aanvragen worden niet door deze instelling beperkt.", + "lines": "regels", + "always_full_read": "Altijd volledig bestand lezen" + } + }, + "terminal": { + "basic": { + "label": "Terminalinstellingen: Basis", + "description": "Basis terminalinstellingen" + }, + "advanced": { + "label": "Terminalinstellingen: Geavanceerd", + "description": "De volgende opties vereisen mogelijk een herstart van de terminal om de instelling toe te passen." + }, + "outputLineLimit": { + "label": "Terminaluitvoerlimiet", + "description": "Maximaal aantal regels dat wordt opgenomen in de terminaluitvoer bij het uitvoeren van commando's. Overtollige regels worden uit het midden verwijderd om tokens te besparen. <0>Meer informatie" + }, + "shellIntegrationTimeout": { + "label": "Terminal shell-integratie timeout", + "description": "Maximale wachttijd voor het initialiseren van shell-integratie voordat commando's worden uitgevoerd. Voor gebruikers met lange shell-opstarttijden moet deze waarde mogelijk worden verhoogd als je 'Shell Integration Unavailable'-fouten ziet in de terminal. <0>Meer informatie" + }, + "shellIntegrationDisabled": { + "label": "Terminal shell-integratie uitschakelen", + "description": "Schakel dit in als terminalcommando's niet correct werken of als je 'Shell Integration Unavailable'-fouten ziet. Dit gebruikt een eenvoudigere methode om commando's uit te voeren en omzeilt enkele geavanceerde terminalfuncties. <0>Meer informatie" + }, + "commandDelay": { + "label": "Terminalcommando-vertraging", + "description": "Vertraging in milliseconden na het uitvoeren van een commando. De standaardinstelling van 0 schakelt de vertraging volledig uit. Dit kan helpen om te zorgen dat de uitvoer volledig wordt vastgelegd in terminals met timingproblemen. In de meeste terminals wordt dit geïmplementeerd door `PROMPT_COMMAND='sleep N'` te zetten en in Powershell wordt `start-sleep` toegevoegd aan het einde van elk commando. Oorspronkelijk was dit een workaround voor VSCode bug#237208 en is mogelijk niet meer nodig. <0>Meer informatie" + }, + "compressProgressBar": { + "label": "Voortgangsbalk-uitvoer comprimeren", + "description": "Indien ingeschakeld, verwerkt Roo terminaluitvoer met carriage returns (\r) om te simuleren hoe een echte terminal inhoud weergeeft. Dit verwijdert tussenliggende voortgangsbalken en behoudt alleen de eindstatus, waardoor er meer contextruimte overblijft. <0>Meer informatie" + }, + "powershellCounter": { + "label": "PowerShell-teller workaround inschakelen", + "description": "Indien ingeschakeld, voegt Roo een teller toe aan PowerShell-commando's om correcte uitvoering te garanderen. Dit helpt bij PowerShell-terminals die problemen hebben met het vastleggen van uitvoer. <0>Meer informatie" + }, + "zshClearEolMark": { + "label": "ZSH EOL-markering wissen", + "description": "Indien ingeschakeld, wist Roo de ZSH end-of-line markering door PROMPT_EOL_MARK='' te zetten. Dit voorkomt problemen met de interpretatie van uitvoer die eindigt met speciale tekens zoals '%'. <0>Meer informatie" + }, + "zshOhMy": { + "label": "Oh My Zsh-integratie inschakelen", + "description": "Indien ingeschakeld, zet Roo ITERM_SHELL_INTEGRATION_INSTALLED=Yes om Oh My Zsh shell-integratiefuncties te activeren. Het toepassen van deze instelling kan een herstart van de IDE vereisen. <0>Meer informatie" + }, + "zshP10k": { + "label": "Powerlevel10k-integratie inschakelen", + "description": "Indien ingeschakeld, zet Roo POWERLEVEL9K_TERM_SHELL_INTEGRATION=true om Powerlevel10k shell-integratiefuncties te activeren. <0>Meer informatie" + }, + "zdotdir": { + "label": "ZDOTDIR-afhandeling inschakelen", + "description": "Indien ingeschakeld, maakt Roo een tijdelijke map aan voor ZDOTDIR om zsh shell-integratie correct af te handelen. Dit zorgt ervoor dat VSCode shell-integratie goed werkt met zsh en je zsh-configuratie behouden blijft. <0>Meer informatie" + }, + "inheritEnv": { + "label": "Omgevingsvariabelen overnemen", + "description": "Indien ingeschakeld, neemt de terminal omgevingsvariabelen over van het bovenliggende VSCode-proces, zoals shell-integratie-instellingen uit het gebruikersprofiel. Dit schakelt direct de VSCode-instelling `terminal.integrated.inheritEnv` om. <0>Meer informatie" + } + }, + "advanced": { + "diff": { + "label": "Bewerken via diffs inschakelen", + "description": "Indien ingeschakeld kan Roo sneller bestanden bewerken en worden afgekorte volledige-bestandswijzigingen automatisch geweigerd. Werkt het beste met het nieuwste Claude 3.7 Sonnet-model.", + "strategy": { + "label": "Diff-strategie", + "options": { + "standard": "Standaard (één blok)", + "multiBlock": "Experimenteel: Multi-block diff", + "unified": "Experimenteel: Unified diff" + }, + "descriptions": { + "standard": "Standaard diff-strategie past wijzigingen toe op één codeblok tegelijk.", + "unified": "Unified diff-strategie gebruikt meerdere methoden om diffs toe te passen en kiest de beste aanpak.", + "multiBlock": "Multi-block diff-strategie laat toe om meerdere codeblokken in één verzoek bij te werken." + } + }, + "matchPrecision": { + "label": "Matchnauwkeurigheid", + "description": "Deze schuifregelaar bepaalt hoe nauwkeurig codeblokken moeten overeenkomen bij het toepassen van diffs. Lagere waarden laten flexibelere matching toe maar verhogen het risico op verkeerde vervangingen. Gebruik waarden onder 100% met uiterste voorzichtigheid." + } + } + }, + "experimental": { + "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Contextvenster intelligent comprimeren", + "description": "Gebruikt een LLM-aanroep om eerdere gesprekken samen te vatten wanneer het contextvenster van de taak bijna vol is, in plaats van oude berichten te verwijderen. Let op: de kosten van het samenvatten zijn momenteel niet inbegrepen in de API-kosten die in de interface worden getoond." + }, + "DIFF_STRATEGY_UNIFIED": { + "name": "Experimentele unified diff-strategie gebruiken", + "description": "Schakel de experimentele unified diff-strategie in. Deze strategie kan het aantal herhalingen door model fouten verminderen, maar kan onverwacht gedrag of onjuiste bewerkingen veroorzaken. Alleen inschakelen als je de risico's begrijpt en wijzigingen zorgvuldig wilt controleren." + }, + "SEARCH_AND_REPLACE": { + "name": "Experimentele zoek-en-vervang-tool gebruiken", + "description": "Schakel de experimentele zoek-en-vervang-tool in, waarmee Roo meerdere instanties van een zoekterm in één verzoek kan vervangen." + }, + "INSERT_BLOCK": { + "name": "Experimentele inhoud-invoeg-tool gebruiken", + "description": "Schakel de experimentele inhoud-invoeg-tool in, waarmee Roo inhoud op specifieke regelnummers kan invoegen zonder een diff te maken." + }, + "POWER_STEERING": { + "name": "Experimentele 'power steering'-modus gebruiken", + "description": "Indien ingeschakeld, herinnert Roo het model vaker aan de details van de huidige modusdefinitie. Dit leidt tot sterkere naleving van roldefinities en aangepaste instructies, maar gebruikt meer tokens per bericht." + }, + "MULTI_SEARCH_AND_REPLACE": { + "name": "Experimentele multi-block diff-tool gebruiken", + "description": "Indien ingeschakeld, gebruikt Roo de multi-block diff-tool. Hiermee wordt geprobeerd meerdere codeblokken in het bestand in één verzoek bij te werken." + } + }, + "promptCaching": { + "label": "Prompt caching inschakelen", + "description": "Indien ingeschakeld, gebruikt Roo dit model met prompt caching om kosten te verlagen." + }, + "temperature": { + "useCustom": "Aangepaste temperatuur gebruiken", + "description": "Bepaalt de willekeurigheid in de antwoorden van het model.", + "rangeDescription": "Hogere waarden maken de output willekeuriger, lagere waarden maken deze deterministischer." + }, + "modelInfo": { + "supportsImages": "Ondersteunt afbeeldingen", + "noImages": "Ondersteunt geen afbeeldingen", + "supportsComputerUse": "Ondersteunt computergebruik", + "noComputerUse": "Ondersteunt geen computergebruik", + "supportsPromptCache": "Ondersteunt prompt caching", + "noPromptCache": "Ondersteunt geen prompt caching", + "maxOutput": "Maximale output", + "inputPrice": "Invoerprijs", + "outputPrice": "Uitvoerprijs", + "cacheReadsPrice": "Cache-leesprijs", + "cacheWritesPrice": "Cache-schrijfprijs", + "enableStreaming": "Streaming inschakelen", + "enableR1Format": "R1-modelparameters inschakelen", + "enableR1FormatTips": "Moet ingeschakeld zijn bij gebruik van R1-modellen zoals QWQ om 400-fouten te voorkomen", + "useAzure": "Azure gebruiken", + "azureApiVersion": "Azure API-versie instellen", + "gemini": { + "freeRequests": "* Gratis tot {{count}} verzoeken per minuut. Daarna is de prijs afhankelijk van de promptgrootte.", + "pricingDetails": "Zie prijsdetails voor meer info.", + "billingEstimate": "* Facturering is een schatting - de exacte kosten hangen af van de promptgrootte." + } + }, + "modelPicker": { + "automaticFetch": "De extensie haalt automatisch de nieuwste lijst met modellen op van {{serviceName}}. Weet je niet welk model je moet kiezen? Roo Code werkt het beste met {{defaultModelId}}. Je kunt ook zoeken op 'free' voor gratis opties die nu beschikbaar zijn.", + "label": "Model", + "searchPlaceholder": "Zoeken", + "noMatchFound": "Geen overeenkomsten gevonden", + "useCustomModel": "Aangepast gebruiken: {{modelId}}" + }, + "footer": { + "feedback": "Heb je vragen of feedback? Open gerust een issue op github.com/RooVetGit/Roo-Code of sluit je aan bij reddit.com/r/RooCode of discord.gg/roocode", + "telemetry": { + "label": "Anonieme fout- en gebruiksrapportage toestaan", + "description": "Help Roo Code te verbeteren door anonieme gebruiksgegevens en foutmeldingen te verzenden. Er worden nooit code, prompts of persoonlijke gegevens verzonden. Zie ons privacybeleid voor meer informatie." + }, + "settings": { + "import": "Importeren", + "export": "Exporteren", + "reset": "Resetten" + } + }, + "thinkingBudget": { + "maxTokens": "Max tokens", + "maxThinkingTokens": "Max denk-tokens" + }, + "validation": { + "apiKey": "Je moet een geldige API-sleutel opgeven.", + "awsRegion": "Je moet een regio kiezen om Amazon Bedrock te gebruiken.", + "googleCloud": "Je moet een geldig Google Cloud Project-ID en regio opgeven.", + "modelId": "Je moet een geldig model-ID opgeven.", + "modelSelector": "Je moet een geldige modelselector opgeven.", + "openAi": "Je moet een geldige basis-URL, API-sleutel en model-ID opgeven.", + "arn": { + "invalidFormat": "Ongeldig ARN-formaat. Controleer de formaatvereisten.", + "regionMismatch": "Waarschuwing: De regio in je ARN ({{arnRegion}}) komt niet overeen met je geselecteerde regio ({{region}}). Dit kan toegangsfouten veroorzaken. De provider gebruikt de regio uit de ARN." + }, + "modelAvailability": "Het opgegeven model-ID ({{modelId}}) is niet beschikbaar. Kies een ander model." + }, + "placeholders": { + "apiKey": "Voer API-sleutel in...", + "profileName": "Voer profielnaam in", + "accessKey": "Voer toegangssleutel in...", + "secretKey": "Voer geheime sleutel in...", + "sessionToken": "Voer sessietoken in...", + "credentialsJson": "Voer Credentials JSON in...", + "keyFilePath": "Voer pad naar sleutelbestand in...", + "projectId": "Voer project-ID in...", + "customArn": "Voer ARN in (bijv. arn:aws:bedrock:us-east-1:123456789012:foundation-model/my-model)", + "baseUrl": "Voer basis-URL in...", + "modelId": { + "lmStudio": "bijv. meta-llama-3.1-8b-instruct", + "lmStudioDraft": "bijv. lmstudio-community/llama-3.2-1b-instruct", + "ollama": "bijv. llama3.1" + }, + "numbers": { + "maxTokens": "bijv. 4096", + "contextWindow": "bijv. 128000", + "inputPrice": "bijv. 0.0001", + "outputPrice": "bijv. 0.0002", + "cacheWritePrice": "bijv. 0.00005" + } + }, + "defaults": { + "ollamaUrl": "Standaard: http://localhost:11434", + "lmStudioUrl": "Standaard: http://localhost:1234", + "geminiUrl": "Standaard: https://generativelanguage.googleapis.com" + }, + "labels": { + "customArn": "Aangepaste ARN", + "useCustomArn": "Aangepaste ARN gebruiken..." + } +} diff --git a/webview-ui/src/i18n/locales/nl/welcome.json b/webview-ui/src/i18n/locales/nl/welcome.json new file mode 100644 index 00000000000..08ed651f87a --- /dev/null +++ b/webview-ui/src/i18n/locales/nl/welcome.json @@ -0,0 +1,28 @@ +{ + "greeting": "Hoi, ik ben Roo!", + "introduction": "Roo Code is de toonaangevende autonome programmeeragent. Maak je klaar om te ontwerpen, coderen, debuggen en je productiviteit te verhogen als nooit tevoren. Om door te gaan, heeft Roo Code een API-sleutel nodig.", + "notice": "Om te beginnen heeft deze extensie een API-provider nodig.", + "start": "Aan de slag!", + "chooseProvider": "Kies een API-provider om te beginnen:", + "routers": { + "requesty": { + "description": "Jouw geoptimaliseerde LLM-router", + "incentive": "$1 gratis tegoed" + }, + "openrouter": { + "description": "Een uniforme interface voor LLM's" + } + }, + "startRouter": "Snelle setup via een router", + "startCustom": "Gebruik je eigen API-sleutel", + "telemetry": { + "title": "Help Roo Code verbeteren", + "anonymousTelemetry": "Stuur anonieme fout- en gebruiksgegevens om ons te helpen bugs op te lossen en de extensie te verbeteren. Er worden nooit code, prompts of persoonlijke gegevens verzonden.", + "changeSettings": "Je kunt dit altijd wijzigen onderaan de instellingen", + "settings": "instellingen", + "allow": "Toestaan", + "deny": "Weigeren" + }, + "or": "of", + "importSettings": "Instellingen importeren" +} diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index 47b537cc867..8e9ad4dc528 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -109,7 +109,7 @@ "diffError": { "title": "Edycja nieudana" }, - "troubleMessage": "Agent ma problemy...", + "troubleMessage": "Roo ma problemy...", "apiRequest": { "title": "Zapytanie API", "failed": "Zapytanie API nie powiodło się", @@ -135,47 +135,47 @@ "current": "Bieżący" }, "instructions": { - "wantsToFetch": "Agent chce pobrać szczegółowe instrukcje, aby pomóc w bieżącym zadaniu" + "wantsToFetch": "Roo chce pobrać szczegółowe instrukcje, aby pomóc w bieżącym zadaniu" }, "fileOperations": { - "wantsToRead": "Agent chce przeczytać ten plik:", - "wantsToReadOutsideWorkspace": "Agent chce przeczytać ten plik poza obszarem roboczym:", - "didRead": "Agent przeczytał ten plik:", - "wantsToEdit": "Agent chce edytować ten plik:", - "wantsToEditOutsideWorkspace": "Agent chce edytować ten plik poza obszarem roboczym:", - "wantsToCreate": "Agent chce utworzyć nowy plik:", - "wantsToSearchReplace": "Agent chce wykonać wyszukiwanie i zamianę w tym pliku:", - "didSearchReplace": "Agent wykonał wyszukiwanie i zamianę w tym pliku:", - "wantsToInsert": "Agent chce wstawić zawartość do tego pliku:", - "wantsToInsertWithLineNumber": "Agent chce wstawić zawartość do tego pliku w linii {{lineNumber}}:", - "wantsToInsertAtEnd": "Agent chce dodać zawartość na końcu tego pliku:" + "wantsToRead": "Roo chce przeczytać ten plik:", + "wantsToReadOutsideWorkspace": "Roo chce przeczytać ten plik poza obszarem roboczym:", + "didRead": "Roo przeczytał ten plik:", + "wantsToEdit": "Roo chce edytować ten plik:", + "wantsToEditOutsideWorkspace": "Roo chce edytować ten plik poza obszarem roboczym:", + "wantsToCreate": "Roo chce utworzyć nowy plik:", + "wantsToSearchReplace": "Roo chce wykonać wyszukiwanie i zamianę w tym pliku:", + "didSearchReplace": "Roo wykonał wyszukiwanie i zamianę w tym pliku:", + "wantsToInsert": "Roo chce wstawić zawartość do tego pliku:", + "wantsToInsertWithLineNumber": "Roo chce wstawić zawartość do tego pliku w linii {{lineNumber}}:", + "wantsToInsertAtEnd": "Roo chce dodać zawartość na końcu tego pliku:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent chce zobaczyć pliki najwyższego poziomu w tym katalogu:", - "didViewTopLevel": "Agent zobaczył pliki najwyższego poziomu w tym katalogu:", - "wantsToViewRecursive": "Agent chce rekurencyjnie zobaczyć wszystkie pliki w tym katalogu:", - "didViewRecursive": "Agent rekurencyjnie zobaczył wszystkie pliki w tym katalogu:", - "wantsToViewDefinitions": "Agent chce zobaczyć nazwy definicji kodu źródłowego używane w tym katalogu:", - "didViewDefinitions": "Agent zobaczył nazwy definicji kodu źródłowego używane w tym katalogu:", - "wantsToSearch": "Agent chce przeszukać ten katalog w poszukiwaniu {{regex}}:", - "didSearch": "Agent przeszukał ten katalog w poszukiwaniu {{regex}}:" + "wantsToViewTopLevel": "Roo chce zobaczyć pliki najwyższego poziomu w tym katalogu:", + "didViewTopLevel": "Roo zobaczył pliki najwyższego poziomu w tym katalogu:", + "wantsToViewRecursive": "Roo chce rekurencyjnie zobaczyć wszystkie pliki w tym katalogu:", + "didViewRecursive": "Roo rekurencyjnie zobaczył wszystkie pliki w tym katalogu:", + "wantsToViewDefinitions": "Roo chce zobaczyć nazwy definicji kodu źródłowego używane w tym katalogu:", + "didViewDefinitions": "Roo zobaczył nazwy definicji kodu źródłowego używane w tym katalogu:", + "wantsToSearch": "Roo chce przeszukać ten katalog w poszukiwaniu {{regex}}:", + "didSearch": "Roo przeszukał ten katalog w poszukiwaniu {{regex}}:" }, "commandOutput": "Wyjście polecenia", "response": "Odpowiedź", "arguments": "Argumenty", "mcp": { - "wantsToUseTool": "Agent chce użyć narzędzia na serwerze MCP {{serverName}}:", - "wantsToAccessResource": "Agent chce uzyskać dostęp do zasobu na serwerze MCP {{serverName}}:" + "wantsToUseTool": "Roo chce użyć narzędzia na serwerze MCP {{serverName}}:", + "wantsToAccessResource": "Roo chce uzyskać dostęp do zasobu na serwerze MCP {{serverName}}:" }, "modes": { - "wantsToSwitch": "Agent chce przełączyć się na tryb {{mode}}", - "wantsToSwitchWithReason": "Agent chce przełączyć się na tryb {{mode}} ponieważ: {{reason}}", - "didSwitch": "Agent przełączył się na tryb {{mode}}", - "didSwitchWithReason": "Agent przełączył się na tryb {{mode}} ponieważ: {{reason}}" + "wantsToSwitch": "Roo chce przełączyć się na tryb {{mode}}", + "wantsToSwitchWithReason": "Roo chce przełączyć się na tryb {{mode}} ponieważ: {{reason}}", + "didSwitch": "Roo przełączył się na tryb {{mode}}", + "didSwitchWithReason": "Roo przełączył się na tryb {{mode}} ponieważ: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent chce utworzyć nowe podzadanie w trybie {{mode}}:", - "wantsToFinish": "Agent chce zakończyć to podzadanie", + "wantsToCreate": "Roo chce utworzyć nowe podzadanie w trybie {{mode}}:", + "wantsToFinish": "Roo chce zakończyć to podzadanie", "newTaskContent": "Instrukcje podzadania", "completionContent": "Podzadanie zakończone", "resultContent": "Wyniki podzadania", @@ -183,7 +183,7 @@ "completionInstructions": "Podzadanie zakończone! Możesz przejrzeć wyniki i zasugerować poprawki lub następne kroki. Jeśli wszystko wygląda dobrze, potwierdź, aby zwrócić wynik do zadania nadrzędnego." }, "questions": { - "hasQuestion": "Agent ma pytanie:" + "hasQuestion": "Roo ma pytanie:" }, "taskCompleted": "Zadanie zakończone", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "Kopiuj do pola wprowadzania (lub Shift + kliknięcie)" }, "announcement": { - "title": "🎉 Roo Code 3.15 wydany", - "description": "Agent Code 3.15 przynosi nowe funkcje i ulepszenia na podstawie Twoich opinii.", + "title": "🎉 Roo Code 3.17 wydany", + "description": "Roo Code 3.17 przynosi potężne nowe funkcje i ulepszenia na podstawie Twoich opinii.", "whatsNew": "Co nowego", - "feature1": "Buforowanie podpowiedzi dla Vertex: Dodano obsługę buforowania podpowiedzi dla Vertex AI, poprawiając czasy odpowiedzi i zmniejszając koszty API", - "feature2": "Mechanizm awaryjny dla Terminala: Zaimplementowano mechanizm awaryjny w przypadku awarii integracji powłoki terminala VSCode", - "feature3": "Ulepszone fragmenty kodu: Ulepszono renderowanie i interakcję z fragmentami kodu w interfejsie czatu", + "feature1": "Niejawne buforowanie dla Gemini: Wywołania API Gemini są teraz automatycznie buforowane, co zmniejsza koszty API", + "feature2": "Inteligentniejszy wybór trybu: Definicje trybów mogą teraz zawierać wskazówki dotyczące tego, kiedy każdy tryb powinien być używany, umożliwiając lepszą orkiestrację", + "feature3": "Inteligentne kondensowanie kontekstu: Inteligentnie podsumowuje historię rozmów, gdy kontekst zapełnia się, zamiast obcinania (włącz w Ustawienia -> Eksperymentalne)", "hideButton": "Ukryj ogłoszenie", "detailsDiscussLinks": "Uzyskaj więcej szczegółów i dołącz do dyskusji na Discord i Reddit 🚀" }, "browser": { - "rooWantsToUse": "Agent chce użyć przeglądarki:", + "rooWantsToUse": "Roo chce użyć przeglądarki:", "consoleLogs": "Logi konsoli", "noNewLogs": "(Brak nowych logów)", "screenshot": "Zrzut ekranu przeglądarki", @@ -233,6 +233,15 @@ "close": "Zamknij przeglądarkę" } }, + "codeblock": { + "tooltips": { + "expand": "Rozwiń blok kodu", + "collapse": "Zwiń blok kodu", + "enable_wrap": "Włącz zawijanie wierszy", + "disable_wrap": "Wyłącz zawijanie wierszy", + "copy_code": "Kopiuj kod" + } + }, "systemPromptWarning": "OSTRZEŻENIE: Aktywne niestandardowe zastąpienie instrukcji systemowych. Może to poważnie zakłócić funkcjonalność i powodować nieprzewidywalne zachowanie.", "shellIntegration": { "title": "Ostrzeżenie wykonania polecenia", diff --git a/webview-ui/src/i18n/locales/pl/mcp.json b/webview-ui/src/i18n/locales/pl/mcp.json index 4f1f2a9411b..13ff4970c80 100644 --- a/webview-ui/src/i18n/locales/pl/mcp.json +++ b/webview-ui/src/i18n/locales/pl/mcp.json @@ -1,29 +1,34 @@ { "title": "Serwery MCP", "done": "Gotowe", - "description": "<0>Model Context Protocol umożliwia komunikację z lokalnie uruchomionymi serwerami MCP, które zapewniają dodatkowe narzędzia i zasoby rozszerzające możliwości Roo. Możesz korzystać z <1>serwerów stworzonych przez społeczność lub poprosić Roo o utworzenie nowych narzędzi specyficznych dla twojego przepływu pracy (np. \"dodaj narzędzie, które pobiera najnowszą dokumentację npm\").", + "description": "Włącz Model Context Protocol (MCP), aby Roo Code mógł korzystać z dodatkowych narzędzi i usług z zewnętrznych serwerów. To rozszerza możliwości Roo. <0>Dowiedz się więcej", "enableToggle": { "title": "Włącz serwery MCP", - "description": "Po włączeniu, Roo będzie mógł komunikować się z serwerami MCP w celu uzyskania zaawansowanych funkcji. Jeśli nie korzystasz z MCP, możesz to wyłączyć, aby zmniejszyć zużycie tokenów przez Roo." + "description": "Włącz to, aby Roo mógł korzystać z narzędzi połączonych serwerów MCP. Daje to Roo więcej możliwości. Jeśli nie planujesz korzystać z tych dodatkowych narzędzi, wyłącz to, aby zmniejszyć koszty tokenów API." }, "enableServerCreation": { "title": "Włącz tworzenie serwerów MCP", - "description": "Po włączeniu, Roo może pomóc w tworzeniu nowych serwerów MCP za pomocą poleceń takich jak \"dodaj nowe narzędzie do...\". Jeśli nie potrzebujesz tworzyć serwerów MCP, możesz to wyłączyć, aby zmniejszyć zużycie tokenów przez Roo." + "description": "Włącz to, aby Roo mógł pomóc ci tworzyć <1>nowe niestandardowe serwery MCP. <0>Dowiedz się więcej o tworzeniu serwerów", + "hint": "Wskazówka: Aby zmniejszyć koszty tokenów API, wyłącz tę opcję, gdy nie prosisz Roo o utworzenie nowego serwera MCP." }, - "editGlobalMCP": "Edytuj globalne MCP", - "editProjectMCP": "Edytuj projektowe MCP", + "editGlobalMCP": "Edytuj globalny MCP", + "editProjectMCP": "Edytuj MCP projektu", + "learnMoreEditingSettings": "Dowiedz się więcej o edycji plików ustawień MCP", "tool": { - "alwaysAllow": "Zawsze zezwalaj", + "alwaysAllow": "Zawsze pozwalaj", "parameters": "Parametry", "noDescription": "Brak opisu" }, "tabs": { "tools": "Narzędzia", - "resources": "Zasoby" + "resources": "Zasoby", + "errors": "Błędy" }, "emptyState": { "noTools": "Nie znaleziono narzędzi", - "noResources": "Nie znaleziono zasobów" + "noResources": "Nie znaleziono zasobów", + "noLogs": "Nie znaleziono logów", + "noErrors": "Nie znaleziono błędów" }, "networkTimeout": { "label": "Limit czasu sieci", @@ -46,7 +51,7 @@ "delete": "Usuń" }, "serverStatus": { - "retrying": "Ponowna próba...", + "retrying": "Ponawianie...", "retryConnection": "Ponów połączenie" } } diff --git a/webview-ui/src/i18n/locales/pl/prompts.json b/webview-ui/src/i18n/locales/pl/prompts.json index 2782e91a4f9..b90a55ed190 100644 --- a/webview-ui/src/i18n/locales/pl/prompts.json +++ b/webview-ui/src/i18n/locales/pl/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Utwórz nowy tryb", "editModesConfig": "Edytuj konfigurację trybów", "editGlobalModes": "Edytuj tryby globalne", - "editProjectModes": "Edytuj tryby projektu (.pearai-agent-ignore)", - "createModeHelpText": "Kliknij +, aby utworzyć nowy niestandardowy tryb, lub po prostu poproś Roo w czacie, aby utworzył go dla Ciebie!" + "editProjectModes": "Edytuj tryby projektu (.roomodes)", + "createModeHelpText": "Tryby to wyspecjalizowane persony, które dostosowują zachowanie Roo. <0>Dowiedz się o używaniu trybów lub <1>dostosowywaniu trybów.", + "selectMode": "Szukaj trybów" }, "apiConfiguration": { "title": "Konfiguracja API", @@ -33,16 +34,21 @@ "resetToDefault": "Przywróć domyślne", "description": "Zdefiniuj wiedzę specjalistyczną i osobowość Roo dla tego trybu. Ten opis kształtuje, jak Roo prezentuje się i podchodzi do zadań." }, + "whenToUse": { + "title": "Kiedy używać (opcjonalne)", + "description": "Opisz, kiedy ten tryb powinien być używany. Pomaga to Orchestratorowi wybrać odpowiedni tryb dla zadania.", + "resetToDefault": "Przywróć domyślny opis 'Kiedy używać'" + }, "customInstructions": { "title": "Niestandardowe instrukcje dla trybu (opcjonalne)", "resetToDefault": "Przywróć domyślne", "description": "Dodaj wytyczne dotyczące zachowania specyficzne dla trybu {{modeName}}.", - "loadFromFile": "Niestandardowe instrukcje dla trybu {{modeName}} mogą być również ładowane z folderu .pearai-agent/rules-{{modeSlug}}/ w Twoim obszarze roboczym (.roorules-{{modeSlug}} i .clinerules-{{modeSlug}} są przestarzałe i wkrótce przestaną działać)." + "loadFromFile": "Niestandardowe instrukcje dla trybu {{mode}} mogą być również ładowane z folderu .roo/rules-{{slug}}/ w Twoim obszarze roboczym (.roorules-{{slug}} i .clinerules-{{slug}} są przestarzałe i wkrótce przestaną działać)." }, "globalCustomInstructions": { "title": "Niestandardowe instrukcje dla wszystkich trybów", - "description": "Te instrukcje dotyczą wszystkich trybów. Zapewniają podstawowy zestaw zachowań, które mogą być rozszerzone przez instrukcje specyficzne dla trybów poniżej.\nJeśli chcesz, aby Roo myślał i mówił w języku innym niż język wyświetlania Twojego edytora ({{language}}), możesz to określić tutaj.", - "loadFromFile": "Instrukcje mogą być również ładowane z folderu .pearai-agent/rules/ w Twoim obszarze roboczym (.roorules i .clinerules są przestarzałe i wkrótce przestaną działać)." + "description": "Te instrukcje dotyczą wszystkich trybów. Zapewniają podstawowy zestaw zachowań, które mogą być rozszerzone przez instrukcje specyficzne dla trybów poniżej. <0>Dowiedz się więcej", + "loadFromFile": "Instrukcje mogą być również ładowane z folderu .roo/rules/ w Twoim obszarze roboczym (.roorules i .clinerules są przestarzałe i wkrótce przestaną działać)." }, "systemPrompt": { "preview": "Podgląd podpowiedzi systemowej", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Zaawansowane: Zastąp podpowiedź systemową", - "description": "Możesz całkowicie zastąpić podpowiedź systemową dla tego trybu (oprócz definicji roli i niestandardowych instrukcji) poprzez utworzenie pliku w .pearai-agent/system-prompt-{{modeSlug}} w swoim obszarze roboczym. Jest to bardzo zaawansowana funkcja, która omija wbudowane zabezpieczenia i kontrole spójności (szczególnie wokół używania narzędzi), więc bądź ostrożny!" + "description": "<2>⚠️ Ostrzeżenie: Ta zaawansowana funkcja omija zabezpieczenia. <1>PRZECZYTAJ TO PRZED UŻYCIEM!Zastąp domyślną podpowiedź systemową, tworząc plik w .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Utwórz nowy tryb", @@ -122,7 +128,7 @@ "description": "Dostępny we wszystkich obszarach roboczych" }, "project": { - "label": "Specyficzny dla projektu (.pearai-agent-ignore)", + "label": "Specyficzny dla projektu (.roomodes)", "description": "Dostępny tylko w tym obszarze roboczym, ma pierwszeństwo przed globalnym" } }, @@ -130,6 +136,10 @@ "label": "Definicja roli", "description": "Zdefiniuj wiedzę specjalistyczną i osobowość Roo dla tego trybu." }, + "whenToUse": { + "label": "Kiedy używać (opcjonalne)", + "description": "Podaj jasny opis, kiedy ten tryb jest najbardziej efektywny i w jakich typach zadań się sprawdza." + }, "tools": { "label": "Dostępne narzędzia", "description": "Wybierz, których narzędzi może używać ten tryb." diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 8cc780dd7ca..201332a3280 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Dostawcy", - "autoApprove": "Automatyczne zatwierdzanie", - "browser": "Przeglądarka / Użycie komputera", + "autoApprove": "Auto-zatwierdzanie", + "browser": "Dostęp komputera", "checkpoints": "Punkty kontrolne", "notifications": "Powiadomienia", - "contextManagement": "Zarządzanie kontekstem", + "contextManagement": "Kontekst", "terminal": "Terminal", - "advanced": "Zaawansowane", - "experimental": "Funkcje eksperymentalne", + "experimental": "Eksperymentalne", "language": "Język", - "about": "O Roo Code", - "interface": "Interfejs" + "about": "O Roo Code" }, "autoApprove": { "description": "Pozwól Roo na automatyczne wykonywanie operacji bez wymagania zatwierdzenia. Włącz te ustawienia tylko jeśli w pełni ufasz AI i rozumiesz związane z tym zagrożenia bezpieczeństwa.", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "Profil konfiguracji", "providerDocumentation": "Dokumentacja {{provider}}", + "configProfile": "Profil konfiguracji", "description": "Zapisz różne konfiguracje API, aby szybko przełączać się między dostawcami i ustawieniami.", "apiProvider": "Dostawca API", "model": "Model", @@ -118,14 +116,22 @@ "headerValue": "Wartość nagłówka", "noCustomHeaders": "Brak zdefiniowanych niestandardowych nagłówków. Kliknij przycisk +, aby dodać.", "requestyApiKey": "Klucz API Requesty", + "refreshModels": { + "label": "Odśwież modele", + "hint": "Proszę ponownie otworzyć ustawienia, aby zobaczyć najnowsze modele." + }, "getRequestyApiKey": "Uzyskaj klucz API Requesty", "openRouterTransformsText": "Kompresuj podpowiedzi i łańcuchy wiadomości do rozmiaru kontekstu (Transformacje OpenRouter)", "anthropicApiKey": "Klucz API Anthropic", "getAnthropicApiKey": "Uzyskaj klucz API Anthropic", "anthropicUseAuthToken": "Przekaż klucz API Anthropic jako nagłówek Authorization zamiast X-Api-Key", + "chutesApiKey": "Klucz API Chutes", + "getChutesApiKey": "Uzyskaj klucz API Chutes", "deepSeekApiKey": "Klucz API DeepSeek", "getDeepSeekApiKey": "Uzyskaj klucz API DeepSeek", "geminiApiKey": "Klucz API Gemini", + "getGroqApiKey": "Uzyskaj klucz API Groq", + "groqApiKey": "Klucz API Groq", "getGeminiApiKey": "Uzyskaj klucz API Gemini", "openAiApiKey": "Klucz API OpenAI", "openAiBaseUrl": "URL bazowy", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Ustaw opcjonalny URL dla modeli Codestral.", "xaiApiKey": "Klucz API xAI", "getXaiApiKey": "Uzyskaj klucz API xAI", + "litellmApiKey": "Klucz API LiteLLM", + "litellmBaseUrl": "URL bazowy LiteLLM", "awsCredentials": "Poświadczenia AWS", "awsProfile": "Profil AWS", "awsProfileName": "Nazwa profilu AWS", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Włącz narzędzie przeglądarki", - "description": "Gdy włączone, Roo może używać przeglądarki do interakcji ze stronami internetowymi podczas korzystania z modeli obsługujących używanie komputera." + "description": "Gdy włączone, Roo może używać przeglądarki do interakcji ze stronami internetowymi podczas korzystania z modeli obsługujących używanie komputera. <0>Dowiedz się więcej" }, "viewport": { "label": "Rozmiar viewportu", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Włącz automatyczne punkty kontrolne", - "description": "Gdy włączone, Roo automatycznie utworzy punkty kontrolne podczas wykonywania zadań, ułatwiając przeglądanie zmian lub powrót do wcześniejszych stanów." + "description": "Gdy włączone, Roo automatycznie utworzy punkty kontrolne podczas wykonywania zadań, ułatwiając przeglądanie zmian lub powrót do wcześniejszych stanów. <0>Dowiedz się więcej" } }, "notifications": { @@ -298,57 +306,69 @@ "label": "Limit kontekstu plików obszaru roboczego", "description": "Maksymalna liczba plików do uwzględnienia w szczegółach bieżącego katalogu roboczego. Wyższe wartości zapewniają więcej kontekstu, ale zwiększają zużycie token." }, - "pearai-agent-ignore": { - "label": "Pokaż pliki .pearai-agent-ignore na listach i w wyszukiwaniach", - "description": "Gdy włączone, pliki pasujące do wzorców w .pearai-agent-ignore będą pokazywane na listach z symbolem kłódki. Gdy wyłączone, te pliki będą całkowicie ukryte z list plików i wyszukiwań." + "rooignore": { + "label": "Pokaż pliki .rooignore na listach i w wyszukiwaniach", + "description": "Gdy włączone, pliki pasujące do wzorców w .rooignore będą pokazywane na listach z symbolem kłódki. Gdy wyłączone, te pliki będą całkowicie ukryte z list plików i wyszukiwań." }, "maxReadFile": { "label": "Próg automatycznego skracania odczytu pliku", - "description": "Agent odczytuje tę liczbę linii, gdy model nie określa wartości początkowej/końcowej. Jeśli ta liczba jest mniejsza niż całkowita liczba linii pliku, Roo generuje indeks numerów linii definicji kodu. Przypadki specjalne: -1 nakazuje Roo odczytać cały plik (bez indeksowania), a 0 nakazuje nie czytać żadnych linii i dostarczyć tylko indeksy linii dla minimalnego kontekstu. Niższe wartości minimalizują początkowe użycie kontekstu, umożliwiając późniejsze precyzyjne odczyty zakresów linii. Jawne żądania początku/końca nie są ograniczone tym ustawieniem.", + "description": "Roo odczytuje tę liczbę linii, gdy model nie określa wartości początkowej/końcowej. Jeśli ta liczba jest mniejsza niż całkowita liczba linii pliku, Roo generuje indeks numerów linii definicji kodu. Przypadki specjalne: -1 nakazuje Roo odczytać cały plik (bez indeksowania), a 0 nakazuje nie czytać żadnych linii i dostarczyć tylko indeksy linii dla minimalnego kontekstu. Niższe wartości minimalizują początkowe użycie kontekstu, umożliwiając późniejsze precyzyjne odczyty zakresów linii. Jawne żądania początku/końca nie są ograniczone tym ustawieniem.", "lines": "linii", "always_full_read": "Zawsze czytaj cały plik" } }, "terminal": { + "basic": { + "label": "Ustawienia terminala: Podstawowe", + "description": "Podstawowe ustawienia terminala" + }, + "advanced": { + "label": "Ustawienia terminala: Zaawansowane", + "description": "Poniższe opcje mogą wymagać ponownego uruchomienia terminala, aby zastosować ustawienie." + }, "outputLineLimit": { "label": "Limit wyjścia terminala", - "description": "Maksymalna liczba linii do uwzględnienia w wyjściu terminala podczas wykonywania poleceń. Po przekroczeniu linie będą usuwane ze środka, oszczędzając token." + "description": "Maksymalna liczba linii do uwzględnienia w wyjściu terminala podczas wykonywania poleceń. Po przekroczeniu linie będą usuwane ze środka, oszczędzając token. <0>Dowiedz się więcej" }, "shellIntegrationTimeout": { "label": "Limit czasu integracji powłoki terminala", - "description": "Maksymalny czas oczekiwania na inicjalizację integracji powłoki przed wykonaniem poleceń. Dla użytkowników z długim czasem uruchamiania powłoki, ta wartość może wymagać zwiększenia, jeśli widzisz błędy \"Shell Integration Unavailable\" w terminalu." + "description": "Maksymalny czas oczekiwania na inicjalizację integracji powłoki przed wykonaniem poleceń. Dla użytkowników z długim czasem uruchamiania powłoki, ta wartość może wymagać zwiększenia, jeśli widzisz błędy \"Shell Integration Unavailable\" w terminalu. <0>Dowiedz się więcej" }, "shellIntegrationDisabled": { "label": "Wyłącz integrację powłoki terminala", - "description": "Włącz tę opcję, jeśli polecenia terminala nie działają poprawnie lub widzisz błędy 'Shell Integration Unavailable'. Używa to prostszej metody uruchamiania poleceń, omijając niektóre zaawansowane funkcje terminala." - }, - "compressProgressBar": { - "label": "Kompresuj wyjście pasków postępu", - "description": "Po włączeniu, przetwarza wyjście terminala z powrotami karetki (\\r), aby symulować sposób wyświetlania treści przez prawdziwy terminal. Usuwa to pośrednie stany pasków postępu, zachowując tylko stan końcowy, co oszczędza przestrzeń kontekstową dla bardziej istotnych informacji." - }, - "zdotdir": { - "label": "Włącz obsługę ZDOTDIR", - "description": "Po włączeniu tworzy tymczasowy katalog dla ZDOTDIR, aby poprawnie obsłużyć integrację powłoki zsh. Zapewnia to prawidłowe działanie integracji powłoki VSCode z zsh, zachowując twoją konfigurację zsh. (eksperymentalne)" + "description": "Włącz tę opcję, jeśli polecenia terminala nie działają poprawnie lub widzisz błędy 'Shell Integration Unavailable'. Używa to prostszej metody uruchamiania poleceń, omijając niektóre zaawansowane funkcje terminala. <0>Dowiedz się więcej" }, "commandDelay": { "label": "Opóźnienie poleceń terminala", - "description": "Opóźnienie w milisekundach dodawane po wykonaniu polecenia. Domyślne ustawienie 0 całkowicie wyłącza opóźnienie. Może to pomóc w zapewnieniu pełnego przechwytywania wyjścia poleceń w terminalach z problemami z synchronizacją. W większości terminali jest to implementowane przez ustawienie `PROMPT_COMMAND='sleep N'`, a PowerShell dodaje `start-sleep` na końcu każdego polecenia. Pierwotnie było to obejście błędu VSCode#237208 i może nie być potrzebne." + "description": "Opóźnienie w milisekundach dodawane po wykonaniu polecenia. Domyślne ustawienie 0 całkowicie wyłącza opóźnienie. Może to pomóc w zapewnieniu pełnego przechwytywania wyjścia poleceń w terminalach z problemami z synchronizacją. W większości terminali jest to implementowane przez ustawienie `PROMPT_COMMAND='sleep N'`, a PowerShell dodaje `start-sleep` na końcu każdego polecenia. Pierwotnie było to obejście błędu VSCode#237208 i może nie być potrzebne. <0>Dowiedz się więcej" + }, + "compressProgressBar": { + "label": "Kompresuj wyjście pasków postępu", + "description": "Po włączeniu, przetwarza wyjście terminala z powrotami karetki (\\r), aby symulować sposób wyświetlania treści przez prawdziwy terminal. Usuwa to pośrednie stany pasków postępu, zachowując tylko stan końcowy, co oszczędza przestrzeń kontekstową dla bardziej istotnych informacji. <0>Dowiedz się więcej" }, "powershellCounter": { "label": "Włącz obejście licznika PowerShell", - "description": "Po włączeniu dodaje licznik do poleceń PowerShell, aby zapewnić prawidłowe wykonanie poleceń. Pomaga to w terminalach PowerShell, które mogą mieć problemy z przechwytywaniem wyjścia." + "description": "Po włączeniu dodaje licznik do poleceń PowerShell, aby zapewnić prawidłowe wykonanie poleceń. Pomaga to w terminalach PowerShell, które mogą mieć problemy z przechwytywaniem wyjścia. <0>Dowiedz się więcej" }, "zshClearEolMark": { "label": "Wyczyść znacznik końca linii ZSH", - "description": "Po włączeniu czyści znacznik końca linii ZSH poprzez ustawienie PROMPT_EOL_MARK=''. Zapobiega to problemom z interpretacją wyjścia poleceń, gdy kończy się ono znakami specjalnymi jak '%'." + "description": "Po włączeniu czyści znacznik końca linii ZSH poprzez ustawienie PROMPT_EOL_MARK=''. Zapobiega to problemom z interpretacją wyjścia poleceń, gdy kończy się ono znakami specjalnymi jak '%'. <0>Dowiedz się więcej" }, "zshOhMy": { "label": "Włącz integrację Oh My Zsh", - "description": "Po włączeniu ustawia ITERM_SHELL_INTEGRATION_INSTALLED=Yes, aby włączyć funkcje integracji powłoki Oh My Zsh. Zastosowanie tego ustawienia może wymagać ponownego uruchomienia IDE. (eksperymentalne)" + "description": "Po włączeniu ustawia ITERM_SHELL_INTEGRATION_INSTALLED=Yes, aby włączyć funkcje integracji powłoki Oh My Zsh. Zastosowanie tego ustawienia może wymagać ponownego uruchomienia IDE. <0>Dowiedz się więcej" }, "zshP10k": { "label": "Włącz integrację Powerlevel10k", - "description": "Po włączeniu ustawia POWERLEVEL9K_TERM_SHELL_INTEGRATION=true, aby włączyć funkcje integracji powłoki Powerlevel10k. (eksperymentalne)" + "description": "Po włączeniu ustawia POWERLEVEL9K_TERM_SHELL_INTEGRATION=true, aby włączyć funkcje integracji powłoki Powerlevel10k. <0>Dowiedz się więcej" + }, + "zdotdir": { + "label": "Włącz obsługę ZDOTDIR", + "description": "Po włączeniu tworzy tymczasowy katalog dla ZDOTDIR, aby poprawnie obsłużyć integrację powłoki zsh. Zapewnia to prawidłowe działanie integracji powłoki VSCode z zsh, zachowując twoją konfigurację zsh. <0>Dowiedz się więcej" + }, + "inheritEnv": { + "label": "Dziedzicz zmienne środowiskowe", + "description": "Po włączeniu terminal dziedziczy zmienne środowiskowe z procesu nadrzędnego VSCode, takie jak ustawienia integracji powłoki zdefiniowane w profilu użytkownika. Przełącza to bezpośrednio globalne ustawienie VSCode `terminal.integrated.inheritEnv`. <0>Dowiedz się więcej" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Inteligentnie kondensuj okno kontekstu", + "description": "Używa wywołania LLM do podsumowania wcześniejszej rozmowy, gdy okno kontekstu zadania jest prawie pełne, zamiast usuwać stare wiadomości. Zastrzeżenie: koszt podsumowania nie jest obecnie uwzględniony w kosztach API pokazywanych w interfejsie." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Użyj eksperymentalnej ujednoliconej strategii diff", "description": "Włącz eksperymentalną ujednoliconą strategię diff. Ta strategia może zmniejszyć liczbę ponownych prób spowodowanych błędami modelu, ale może powodować nieoczekiwane zachowanie lub nieprawidłowe edycje. Włącz tylko jeśli rozumiesz ryzyko i jesteś gotów dokładnie przeglądać wszystkie zmiany." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Włącz buforowanie podpowiedzi", - "description": "Po włączeniu, Roo będzie używać tego modelu z włączonym buforowaniem promptów w celu zmniejszenia kosztów." + "label": "Wyłącz buforowanie promptów", + "description": "Po zaznaczeniu, Roo nie będzie używać buforowania promptów dla tego modelu." }, "temperature": { "useCustom": "Użyj niestandardowej temperatury", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Jeśli masz jakiekolwiek pytania lub opinie, śmiało otwórz zgłoszenie na github.com/RooVetGit/Roo-Code lub dołącz do reddit.com/r/RooCode lub discord.gg/roocode", - "version": "Agent v{{version}}", "telemetry": { "label": "Zezwól na anonimowe raportowanie błędów i użycia", "description": "Pomóż ulepszyć Roo Code, wysyłając anonimowe dane o użytkowaniu i raporty o błędach. Nigdy nie są wysyłane kod, podpowiedzi ani informacje osobiste. Zobacz naszą politykę prywatności, aby uzyskać więcej szczegółów." diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 23649049c66..d8aeb86f00d 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -109,7 +109,7 @@ "diffError": { "title": "Edição mal-sucedida" }, - "troubleMessage": "Agent está tendo problemas...", + "troubleMessage": "Roo está tendo problemas...", "apiRequest": { "title": "Requisição API", "failed": "Requisição API falhou", @@ -135,47 +135,47 @@ "current": "Atual" }, "instructions": { - "wantsToFetch": "Agent quer buscar instruções detalhadas para ajudar com a tarefa atual" + "wantsToFetch": "Roo quer buscar instruções detalhadas para ajudar com a tarefa atual" }, "fileOperations": { - "wantsToRead": "Agent quer ler este arquivo:", - "wantsToReadOutsideWorkspace": "Agent quer ler este arquivo fora do espaço de trabalho:", - "didRead": "Agent leu este arquivo:", - "wantsToEdit": "Agent quer editar este arquivo:", - "wantsToEditOutsideWorkspace": "Agent quer editar este arquivo fora do espaço de trabalho:", - "wantsToCreate": "Agent quer criar um novo arquivo:", - "wantsToSearchReplace": "Agent quer realizar busca e substituição neste arquivo:", - "didSearchReplace": "Agent realizou busca e substituição neste arquivo:", - "wantsToInsert": "Agent quer inserir conteúdo neste arquivo:", - "wantsToInsertWithLineNumber": "Agent quer inserir conteúdo neste arquivo na linha {{lineNumber}}:", - "wantsToInsertAtEnd": "Agent quer adicionar conteúdo ao final deste arquivo:" + "wantsToRead": "Roo quer ler este arquivo:", + "wantsToReadOutsideWorkspace": "Roo quer ler este arquivo fora do espaço de trabalho:", + "didRead": "Roo leu este arquivo:", + "wantsToEdit": "Roo quer editar este arquivo:", + "wantsToEditOutsideWorkspace": "Roo quer editar este arquivo fora do espaço de trabalho:", + "wantsToCreate": "Roo quer criar um novo arquivo:", + "wantsToSearchReplace": "Roo quer realizar busca e substituição neste arquivo:", + "didSearchReplace": "Roo realizou busca e substituição neste arquivo:", + "wantsToInsert": "Roo quer inserir conteúdo neste arquivo:", + "wantsToInsertWithLineNumber": "Roo quer inserir conteúdo neste arquivo na linha {{lineNumber}}:", + "wantsToInsertAtEnd": "Roo quer adicionar conteúdo ao final deste arquivo:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent quer visualizar os arquivos de nível superior neste diretório:", - "didViewTopLevel": "Agent visualizou os arquivos de nível superior neste diretório:", - "wantsToViewRecursive": "Agent quer visualizar recursivamente todos os arquivos neste diretório:", - "didViewRecursive": "Agent visualizou recursivamente todos os arquivos neste diretório:", - "wantsToViewDefinitions": "Agent quer visualizar nomes de definição de código-fonte usados neste diretório:", - "didViewDefinitions": "Agent visualizou nomes de definição de código-fonte usados neste diretório:", - "wantsToSearch": "Agent quer pesquisar neste diretório por {{regex}}:", - "didSearch": "Agent pesquisou neste diretório por {{regex}}:" + "wantsToViewTopLevel": "Roo quer visualizar os arquivos de nível superior neste diretório:", + "didViewTopLevel": "Roo visualizou os arquivos de nível superior neste diretório:", + "wantsToViewRecursive": "Roo quer visualizar recursivamente todos os arquivos neste diretório:", + "didViewRecursive": "Roo visualizou recursivamente todos os arquivos neste diretório:", + "wantsToViewDefinitions": "Roo quer visualizar nomes de definição de código-fonte usados neste diretório:", + "didViewDefinitions": "Roo visualizou nomes de definição de código-fonte usados neste diretório:", + "wantsToSearch": "Roo quer pesquisar neste diretório por {{regex}}:", + "didSearch": "Roo pesquisou neste diretório por {{regex}}:" }, "commandOutput": "Saída do comando", "response": "Resposta", "arguments": "Argumentos", "mcp": { - "wantsToUseTool": "Agent quer usar uma ferramenta no servidor MCP {{serverName}}:", - "wantsToAccessResource": "Agent quer acessar um recurso no servidor MCP {{serverName}}:" + "wantsToUseTool": "Roo quer usar uma ferramenta no servidor MCP {{serverName}}:", + "wantsToAccessResource": "Roo quer acessar um recurso no servidor MCP {{serverName}}:" }, "modes": { - "wantsToSwitch": "Agent quer mudar para o modo {{mode}}", - "wantsToSwitchWithReason": "Agent quer mudar para o modo {{mode}} porque: {{reason}}", - "didSwitch": "Agent mudou para o modo {{mode}}", - "didSwitchWithReason": "Agent mudou para o modo {{mode}} porque: {{reason}}" + "wantsToSwitch": "Roo quer mudar para o modo {{mode}}", + "wantsToSwitchWithReason": "Roo quer mudar para o modo {{mode}} porque: {{reason}}", + "didSwitch": "Roo mudou para o modo {{mode}}", + "didSwitchWithReason": "Roo mudou para o modo {{mode}} porque: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent quer criar uma nova subtarefa no modo {{mode}}:", - "wantsToFinish": "Agent quer finalizar esta subtarefa", + "wantsToCreate": "Roo quer criar uma nova subtarefa no modo {{mode}}:", + "wantsToFinish": "Roo quer finalizar esta subtarefa", "newTaskContent": "Instruções da subtarefa", "completionContent": "Subtarefa concluída", "resultContent": "Resultados da subtarefa", @@ -183,7 +183,7 @@ "completionInstructions": "Subtarefa concluída! Você pode revisar os resultados e sugerir correções ou próximos passos. Se tudo parecer bom, confirme para retornar o resultado à tarefa principal." }, "questions": { - "hasQuestion": "Agent tem uma pergunta:" + "hasQuestion": "Roo tem uma pergunta:" }, "taskCompleted": "Tarefa concluída", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "Copiar para entrada (ou Shift + clique)" }, "announcement": { - "title": "🎉 Roo Code 3.15 Lançado", - "description": "Agent Code 3.15 traz novos recursos e melhorias baseados no seu feedback.", + "title": "🎉 Roo Code 3.17 Lançado", + "description": "Roo Code 3.17 traz poderosos novos recursos e melhorias baseados no seu feedback.", "whatsNew": "O que há de novo", - "feature1": "Cache de prompts para Vertex: Adicionado suporte de cache de prompts para Vertex AI, melhorando tempos de resposta e reduzindo custos de API", - "feature2": "Fallback de Terminal: Implementado um mecanismo de fallback quando a integração de shell do terminal VSCode falha", - "feature3": "Snippets de código aprimorados: Renderização e interação aprimoradas de snippets de código na interface de chat", + "feature1": "Cache implícito para Gemini: Chamadas de API Gemini agora são automaticamente armazenadas em cache, reduzindo custos de API", + "feature2": "Seleção de modo mais inteligente: Definições de modo podem agora incluir orientações sobre quando cada modo deve ser usado, permitindo melhor orquestração", + "feature3": "Condensação inteligente de contexto: Resume de forma inteligente o histórico de conversas quando o contexto fica cheio, em vez de truncar (ative em Configurações -> Experimental)", "hideButton": "Ocultar anúncio", "detailsDiscussLinks": "Obtenha mais detalhes e participe da discussão no Discord e Reddit 🚀" }, "browser": { - "rooWantsToUse": "Agent quer usar o navegador:", + "rooWantsToUse": "Roo quer usar o navegador:", "consoleLogs": "Logs do console", "noNewLogs": "(Sem novos logs)", "screenshot": "Captura de tela do navegador", @@ -233,6 +233,15 @@ "close": "Fechar navegador" } }, + "codeblock": { + "tooltips": { + "expand": "Expandir bloco de código", + "collapse": "Recolher bloco de código", + "enable_wrap": "Ativar quebra de linha", + "disable_wrap": "Desativar quebra de linha", + "copy_code": "Copiar código" + } + }, "systemPromptWarning": "AVISO: Substituição personalizada de instrução do sistema ativa. Isso pode comprometer gravemente a funcionalidade e causar comportamento imprevisível.", "shellIntegration": { "title": "Aviso de execução de comando", diff --git a/webview-ui/src/i18n/locales/pt-BR/mcp.json b/webview-ui/src/i18n/locales/pt-BR/mcp.json index 2c8c282e0d4..c67698a0c0c 100644 --- a/webview-ui/src/i18n/locales/pt-BR/mcp.json +++ b/webview-ui/src/i18n/locales/pt-BR/mcp.json @@ -1,17 +1,19 @@ { "title": "Servidores MCP", "done": "Concluído", - "description": "O <0>Model Context Protocol permite a comunicação com servidores MCP em execução localmente que fornecem ferramentas e recursos adicionais para estender as capacidades do Roo. Você pode usar <1>servidores criados pela comunidade ou pedir ao Roo para criar novas ferramentas específicas para seu fluxo de trabalho (por exemplo, \"adicionar uma ferramenta que obtém a documentação mais recente do npm\").", + "description": "Ative o Model Context Protocol (MCP) para permitir que o Roo Code use ferramentas e serviços extras de servidores externos. Isso amplia o que o Roo pode fazer por você. <0>Saiba mais", "enableToggle": { "title": "Ativar servidores MCP", - "description": "Quando ativado, o Roo poderá interagir com servidores MCP para funcionalidades avançadas. Se você não estiver usando MCP, pode desativar isso para reduzir o uso de tokens do Roo." + "description": "Ative para que o Roo possa usar ferramentas de servidores MCP conectados. Isso dá mais capacidades ao Roo. Se você não pretende usar essas ferramentas extras, desative para ajudar a reduzir os custos de tokens da API." }, "enableServerCreation": { "title": "Ativar criação de servidores MCP", - "description": "Quando ativado, o Roo pode ajudar você a criar novos servidores MCP por meio de comandos como \"adicionar uma nova ferramenta para...\". Se você não precisar criar servidores MCP, pode desativar isso para reduzir o uso de tokens do Roo." + "description": "Ative para que o Roo possa te ajudar a criar <1>novos servidores MCP personalizados. <0>Saiba mais sobre criação de servidores", + "hint": "Dica: Para reduzir os custos de tokens da API, desative esta configuração quando não estiver pedindo ao Roo para criar um novo servidor MCP." }, - "editGlobalMCP": "Editar MCP Global", - "editProjectMCP": "Editar MCP do Projeto", + "editGlobalMCP": "Editar MCP global", + "editProjectMCP": "Editar MCP do projeto", + "learnMoreEditingSettings": "Saiba mais sobre como editar arquivos de configuração MCP", "tool": { "alwaysAllow": "Sempre permitir", "parameters": "Parâmetros", @@ -19,15 +21,18 @@ }, "tabs": { "tools": "Ferramentas", - "resources": "Recursos" + "resources": "Recursos", + "errors": "Erros" }, "emptyState": { "noTools": "Nenhuma ferramenta encontrada", - "noResources": "Nenhum recurso encontrado" + "noResources": "Nenhum recurso encontrado", + "noLogs": "Nenhum log encontrado", + "noErrors": "Nenhum erro encontrado" }, "networkTimeout": { "label": "Tempo limite de rede", - "description": "Tempo máximo de espera para respostas do servidor", + "description": "Tempo máximo de espera por respostas do servidor", "options": { "15seconds": "15 segundos", "30seconds": "30 segundos", @@ -47,6 +52,6 @@ }, "serverStatus": { "retrying": "Tentando novamente...", - "retryConnection": "Tentar conexão novamente" + "retryConnection": "Tentar reconectar" } } diff --git a/webview-ui/src/i18n/locales/pt-BR/prompts.json b/webview-ui/src/i18n/locales/pt-BR/prompts.json index b18b0fa0a70..2b378cbcc21 100644 --- a/webview-ui/src/i18n/locales/pt-BR/prompts.json +++ b/webview-ui/src/i18n/locales/pt-BR/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Criar novo modo", "editModesConfig": "Editar configuração de modos", "editGlobalModes": "Editar modos globais", - "editProjectModes": "Editar modos do projeto (.pearai-agent-ignore)", - "createModeHelpText": "Clique em + para criar um novo modo personalizado, ou simplesmente peça ao Roo no chat para criar um para você!" + "editProjectModes": "Editar modos do projeto (.roomodes)", + "createModeHelpText": "Modos são personas especializadas que adaptam o comportamento do Roo. <0>Saiba mais sobre o uso de modos ou <1>Personalização de modos.", + "selectMode": "Buscar modos" }, "apiConfiguration": { "title": "Configuração de API", @@ -33,16 +34,21 @@ "resetToDefault": "Restaurar para padrão", "description": "Defina a expertise e personalidade do Roo para este modo. Esta descrição molda como o Roo se apresenta e aborda tarefas." }, + "whenToUse": { + "title": "Quando usar (opcional)", + "description": "Descreva quando este modo deve ser usado. Isso ajuda o Orchestrator a escolher o modo certo para uma tarefa.", + "resetToDefault": "Restaurar descrição 'Quando usar' para padrão" + }, "customInstructions": { "title": "Instruções personalizadas específicas do modo (opcional)", "resetToDefault": "Restaurar para padrão", "description": "Adicione diretrizes comportamentais específicas para o modo {{modeName}}.", - "loadFromFile": "Instruções personalizadas específicas para o modo {{modeName}} também podem ser carregadas da pasta .pearai-agent/rules-{{modeSlug}}/ no seu espaço de trabalho (.roorules-{{modeSlug}} e .clinerules-{{modeSlug}} estão obsoletos e deixarão de funcionar em breve)." + "loadFromFile": "Instruções personalizadas específicas para o modo {{mode}} também podem ser carregadas da pasta .roo/rules-{{slug}}/ no seu espaço de trabalho (.roorules-{{slug}} e .clinerules-{{slug}} estão obsoletos e deixarão de funcionar em breve)." }, "globalCustomInstructions": { "title": "Instruções personalizadas para todos os modos", - "description": "Estas instruções se aplicam a todos os modos. Elas fornecem um conjunto base de comportamentos que podem ser aprimorados por instruções específicas do modo abaixo.\nSe você desejar que o Roo pense e fale em um idioma diferente do idioma de exibição do seu editor ({{language}}), você pode especificá-lo aqui.", - "loadFromFile": "As instruções também podem ser carregadas da pasta .pearai-agent/rules/ no seu espaço de trabalho (.roorules e .clinerules estão obsoletos e deixarão de funcionar em breve)." + "description": "Estas instruções se aplicam a todos os modos. Elas fornecem um conjunto base de comportamentos que podem ser aprimorados por instruções específicas do modo abaixo. <0>Saiba mais", + "loadFromFile": "As instruções também podem ser carregadas da pasta .roo/rules/ no seu espaço de trabalho (.roorules e .clinerules estão obsoletos e deixarão de funcionar em breve)." }, "systemPrompt": { "preview": "Visualizar prompt do sistema", @@ -75,7 +81,7 @@ }, "IMPROVE": { "label": "Melhorar Código", - "description": "Receba sugestões para otimização de código, melhores práticas e melhorias arquitetônicas mantendo a funcionalidade. Disponível nas ações de código (ícone de lâmpada no editor) e no menu de contexto do editor (clique direito no código selecionado)." + "description": "Receba sugestões para otimização de código, melhores práticas e melhorias arquitetônicas mantendo la funcionalidade. Disponível nas ações de código (ícone de lâmpada no editor) e no menu de contexto do editor (clique direito no código selecionado)." }, "ADD_TO_CONTEXT": { "label": "Adicionar ao Contexto", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Avançado: Substituir prompt do sistema", - "description": "Você pode substituir completamente o prompt do sistema para este modo (além da definição de função e instruções personalizadas) criando um arquivo em .pearai-agent/system-prompt-{{modeSlug}} no seu espaço de trabalho. Esta é uma funcionalidade muito avançada que contorna as salvaguardas integradas e verificações de consistência (especialmente em torno do uso de ferramentas), então tenha cuidado!" + "description": "<2>⚠️ Aviso: Este recurso avançado ignora as proteções. <1>LEIA ISTO ANTES DE USAR!Substitua o prompt do sistema padrão criando um arquivo em .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Criar novo modo", @@ -122,7 +128,7 @@ "description": "Disponível em todos os espaços de trabalho" }, "project": { - "label": "Específico do projeto (.pearai-agent-ignore)", + "label": "Específico do projeto (.roomodes)", "description": "Disponível apenas neste espaço de trabalho, tem precedência sobre o global" } }, @@ -130,6 +136,10 @@ "label": "Definição de função", "description": "Defina a expertise e personalidade do Roo para este modo." }, + "whenToUse": { + "label": "Quando usar (opcional)", + "description": "Forneça uma descrição clara de quando este modo é mais eficaz e para quais tipos de tarefas ele se destaca." + }, "tools": { "label": "Ferramentas disponíveis", "description": "Selecione quais ferramentas este modo pode usar." diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index b6e0e6e491d..ac768c15bd0 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Provedores", - "autoApprove": "Aprovação automática", - "browser": "Navegador / Uso do computador", - "checkpoints": "Pontos de verificação", + "autoApprove": "Aprovação", + "browser": "Navegador", + "checkpoints": "Checkpoints", "notifications": "Notificações", - "contextManagement": "Gestão de contexto", + "contextManagement": "Contexto", "terminal": "Terminal", - "advanced": "Avançado", - "experimental": "Recursos experimentais", + "experimental": "Experimental", "language": "Idioma", - "about": "Sobre o Roo Code", - "interface": "Interface" + "about": "Sobre" }, "autoApprove": { "description": "Permitir que o Roo realize operações automaticamente sem exigir aprovação. Ative essas configurações apenas se confiar totalmente na IA e compreender os riscos de segurança associados.", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "Perfil de configuração", "providerDocumentation": "Documentação do {{provider}}", + "configProfile": "Perfil de configuração", "description": "Salve diferentes configurações de API para alternar rapidamente entre provedores e configurações.", "apiProvider": "Provedor de API", "model": "Modelo", @@ -118,14 +116,22 @@ "headerValue": "Valor do cabeçalho", "noCustomHeaders": "Nenhum cabeçalho personalizado definido. Clique no botão + para adicionar um.", "requestyApiKey": "Chave de API Requesty", + "refreshModels": { + "label": "Atualizar modelos", + "hint": "Por favor, reabra as configurações para ver os modelos mais recentes." + }, "getRequestyApiKey": "Obter chave de API Requesty", "openRouterTransformsText": "Comprimir prompts e cadeias de mensagens para o tamanho do contexto (Transformações OpenRouter)", "anthropicApiKey": "Chave de API Anthropic", "getAnthropicApiKey": "Obter chave de API Anthropic", "anthropicUseAuthToken": "Passar a chave de API Anthropic como cabeçalho Authorization em vez de X-Api-Key", + "chutesApiKey": "Chave de API Chutes", + "getChutesApiKey": "Obter chave de API Chutes", "deepSeekApiKey": "Chave de API DeepSeek", "getDeepSeekApiKey": "Obter chave de API DeepSeek", "geminiApiKey": "Chave de API Gemini", + "getGroqApiKey": "Obter chave de API Groq", + "groqApiKey": "Chave de API Groq", "getGeminiApiKey": "Obter chave de API Gemini", "openAiApiKey": "Chave de API OpenAI", "openAiBaseUrl": "URL Base", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Defina uma URL alternativa para o modelo Codestral.", "xaiApiKey": "Chave de API xAI", "getXaiApiKey": "Obter chave de API xAI", + "litellmApiKey": "Chave API LiteLLM", + "litellmBaseUrl": "URL base LiteLLM", "awsCredentials": "Credenciais AWS", "awsProfile": "Perfil AWS", "awsProfileName": "Nome do Perfil AWS", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Ativar ferramenta de navegador", - "description": "Quando ativado, o Roo pode usar um navegador para interagir com sites ao usar modelos que suportam o uso do computador." + "description": "Quando ativado, o Roo pode usar um navegador para interagir com sites ao usar modelos que suportam o uso do computador. <0>Saiba mais" }, "viewport": { "label": "Tamanho da viewport", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Ativar pontos de verificação automáticos", - "description": "Quando ativado, o Roo criará automaticamente pontos de verificação durante a execução de tarefas, facilitando a revisão de alterações ou o retorno a estados anteriores." + "description": "Quando ativado, o Roo criará automaticamente pontos de verificação durante a execução de tarefas, facilitando a revisão de alterações ou o retorno a estados anteriores. <0>Saiba mais" } }, "notifications": { @@ -298,9 +306,9 @@ "label": "Limite de contexto de arquivos do espaço de trabalho", "description": "Número máximo de arquivos a incluir nos detalhes do diretório de trabalho atual. Valores mais altos fornecem mais contexto, mas aumentam o uso de token." }, - "pearai-agent-ignore": { - "label": "Mostrar arquivos .pearai-agent-ignore em listas e pesquisas", - "description": "Quando ativado, os arquivos que correspondem aos padrões em .pearai-agent-ignore serão mostrados em listas com um símbolo de cadeado. Quando desativado, esses arquivos serão completamente ocultos das listas de arquivos e pesquisas." + "rooignore": { + "label": "Mostrar arquivos .rooignore em listas e pesquisas", + "description": "Quando ativado, os arquivos que correspondem aos padrões em .rooignore serão mostrados em listas com um símbolo de cadeado. Quando desativado, esses arquivos serão completamente ocultos das listas de arquivos e pesquisas." }, "maxReadFile": { "label": "Limite de auto-truncamento de leitura de arquivo", @@ -310,45 +318,57 @@ } }, "terminal": { + "basic": { + "label": "Configurações do terminal: Básicas", + "description": "Configurações básicas do terminal" + }, + "advanced": { + "label": "Configurações do terminal: Avançadas", + "description": "As seguintes opções podem exigir reiniciar o terminal para aplicar a configuração." + }, "outputLineLimit": { "label": "Limite de saída do terminal", - "description": "Número máximo de linhas a incluir na saída do terminal ao executar comandos. Quando excedido, as linhas serão removidas do meio, economizando token." + "description": "Número máximo de linhas a incluir na saída do terminal ao executar comandos. Quando excedido, as linhas serão removidas do meio, economizando token. <0>Saiba mais" }, "shellIntegrationTimeout": { "label": "Tempo limite de integração do shell do terminal", - "description": "Tempo máximo de espera para a inicialização da integração do shell antes de executar comandos. Para usuários com tempos de inicialização de shell longos, este valor pode precisar ser aumentado se você vir erros \"Shell Integration Unavailable\" no terminal." + "description": "Tempo máximo de espera para a inicialização da integração do shell antes de executar comandos. Para usuários com tempos de inicialização de shell longos, este valor pode precisar ser aumentado se você vir erros \"Shell Integration Unavailable\" no terminal. <0>Saiba mais" }, "shellIntegrationDisabled": { "label": "Desativar integração do shell do terminal", - "description": "Ative isso se os comandos do terminal não estiverem funcionando corretamente ou se você vir erros de 'Shell Integration Unavailable'. Isso usa um método mais simples para executar comandos, ignorando alguns recursos avançados do terminal." - }, - "compressProgressBar": { - "label": "Comprimir saída de barras de progresso", - "description": "Quando ativado, processa a saída do terminal com retornos de carro (\\r) para simular como um terminal real exibiria o conteúdo. Isso remove os estados intermediários das barras de progresso, mantendo apenas o estado final, o que conserva espaço de contexto para informações mais relevantes." - }, - "zdotdir": { - "label": "Ativar gerenciamento do ZDOTDIR", - "description": "Quando ativado, cria um diretório temporário para o ZDOTDIR para lidar corretamente com a integração do shell zsh. Isso garante que a integração do shell do VSCode funcione corretamente com o zsh enquanto preserva sua configuração do zsh. (experimental)" + "description": "Ative isso se os comandos do terminal não estiverem funcionando corretamente ou se você vir erros de 'Shell Integration Unavailable'. Isso usa um método mais simples para executar comandos, ignorando alguns recursos avançados do terminal. <0>Saiba mais" }, "commandDelay": { "label": "Atraso de comando do terminal", - "description": "Atraso em milissegundos para adicionar após a execução do comando. A configuração padrão de 0 desativa completamente o atraso. Isso pode ajudar a garantir que a saída do comando seja totalmente capturada em terminais com problemas de temporização. Na maioria dos terminais, isso é implementado definindo `PROMPT_COMMAND='sleep N'` e o PowerShell adiciona `start-sleep` ao final de cada comando. Originalmente era uma solução para o bug VSCode#237208 e pode não ser necessário." + "description": "Atraso em milissegundos para adicionar após a execução do comando. A configuração padrão de 0 desativa completamente o atraso. Isso pode ajudar a garantir que a saída do comando seja totalmente capturada em terminais com problemas de temporização. Na maioria dos terminais, isso é implementado definindo `PROMPT_COMMAND='sleep N'` e o PowerShell adiciona `start-sleep` ao final de cada comando. Originalmente era uma solução para o bug VSCode#237208 e pode não ser necessário. <0>Saiba mais" + }, + "compressProgressBar": { + "label": "Comprimir saída de barras de progresso", + "description": "Quando ativado, processa a saída do terminal com retornos de carro (\\r) para simular como um terminal real exibiria o conteúdo. Isso remove os estados intermediários das barras de progresso, mantendo apenas o estado final, o que conserva espaço de contexto para informações mais relevantes. <0>Saiba mais" }, "powershellCounter": { "label": "Ativar solução alternativa do contador PowerShell", - "description": "Quando ativado, adiciona um contador aos comandos PowerShell para garantir a execução correta dos comandos. Isso ajuda com terminais PowerShell que podem ter problemas com a captura de saída." + "description": "Quando ativado, adiciona um contador aos comandos PowerShell para garantir a execução correta dos comandos. Isso ajuda com terminais PowerShell que podem ter problemas com a captura de saída. <0>Saiba mais" }, "zshClearEolMark": { "label": "Limpar marca de fim de linha do ZSH", - "description": "Quando ativado, limpa a marca de fim de linha do ZSH definindo PROMPT_EOL_MARK=''. Isso evita problemas com a interpretação da saída de comandos quando termina com caracteres especiais como '%'." + "description": "Quando ativado, limpa a marca de fim de linha do ZSH definindo PROMPT_EOL_MARK=''. Isso evita problemas com a interpretação da saída de comandos quando termina com caracteres especiais como '%'. <0>Saiba mais" }, "zshOhMy": { "label": "Ativar integração Oh My Zsh", - "description": "Quando ativado, define ITERM_SHELL_INTEGRATION_INSTALLED=Yes para habilitar os recursos de integração do shell Oh My Zsh. A aplicação desta configuração pode exigir a reinicialização do IDE. (experimental)" + "description": "Quando ativado, define ITERM_SHELL_INTEGRATION_INSTALLED=Yes para habilitar os recursos de integração do shell Oh My Zsh. A aplicação desta configuração pode exigir a reinicialização do IDE. <0>Saiba mais" }, "zshP10k": { "label": "Ativar integração Powerlevel10k", - "description": "Quando ativado, define POWERLEVEL9K_TERM_SHELL_INTEGRATION=true para habilitar os recursos de integração do shell Powerlevel10k. (experimental)" + "description": "Quando ativado, define POWERLEVEL9K_TERM_SHELL_INTEGRATION=true para habilitar os recursos de integração do shell Powerlevel10k. <0>Saiba mais" + }, + "zdotdir": { + "label": "Ativar gerenciamento do ZDOTDIR", + "description": "Quando ativado, cria um diretório temporário para o ZDOTDIR para lidar corretamente com a integração do shell zsh. Isso garante que a integração do shell do VSCode funcione corretamente com o zsh enquanto preserva sua configuração do zsh. <0>Saiba mais" + }, + "inheritEnv": { + "label": "Herdar variáveis de ambiente", + "description": "Quando ativado, o terminal herda variáveis de ambiente do processo pai do VSCode, como configurações de integração do shell definidas no perfil do usuário. Isso alterna diretamente a configuração global do VSCode `terminal.integrated.inheritEnv`. <0>Saiba mais" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Condensar inteligentemente a janela de contexto", + "description": "Usa uma chamada LLM para resumir a conversa anterior quando a janela de contexto da tarefa está quase cheia, em vez de descartar mensagens antigas. Aviso: o custo de resumir não está atualmente incluído nos custos de API mostrados na interface." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Usar estratégia diff unificada experimental", "description": "Ativar a estratégia diff unificada experimental. Esta estratégia pode reduzir o número de novas tentativas causadas por erros do modelo, mas pode causar comportamento inesperado ou edições incorretas. Ative apenas se compreender os riscos e estiver disposto a revisar cuidadosamente todas as alterações." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Ativar cache de prompts", - "description": "Quando ativado, o Roo usará este modelo com o cache de prompts ativado para reduzir custos." + "label": "Desativar cache de prompts", + "description": "Quando marcado, o Roo não usará o cache de prompts para este modelo." }, "temperature": { "useCustom": "Usar temperatura personalizada", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Se tiver alguma dúvida ou feedback, sinta-se à vontade para abrir um problema em github.com/RooVetGit/Roo-Code ou juntar-se a reddit.com/r/RooCode ou discord.gg/roocode", - "version": "Agent v{{version}}", "telemetry": { "label": "Permitir relatórios anônimos de erros e uso", "description": "Ajude a melhorar o Roo Code enviando dados de uso anônimos e relatórios de erros. Nunca são enviados código, prompts ou informações pessoais. Consulte nossa política de privacidade para mais detalhes." diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index f768a59d8d8..225d709a739 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -130,47 +130,47 @@ "current": "Текущая" }, "instructions": { - "wantsToFetch": "Agent хочет получить подробные инструкции для помощи с текущей задачей" + "wantsToFetch": "Roo хочет получить подробные инструкции для помощи с текущей задачей" }, "fileOperations": { - "wantsToRead": "Agent хочет прочитать этот файл:", - "wantsToReadOutsideWorkspace": "Agent хочет прочитать этот файл вне рабочей области:", - "didRead": "Agent прочитал этот файл:", - "wantsToEdit": "Agent хочет отредактировать этот файл:", - "wantsToEditOutsideWorkspace": "Agent хочет отредактировать этот файл вне рабочей области:", - "wantsToCreate": "Agent хочет создать новый файл:", - "wantsToSearchReplace": "Agent хочет выполнить поиск и замену в этом файле:", - "didSearchReplace": "Agent выполнил поиск и замену в этом файле:", - "wantsToInsert": "Agent хочет вставить содержимое в этот файл:", - "wantsToInsertWithLineNumber": "Agent хочет вставить содержимое в этот файл на строку {{lineNumber}}:", - "wantsToInsertAtEnd": "Agent хочет добавить содержимое в конец этого файла:" + "wantsToRead": "Roo хочет прочитать этот файл:", + "wantsToReadOutsideWorkspace": "Roo хочет прочитать этот файл вне рабочей области:", + "didRead": "Roo прочитал этот файл:", + "wantsToEdit": "Roo хочет отредактировать этот файл:", + "wantsToEditOutsideWorkspace": "Roo хочет отредактировать этот файл вне рабочей области:", + "wantsToCreate": "Roo хочет создать новый файл:", + "wantsToSearchReplace": "Roo хочет выполнить поиск и замену в этом файле:", + "didSearchReplace": "Roo выполнил поиск и замену в этом файле:", + "wantsToInsert": "Roo хочет вставить содержимое в этот файл:", + "wantsToInsertWithLineNumber": "Roo хочет вставить содержимое в этот файл на строку {{lineNumber}}:", + "wantsToInsertAtEnd": "Roo хочет добавить содержимое в конец этого файла:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent хочет просмотреть файлы верхнего уровня в этой директории:", - "didViewTopLevel": "Agent просмотрел файлы верхнего уровня в этой директории:", - "wantsToViewRecursive": "Agent хочет рекурсивно просмотреть все файлы в этой директории:", - "didViewRecursive": "Agent рекурсивно просмотрел все файлы в этой директории:", - "wantsToViewDefinitions": "Agent хочет просмотреть имена определений исходного кода в этой директории:", - "didViewDefinitions": "Agent просмотрел имена определений исходного кода в этой директории:", - "wantsToSearch": "Agent хочет выполнить поиск в этой директории по {{regex}}:", - "didSearch": "Agent выполнил поиск в этой директории по {{regex}}:" + "wantsToViewTopLevel": "Roo хочет просмотреть файлы верхнего уровня в этой директории:", + "didViewTopLevel": "Roo просмотрел файлы верхнего уровня в этой директории:", + "wantsToViewRecursive": "Roo хочет рекурсивно просмотреть все файлы в этой директории:", + "didViewRecursive": "Roo рекурсивно просмотрел все файлы в этой директории:", + "wantsToViewDefinitions": "Roo хочет просмотреть имена определений исходного кода в этой директории:", + "didViewDefinitions": "Roo просмотрел имена определений исходного кода в этой директории:", + "wantsToSearch": "Roo хочет выполнить поиск в этой директории по {{regex}}:", + "didSearch": "Roo выполнил поиск в этой директории по {{regex}}:" }, "commandOutput": "Вывод команды", "response": "Ответ", "arguments": "Аргументы", "mcp": { - "wantsToUseTool": "Agent хочет использовать инструмент на сервере MCP {{serverName}}:", - "wantsToAccessResource": "Agent хочет получить доступ к ресурсу на сервере MCP {{serverName}}:" + "wantsToUseTool": "Roo хочет использовать инструмент на сервере MCP {{serverName}}:", + "wantsToAccessResource": "Roo хочет получить доступ к ресурсу на сервере MCP {{serverName}}:" }, "modes": { - "wantsToSwitch": "Agent хочет переключиться в режим {{mode}}", - "wantsToSwitchWithReason": "Agent хочет переключиться в режим {{mode}}, потому что: {{reason}}", - "didSwitch": "Agent переключился в режим {{mode}}", - "didSwitchWithReason": "Agent переключился в режим {{mode}}, потому что: {{reason}}" + "wantsToSwitch": "Roo хочет переключиться в режим {{mode}}", + "wantsToSwitchWithReason": "Roo хочет переключиться в режим {{mode}}, потому что: {{reason}}", + "didSwitch": "Roo переключился в режим {{mode}}", + "didSwitchWithReason": "Roo переключился в режим {{mode}}, потому что: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent хочет создать новую подзадачу в режиме {{mode}}:", - "wantsToFinish": "Agent хочет завершить эту подзадачу", + "wantsToCreate": "Roo хочет создать новую подзадачу в режиме {{mode}}:", + "wantsToFinish": "Roo хочет завершить эту подзадачу", "newTaskContent": "Инструкции по подзадаче", "completionContent": "Подзадача завершена", "resultContent": "Результаты подзадачи", @@ -195,12 +195,12 @@ "description": "Автоодобрение позволяет Roo Code выполнять действия без запроса разрешения. Включайте только для полностью доверенных действий. Более подробная настройка доступна в Настройках." }, "announcement": { - "title": "🎉 Выпущен Roo Code 3.15", - "description": "Agent Code 3.15 приносит новые функции и улучшения на основе ваших отзывов.", + "title": "🎉 Выпущен Roo Code 3.17", + "description": "Roo Code 3.17 приносит мощные новые функции и улучшения на основе ваших отзывов.", "whatsNew": "Что нового", - "feature1": "Кэширование запросов для Vertex: Добавлена поддержка кэширования запросов для Vertex AI, улучшающая время отклика и снижающая затраты на API", - "feature2": "Резервный механизм для терминала: Реализован резервный механизм на случай сбоя интеграции оболочки терминала VSCode", - "feature3": "Улучшенные фрагменты кода: Улучшено отображение и взаимодействие с фрагментами кода в интерфейсе чата", + "feature1": "Неявное кэширование для Gemini: Вызовы API Gemini теперь автоматически кэшируются, сокращая затраты на API", + "feature2": "Умнее выбор режимов: Определения режимов теперь могут включать указания о том, когда следует использовать каждый режим, обеспечивая лучшую оркестрацию", + "feature3": "Интеллектуальное сжатие контекста: Интеллектуально обобщает историю разговоров, когда контекст заполняется, вместо усечения (включите в Настройки -> Экспериментальные)", "hideButton": "Скрыть объявление", "detailsDiscussLinks": "Подробнее и обсуждение в Discord и Reddit 🚀" }, @@ -212,7 +212,7 @@ "copyToInput": "Скопировать во ввод (то же, что shift + клик)" }, "browser": { - "rooWantsToUse": "Agent хочет использовать браузер:", + "rooWantsToUse": "Roo хочет использовать браузер:", "consoleLogs": "Логи консоли", "noNewLogs": "(Новых логов нет)", "screenshot": "Скриншот браузера", @@ -233,6 +233,15 @@ "close": "Закрыть браузер" } }, + "codeblock": { + "tooltips": { + "expand": "Развернуть блок кода", + "collapse": "Свернуть блок кода", + "enable_wrap": "Включить перенос строк", + "disable_wrap": "Отключить перенос строк", + "copy_code": "Копировать код" + } + }, "systemPromptWarning": "ПРЕДУПРЕЖДЕНИЕ: Активна пользовательская системная подсказка. Это может серьезно нарушить работу и вызвать непредсказуемое поведение.", "shellIntegration": { "title": "Предупреждение о выполнении команды", diff --git a/webview-ui/src/i18n/locales/ru/mcp.json b/webview-ui/src/i18n/locales/ru/mcp.json index e99bedc2c36..e11e01ce4f1 100644 --- a/webview-ui/src/i18n/locales/ru/mcp.json +++ b/webview-ui/src/i18n/locales/ru/mcp.json @@ -1,17 +1,19 @@ { - "title": "MCP-серверы", + "title": "Серверы MCP", "done": "Готово", - "description": "<0>Model Context Protocol обеспечивает связь с локально запущенными MCP-серверами, которые предоставляют дополнительные инструменты и ресурсы для расширения возможностей Roo. Вы можете использовать <1>серверы, созданные сообществом, или попросить Roo создать новые инструменты, специфичные для вашего рабочего процесса (например, «добавь инструмент, который получает последние npm-документацию»).", + "description": "Включи Model Context Protocol (MCP), чтобы Roo Code мог использовать дополнительные инструменты и сервисы с внешних серверов. Это расширяет возможности Roo. <0>Подробнее", "enableToggle": { - "title": "Включить MCP-серверы", - "description": "Если включено, Roo сможет взаимодействовать с MCP-серверами для расширенной функциональности. Если вы не используете MCP, вы можете отключить эту функцию, чтобы сократить использование токенов Roo." + "title": "Включить серверы MCP", + "description": "Включи, чтобы Roo мог использовать инструменты с подключённых серверов MCP. Это даст Roo больше возможностей. Если не планируешь использовать эти дополнительные инструменты, выключи для экономии токенов API." }, "enableServerCreation": { - "title": "Включить создание MCP-серверов", - "description": "Если включено, Roo сможет помогать вам создавать новые MCP-серверы с помощью команд вроде «добавь новый инструмент для...». Если вам не нужно создавать MCP-серверы, вы можете отключить эту функцию, чтобы сократить использование токенов Roo." + "title": "Включить создание серверов MCP", + "description": "Включи, чтобы Roo помогал создавать <1>новые кастомные серверы MCP. <0>Подробнее о создании серверов", + "hint": "Совет: чтобы снизить расходы на токены API, отключай эту настройку, когда не просишь Roo создать новый сервер MCP." }, "editGlobalMCP": "Редактировать глобальный MCP", "editProjectMCP": "Редактировать проектный MCP", + "learnMoreEditingSettings": "Подробнее о редактировании файлов настроек MCP", "tool": { "alwaysAllow": "Всегда разрешать", "parameters": "Параметры", @@ -19,11 +21,14 @@ }, "tabs": { "tools": "Инструменты", - "resources": "Ресурсы" + "resources": "Ресурсы", + "errors": "Ошибки" }, "emptyState": { "noTools": "Инструменты не найдены", - "noResources": "Ресурсы не найдены" + "noResources": "Ресурсы не найдены", + "noLogs": "Логи не найдены", + "noErrors": "Ошибки не найдены" }, "networkTimeout": { "label": "Тайм-аут сети", @@ -40,8 +45,8 @@ } }, "deleteDialog": { - "title": "Удалить MCP-сервер", - "description": "Вы уверены, что хотите удалить MCP-сервер «{{serverName}}»? Это действие нельзя будет отменить.", + "title": "Удалить сервер MCP", + "description": "Ты уверен, что хочешь удалить сервер MCP \"{{serverName}}\"? Это действие нельзя отменить.", "cancel": "Отмена", "delete": "Удалить" }, diff --git a/webview-ui/src/i18n/locales/ru/prompts.json b/webview-ui/src/i18n/locales/ru/prompts.json index 86058aca833..2f92ac8793a 100644 --- a/webview-ui/src/i18n/locales/ru/prompts.json +++ b/webview-ui/src/i18n/locales/ru/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Создать новый режим", "editModesConfig": "Редактировать конфигурацию режимов", "editGlobalModes": "Редактировать глобальные режимы", - "editProjectModes": "Редактировать режимы проекта (.pearai-agent-ignore)", - "createModeHelpText": "Нажмите +, чтобы создать новый пользовательский режим, или просто попросите Roo в чате создать его для вас!" + "editProjectModes": "Редактировать режимы проекта (.roomodes)", + "createModeHelpText": "Режимы — это специализированные персоны, которые адаптируют поведение Roo. <0>Узнайте об использовании режимов или <1>настройке режимов.", + "selectMode": "Поиск режимов" }, "apiConfiguration": { "title": "Конфигурация API", @@ -33,16 +34,21 @@ "resetToDefault": "Сбросить по умолчанию", "description": "Определите экспертность и личность Roo для этого режима. Это описание формирует, как Roo будет себя вести и выполнять задачи." }, + "whenToUse": { + "title": "Когда использовать (необязательно)", + "description": "Опишите, когда следует использовать этот режим. Это помогает Orchestrator выбрать правильный режим для задачи.", + "resetToDefault": "Сбросить описание 'Когда использовать' по умолчанию" + }, "customInstructions": { "title": "Пользовательские инструкции для режима (необязательно)", "resetToDefault": "Сбросить по умолчанию", "description": "Добавьте рекомендации по поведению, специфичные для режима {{modeName}}.", - "loadFromFile": "Пользовательские инструкции для режима {{mode}} также можно загрузить из папки .pearai-agent/rules-{{slug}}/ в вашем рабочем пространстве (.roorules-{{slug}} и .clinerules-{{slug}} устарели и скоро перестанут работать)." + "loadFromFile": "Пользовательские инструкции для режима {{mode}} также можно загрузить из папки .roo/rules-{{slug}}/ в вашем рабочем пространстве (.roorules-{{slug}} и .clinerules-{{slug}} устарели и скоро перестанут работать)." }, "globalCustomInstructions": { "title": "Пользовательские инструкции для всех режимов", - "description": "Эти инструкции применяются ко всем режимам. Они задают базовое поведение, которое можно расширить с помощью инструкций ниже.\nЕсли вы хотите, чтобы Roo думал и говорил на другом языке, отличном от языка редактора ({{language}}), укажите это здесь.", - "loadFromFile": "Инструкции также можно загрузить из папки .pearai-agent/rules/ в вашем рабочем пространстве (.roorules и .clinerules устарели и скоро перестанут работать)." + "description": "Эти инструкции применяются ко всем режимам. Они задают базовое поведение, которое можно расширить с помощью инструкций ниже. <0>Узнать больше", + "loadFromFile": "Инструкции также можно загрузить из папки .roo/rules/ в вашем рабочем пространстве (.roorules и .clinerules устарели и скоро перестанут работать)." }, "systemPrompt": { "preview": "Предпросмотр системного промпта", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Дополнительно: переопределить системный промпт", - "description": "Вы можете полностью заменить системный промпт для этого режима (кроме определения роли и пользовательских инструкций), создав файл .pearai-agent/system-prompt-{{slug}} в вашем рабочем пространстве. Это продвинутая функция, которая обходит встроенные ограничения и проверки (особенно для инструментов), поэтому будьте осторожны!" + "description": "<2>⚠️ Внимание: Эта расширенная функция обходит средства защиты. <1>ПРОЧТИТЕ ЭТО ПЕРЕД ИСПОЛЬЗОВАНИЕМ!Переопределите системный промпт по умолчанию, создав файл в .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Создать новый режим", @@ -122,7 +128,7 @@ "description": "Доступно во всех рабочих пространствах" }, "project": { - "label": "Для проекта (.pearai-agent-ignore)", + "label": "Для проекта (.roomodes)", "description": "Доступно только в этом рабочем пространстве, имеет приоритет над глобальными" } }, @@ -130,6 +136,10 @@ "label": "Определение роли", "description": "Определите экспертность и личность Roo для этого режима." }, + "whenToUse": { + "label": "Когда использовать (необязательно)", + "description": "Предоставьте четкое описание, когда этот режим наиболее эффективен и для каких типов задач он лучше всего подходит." + }, "tools": { "label": "Доступные инструменты", "description": "Выберите, какие инструменты может использовать этот режим." diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 6f39c47d154..6d5cc719703 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -23,16 +23,14 @@ "sections": { "providers": "Провайдеры", "autoApprove": "Автоодобрение", - "browser": "Браузер / Использование компьютера", + "browser": "Доступ к компьютеру", "checkpoints": "Контрольные точки", "notifications": "Уведомления", - "contextManagement": "Управление контекстом", + "contextManagement": "Контекст", "terminal": "Терминал", - "advanced": "Дополнительно", - "experimental": "Экспериментальные функции", + "experimental": "Экспериментальное", "language": "Язык", - "about": "О Roo Code", - "interface": "Интерфейс" + "about": "О Roo Code" }, "autoApprove": { "description": "Разрешить Roo автоматически выполнять операции без необходимости одобрения. Включайте эти параметры только если полностью доверяете ИИ и понимаете связанные с этим риски безопасности.", @@ -118,14 +116,22 @@ "headerValue": "Значение заголовка", "noCustomHeaders": "Пользовательские заголовки не определены. Нажмите кнопку +, чтобы добавить.", "requestyApiKey": "Requesty API-ключ", + "refreshModels": { + "label": "Обновить модели", + "hint": "Пожалуйста, откройте настройки заново, чтобы увидеть последние модели." + }, "getRequestyApiKey": "Получить Requesty API-ключ", "openRouterTransformsText": "Сжимать подсказки и цепочки сообщений до размера контекста (OpenRouter Transforms)", "anthropicApiKey": "Anthropic API-ключ", "getAnthropicApiKey": "Получить Anthropic API-ключ", "anthropicUseAuthToken": "Передавать Anthropic API-ключ как Authorization-заголовок вместо X-Api-Key", + "chutesApiKey": "Chutes API-ключ", + "getChutesApiKey": "Получить Chutes API-ключ", "deepSeekApiKey": "DeepSeek API-ключ", "getDeepSeekApiKey": "Получить DeepSeek API-ключ", "geminiApiKey": "Gemini API-ключ", + "getGroqApiKey": "Получить Groq API-ключ", + "groqApiKey": "Groq API-ключ", "getGeminiApiKey": "Получить Gemini API-ключ", "openAiApiKey": "OpenAI API-ключ", "openAiBaseUrl": "Базовый URL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Укажите альтернативный URL для модели Codestral.", "xaiApiKey": "xAI API-ключ", "getXaiApiKey": "Получить xAI API-ключ", + "litellmApiKey": "API-ключ LiteLLM", + "litellmBaseUrl": "Базовый URL LiteLLM", "awsCredentials": "AWS-учётные данные", "awsProfile": "Профиль AWS", "awsProfileName": "Имя профиля AWS", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Включить инструмент браузера", - "description": "Если включено, Roo может использовать браузер для взаимодействия с сайтами при использовании моделей, поддерживающих работу с компьютером." + "description": "Если включено, Roo может использовать браузер для взаимодействия с сайтами при использовании моделей, поддерживающих работу с компьютером. <0>Подробнее" }, "viewport": { "label": "Размер окна просмотра", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Включить автоматические контрольные точки", - "description": "Если включено, Roo будет автоматически создавать контрольные точки во время выполнения задач, что упрощает просмотр изменений или возврат к предыдущим состояниям." + "description": "Если включено, Roo будет автоматически создавать контрольные точки во время выполнения задач, что упрощает просмотр изменений или возврат к предыдущим состояниям. <0>Подробнее" } }, "notifications": { @@ -310,45 +318,57 @@ } }, "terminal": { + "basic": { + "label": "Настройки терминала: Основные", + "description": "Основные настройки терминала" + }, + "advanced": { + "label": "Настройки терминала: Расширенные", + "description": "Следующие параметры могут потребовать перезапуск терминала для применения настроек." + }, "outputLineLimit": { "label": "Лимит вывода терминала", - "description": "Максимальное количество строк, включаемых в вывод терминала при выполнении команд. При превышении строки из середины будут удаляться для экономии токенов." + "description": "Максимальное количество строк, включаемых в вывод терминала при выполнении команд. При превышении строки из середины будут удаляться для экономии токенов. <0>Подробнее" }, "shellIntegrationTimeout": { "label": "Таймаут интеграции оболочки терминала", - "description": "Максимальное время ожидания инициализации интеграции оболочки перед выполнением команд. Для пользователей с долгим стартом shell это значение можно увеличить, если появляются ошибки \"Shell Integration Unavailable\"." + "description": "Максимальное время ожидания инициализации интеграции оболочки перед выполнением команд. Для пользователей с долгим стартом shell это значение можно увеличить, если появляются ошибки \"Shell Integration Unavailable\". <0>Подробнее" }, "shellIntegrationDisabled": { "label": "Отключить интеграцию оболочки терминала", - "description": "Включите это, если команды терминала не работают должным образом или вы видите ошибки 'Shell Integration Unavailable'. Это использует более простой метод выполнения команд, обходя некоторые расширенные функции терминала." + "description": "Включите это, если команды терминала не работают должным образом или вы видите ошибки 'Shell Integration Unavailable'. Это использует более простой метод выполнения команд, обходя некоторые расширенные функции терминала. <0>Подробнее" }, "commandDelay": { "label": "Задержка команды терминала", - "description": "Задержка в миллисекундах после выполнения команды. Значение по умолчанию 0 полностью отключает задержку. Это может помочь захватить весь вывод в терминалах с проблемами синхронизации. Обычно реализуется установкой `PROMPT_COMMAND='sleep N'`, в Powershell добавляется `start-sleep` в конец команды. Изначально было обходом бага VSCode #237208 и может не требоваться." + "description": "Задержка в миллисекундах после выполнения команды. Значение по умолчанию 0 полностью отключает задержку. Это может помочь захватить весь вывод в терминалах с проблемами синхронизации. Обычно реализуется установкой `PROMPT_COMMAND='sleep N'`, в Powershell добавляется `start-sleep` в конец команды. Изначально было обходом бага VSCode #237208 и может не требоваться. <0>Подробнее" }, "compressProgressBar": { "label": "Сжимать вывод прогресс-бара", - "description": "Если включено, обрабатывает вывод терминала с возвратами каретки (\\r), имитируя отображение в реальном терминале. Промежуточные состояния прогресс-бара удаляются, остаётся только финальное, что экономит место в контексте." + "description": "Если включено, обрабатывает вывод терминала с возвратами каретки (\\r), имитируя отображение в реальном терминале. Промежуточные состояния прогресс-бара удаляются, остаётся только финальное, что экономит место в контексте. <0>Подробнее" }, "powershellCounter": { "label": "Включить обходчик счётчика PowerShell", - "description": "Если включено, добавляет счётчик к командам PowerShell для корректного выполнения. Помогает при проблемах с захватом вывода в терминалах PowerShell." + "description": "Если включено, добавляет счётчик к командам PowerShell для корректного выполнения. Помогает при проблемах с захватом вывода в терминалах PowerShell. <0>Подробнее" }, "zshClearEolMark": { "label": "Очищать метку конца строки ZSH", - "description": "Если включено, очищает PROMPT_EOL_MARK в zsh, чтобы избежать проблем с интерпретацией вывода, когда он заканчивается специальными символами типа '%'." + "description": "Если включено, очищает PROMPT_EOL_MARK в zsh, чтобы избежать проблем с интерпретацией вывода, когда он заканчивается специальными символами типа '%'. <0>Подробнее" }, "zshOhMy": { "label": "Включить интеграцию Oh My Zsh", - "description": "Если включено, устанавливает ITERM_SHELL_INTEGRATION_INSTALLED=Yes для поддержки функций интеграции Oh My Zsh. Применение этой настройки может потребовать перезапуска IDE. (экспериментально)" + "description": "Если включено, устанавливает ITERM_SHELL_INTEGRATION_INSTALLED=Yes для поддержки функций интеграции Oh My Zsh. Применение этой настройки может потребовать перезапуска IDE. <0>Подробнее" }, "zshP10k": { "label": "Включить интеграцию Powerlevel10k", - "description": "Если включено, устанавливает POWERLEVEL9K_TERM_SHELL_INTEGRATION=true для поддержки функций Powerlevel10k. (экспериментально)" + "description": "Если включено, устанавливает POWERLEVEL9K_TERM_SHELL_INTEGRATION=true для поддержки функций Powerlevel10k. <0>Подробнее" }, "zdotdir": { "label": "Включить обработку ZDOTDIR", - "description": "Если включено, создаёт временную директорию для ZDOTDIR для корректной интеграции zsh. Это обеспечивает корректную работу интеграции VSCode с zsh, сохраняя вашу конфигурацию. (экспериментально)" + "description": "Если включено, создаёт временную директорию для ZDOTDIR для корректной интеграции zsh. Это обеспечивает корректную работу интеграции VSCode с zsh, сохраняя вашу конфигурацию. <0>Подробнее" + }, + "inheritEnv": { + "label": "Наследовать переменные среды", + "description": "Если включено, терминал будет наследовать переменные среды от родительского процесса VSCode, такие как настройки интеграции оболочки, определённые в профиле пользователя. Напрямую переключает глобальную настройку VSCode `terminal.integrated.inheritEnv`. <0>Подробнее" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Интеллектуальное сжатие контекстного окна", + "description": "Использует вызов LLM для обобщения предыдущего разговора, когда контекстное окно задачи почти заполнено, вместо удаления старых сообщений. Примечание: стоимость обобщения в настоящее время не включена в стоимость API, отображаемую в интерфейсе." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Использовать экспериментальную стратегию унифицированного диффа", "description": "Включает экспериментальную стратегию унифицированного диффа. Может уменьшить количество повторных попыток из-за ошибок модели, но может привести к неожиданному поведению или неверным правкам. Включайте только если готовы внимательно проверять все изменения." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Включить кэширование подсказок", - "description": "Если включено, Roo будет использовать эту модель с включенным кэшированием подсказок для снижения затрат." + "label": "Отключить кэширование промптов", + "description": "Если отмечено, Roo не будет использовать кэширование промптов для этой модели." }, "temperature": { "useCustom": "Использовать пользовательскую температуру", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index a8f4045090b..fa8184899e4 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -1,5 +1,5 @@ { - "greeting": "Agent Code'a Hoş Geldiniz", + "greeting": "Roo Code'a Hoş Geldiniz", "task": { "title": "Görev", "seeMore": "Daha fazla gör", @@ -109,7 +109,7 @@ "diffError": { "title": "Düzenleme Başarısız" }, - "troubleMessage": "Agent sorun yaşıyor...", + "troubleMessage": "Roo sorun yaşıyor...", "apiRequest": { "title": "API İsteği", "failed": "API İsteği Başarısız", @@ -135,47 +135,47 @@ "current": "Mevcut" }, "instructions": { - "wantsToFetch": "Agent mevcut göreve yardımcı olmak için ayrıntılı talimatlar almak istiyor" + "wantsToFetch": "Roo mevcut göreve yardımcı olmak için ayrıntılı talimatlar almak istiyor" }, "fileOperations": { - "wantsToRead": "Agent bu dosyayı okumak istiyor:", - "wantsToReadOutsideWorkspace": "Agent çalışma alanı dışındaki bu dosyayı okumak istiyor:", - "didRead": "Agent bu dosyayı okudu:", - "wantsToEdit": "Agent bu dosyayı düzenlemek istiyor:", - "wantsToEditOutsideWorkspace": "Agent çalışma alanı dışındaki bu dosyayı düzenlemek istiyor:", - "wantsToCreate": "Agent yeni bir dosya oluşturmak istiyor:", - "wantsToSearchReplace": "Agent bu dosyada arama ve değiştirme yapmak istiyor:", - "didSearchReplace": "Agent bu dosyada arama ve değiştirme yaptı:", - "wantsToInsert": "Agent bu dosyaya içerik eklemek istiyor:", - "wantsToInsertWithLineNumber": "Agent bu dosyanın {{lineNumber}}. satırına içerik eklemek istiyor:", - "wantsToInsertAtEnd": "Agent bu dosyanın sonuna içerik eklemek istiyor:" + "wantsToRead": "Roo bu dosyayı okumak istiyor:", + "wantsToReadOutsideWorkspace": "Roo çalışma alanı dışındaki bu dosyayı okumak istiyor:", + "didRead": "Roo bu dosyayı okudu:", + "wantsToEdit": "Roo bu dosyayı düzenlemek istiyor:", + "wantsToEditOutsideWorkspace": "Roo çalışma alanı dışındaki bu dosyayı düzenlemek istiyor:", + "wantsToCreate": "Roo yeni bir dosya oluşturmak istiyor:", + "wantsToSearchReplace": "Roo bu dosyada arama ve değiştirme yapmak istiyor:", + "didSearchReplace": "Roo bu dosyada arama ve değiştirme yaptı:", + "wantsToInsert": "Roo bu dosyaya içerik eklemek istiyor:", + "wantsToInsertWithLineNumber": "Roo bu dosyanın {{lineNumber}}. satırına içerik eklemek istiyor:", + "wantsToInsertAtEnd": "Roo bu dosyanın sonuna içerik eklemek istiyor:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent bu dizindeki üst düzey dosyaları görüntülemek istiyor:", - "didViewTopLevel": "Agent bu dizindeki üst düzey dosyaları görüntüledi:", - "wantsToViewRecursive": "Agent bu dizindeki tüm dosyaları özyinelemeli olarak görüntülemek istiyor:", - "didViewRecursive": "Agent bu dizindeki tüm dosyaları özyinelemeli olarak görüntüledi:", - "wantsToViewDefinitions": "Agent bu dizinde kullanılan kaynak kod tanımlama isimlerini görüntülemek istiyor:", - "didViewDefinitions": "Agent bu dizinde kullanılan kaynak kod tanımlama isimlerini görüntüledi:", - "wantsToSearch": "Agent bu dizinde {{regex}} için arama yapmak istiyor:", - "didSearch": "Agent bu dizinde {{regex}} için arama yaptı:" + "wantsToViewTopLevel": "Roo bu dizindeki üst düzey dosyaları görüntülemek istiyor:", + "didViewTopLevel": "Roo bu dizindeki üst düzey dosyaları görüntüledi:", + "wantsToViewRecursive": "Roo bu dizindeki tüm dosyaları özyinelemeli olarak görüntülemek istiyor:", + "didViewRecursive": "Roo bu dizindeki tüm dosyaları özyinelemeli olarak görüntüledi:", + "wantsToViewDefinitions": "Roo bu dizinde kullanılan kaynak kod tanımlama isimlerini görüntülemek istiyor:", + "didViewDefinitions": "Roo bu dizinde kullanılan kaynak kod tanımlama isimlerini görüntüledi:", + "wantsToSearch": "Roo bu dizinde {{regex}} için arama yapmak istiyor:", + "didSearch": "Roo bu dizinde {{regex}} için arama yaptı:" }, "commandOutput": "Komut Çıktısı", "response": "Yanıt", "arguments": "Argümanlar", "mcp": { - "wantsToUseTool": "Agent {{serverName}} MCP sunucusunda bir araç kullanmak istiyor:", - "wantsToAccessResource": "Agent {{serverName}} MCP sunucusundaki bir kaynağa erişmek istiyor:" + "wantsToUseTool": "Roo {{serverName}} MCP sunucusunda bir araç kullanmak istiyor:", + "wantsToAccessResource": "Roo {{serverName}} MCP sunucusundaki bir kaynağa erişmek istiyor:" }, "modes": { - "wantsToSwitch": "Agent {{mode}} moduna geçmek istiyor", - "wantsToSwitchWithReason": "Agent {{mode}} moduna geçmek istiyor çünkü: {{reason}}", - "didSwitch": "Agent {{mode}} moduna geçti", - "didSwitchWithReason": "Agent {{mode}} moduna geçti çünkü: {{reason}}" + "wantsToSwitch": "Roo {{mode}} moduna geçmek istiyor", + "wantsToSwitchWithReason": "Roo {{mode}} moduna geçmek istiyor çünkü: {{reason}}", + "didSwitch": "Roo {{mode}} moduna geçti", + "didSwitchWithReason": "Roo {{mode}} moduna geçti çünkü: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent {{mode}} modunda yeni bir alt görev oluşturmak istiyor:", - "wantsToFinish": "Agent bu alt görevi bitirmek istiyor", + "wantsToCreate": "Roo {{mode}} modunda yeni bir alt görev oluşturmak istiyor:", + "wantsToFinish": "Roo bu alt görevi bitirmek istiyor", "newTaskContent": "Alt Görev Talimatları", "completionContent": "Alt Görev Tamamlandı", "resultContent": "Alt Görev Sonuçları", @@ -202,17 +202,17 @@ "copyToInput": "Giriş alanına kopyala (veya Shift + tıklama)" }, "announcement": { - "title": "🎉 Roo Code 3.15 Yayınlandı", - "description": "Agent Code 3.15 geri bildirimlerinize dayalı yeni özellikler ve iyileştirmeler getiriyor.", + "title": "🎉 Roo Code 3.17 Yayınlandı", + "description": "Roo Code 3.17 geri bildirimlerinize dayalı güçlü yeni özellikler ve iyileştirmeler getiriyor.", "whatsNew": "Yenilikler", - "feature1": "Vertex için İstem Önbelleği: Vertex AI için istem önbelleği desteği eklendi, yanıt sürelerini iyileştiriyor ve API maliyetlerini azaltıyor", - "feature2": "Terminal Yedek Mekanizması: VSCode terminal kabuk entegrasyonu başarısız olduğunda devreye giren yedek mekanizma uygulandı", - "feature3": "Geliştirilmiş Kod Parçacıkları: Sohbet arayüzünde kod parçacıklarının görüntülenmesi ve etkileşimi iyileştirildi", + "feature1": "Gemini için Örtük Önbelleğe Alma: Gemini API çağrıları artık otomatik olarak önbelleğe alınıyor, API maliyetlerini azaltıyor", + "feature2": "Daha Akıllı Mod Seçimi: Mod tanımları artık her modun ne zaman kullanılması gerektiğine dair rehberlik içerebiliyor, daha iyi orkestrasyon sağlıyor", + "feature3": "Akıllı Bağlam Yoğunlaştırma: Bağlam dolduğunda kesip atmak yerine konuşma geçmişini akıllıca özetliyor (Ayarlar -> Deneysel bölümünden etkinleştirin)", "hideButton": "Duyuruyu gizle", "detailsDiscussLinks": "Discord ve Reddit üzerinde daha fazla ayrıntı edinin ve tartışmalara katılın 🚀" }, "browser": { - "rooWantsToUse": "Agent tarayıcıyı kullanmak istiyor:", + "rooWantsToUse": "Roo tarayıcıyı kullanmak istiyor:", "consoleLogs": "Konsol Kayıtları", "noNewLogs": "(Yeni kayıt yok)", "screenshot": "Tarayıcı ekran görüntüsü", @@ -233,6 +233,15 @@ "close": "Tarayıcıyı kapat" } }, + "codeblock": { + "tooltips": { + "expand": "Kod bloğunu genişlet", + "collapse": "Kod bloğunu daralt", + "enable_wrap": "Satır kaydırmayı etkinleştir", + "disable_wrap": "Satır kaydırmayı devre dışı bırak", + "copy_code": "Kodu kopyala" + } + }, "systemPromptWarning": "UYARI: Özel sistem komut geçersiz kılma aktif. Bu işlevselliği ciddi şekilde bozabilir ve öngörülemeyen davranışlara neden olabilir.", "shellIntegration": { "title": "Komut Çalıştırma Uyarısı", diff --git a/webview-ui/src/i18n/locales/tr/mcp.json b/webview-ui/src/i18n/locales/tr/mcp.json index c21b009773c..05f77cdccc0 100644 --- a/webview-ui/src/i18n/locales/tr/mcp.json +++ b/webview-ui/src/i18n/locales/tr/mcp.json @@ -1,17 +1,19 @@ { "title": "MCP Sunucuları", - "done": "Tamam", - "description": "<0>Model Context Protocol, Roo'nun yeteneklerini genişletmek için ek araçlar ve kaynaklar sağlayan yerel olarak çalışan MCP sunucularıyla iletişim kurmanızı sağlar. <1>Topluluk tarafından oluşturulan sunucuları kullanabilir veya Roo'dan iş akışınıza özel yeni araçlar oluşturmasını isteyebilirsiniz (örneğin, \"en son npm belgelerini alan bir araç ekle\").", + "done": "Bitti", + "description": "Roo Code'un harici sunuculardan ek araçlar ve servisler kullanabilmesi için Model Context Protocol (MCP)'yi etkinleştir. Böylece Roo senin için daha fazlasını yapabilir. <0>Daha fazla bilgi", "enableToggle": { "title": "MCP Sunucularını Etkinleştir", - "description": "Etkinleştirildiğinde, Roo gelişmiş işlevler için MCP sunucularıyla etkileşim kurabilecektir. MCP kullanmıyorsanız, Roo'nun token kullanımını azaltmak için bunu devre dışı bırakabilirsiniz." + "description": "Bunu AÇ, böylece Roo bağlı MCP sunucularından araçlar kullanabilir. Roo'ya daha fazla yetenek kazandırır. Ekstra araçları kullanmayacaksan, API token maliyetini azaltmak için bunu KAPAT." }, "enableServerCreation": { "title": "MCP Sunucu Oluşturmayı Etkinleştir", - "description": "Etkinleştirildiğinde, Roo \"için yeni bir araç ekle...\" gibi komutlar aracılığıyla yeni MCP sunucuları oluşturmanıza yardımcı olabilir. MCP sunucuları oluşturmanız gerekmiyorsa, Roo'nun token kullanımını azaltmak için bunu devre dışı bırakabilirsiniz." + "description": "Bunu AÇ, Roo'nun <1>yeni özel MCP sunucuları oluşturmanda sana yardımcı olmasını sağlar. <0>Sunucu oluşturma hakkında bilgi al", + "hint": "İpucu: API token maliyetini azaltmak için Roo'dan yeni bir MCP sunucusu oluşturmasını istemediğinde bu ayarı kapat." }, "editGlobalMCP": "Global MCP'yi Düzenle", - "editProjectMCP": "Proje MCP'yi Düzenle", + "editProjectMCP": "Proje MCP'sini Düzenle", + "learnMoreEditingSettings": "MCP ayar dosyalarını düzenleme hakkında daha fazla bilgi", "tool": { "alwaysAllow": "Her zaman izin ver", "parameters": "Parametreler", @@ -19,15 +21,18 @@ }, "tabs": { "tools": "Araçlar", - "resources": "Kaynaklar" + "resources": "Kaynaklar", + "errors": "Hatalar" }, "emptyState": { "noTools": "Araç bulunamadı", - "noResources": "Kaynak bulunamadı" + "noResources": "Kaynak bulunamadı", + "noLogs": "Kayıt bulunamadı", + "noErrors": "Hata bulunamadı" }, "networkTimeout": { "label": "Ağ Zaman Aşımı", - "description": "Sunucu yanıtları için beklenecek maksimum süre", + "description": "Sunucu yanıtları için maksimum bekleme süresi", "options": { "15seconds": "15 saniye", "30seconds": "30 saniye", @@ -41,12 +46,12 @@ }, "deleteDialog": { "title": "MCP Sunucusunu Sil", - "description": "\"{{serverName}}\" MCP sunucusunu silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "description": "\"{{serverName}}\" MCP sunucusunu silmek istediğine emin misin? Bu işlem geri alınamaz.", "cancel": "İptal", "delete": "Sil" }, "serverStatus": { - "retrying": "Yeniden deneniyor...", - "retryConnection": "Bağlantıyı Yeniden Dene" + "retrying": "Tekrar deneniyor...", + "retryConnection": "Bağlantıyı tekrar dene" } } diff --git a/webview-ui/src/i18n/locales/tr/prompts.json b/webview-ui/src/i18n/locales/tr/prompts.json index 1e505dc07de..42849a1ed1d 100644 --- a/webview-ui/src/i18n/locales/tr/prompts.json +++ b/webview-ui/src/i18n/locales/tr/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Yeni mod oluştur", "editModesConfig": "Mod yapılandırmasını düzenle", "editGlobalModes": "Global modları düzenle", - "editProjectModes": "Proje modlarını düzenle (.pearai-agent-ignore)", - "createModeHelpText": "Yeni bir özel mod oluşturmak için + düğmesine tıklayın veya sohbette Roo'dan sizin için bir tane oluşturmasını isteyin!" + "editProjectModes": "Proje modlarını düzenle (.roomodes)", + "createModeHelpText": "Modlar, Roo'nun davranışını uyarlayan özel kişiliklerdir. <0>Modları Kullanma Hakkında Bilgi Edinin veya <1>Modları Özelleştirme.", + "selectMode": "Modları Ara" }, "apiConfiguration": { "title": "API Yapılandırması", @@ -33,16 +34,21 @@ "resetToDefault": "Varsayılana sıfırla", "description": "Bu mod için Roo'nun uzmanlığını ve kişiliğini tanımlayın. Bu açıklama, Roo'nun kendini nasıl sunduğunu ve görevlere nasıl yaklaştığını şekillendirir." }, + "whenToUse": { + "title": "Ne zaman kullanılmalı (isteğe bağlı)", + "description": "Bu modun ne zaman kullanılması gerektiğini açıklayın. Bu, Orchestrator'ın bir görev için doğru modu seçmesine yardımcı olur.", + "resetToDefault": "'Ne zaman kullanılmalı' açıklamasını varsayılana sıfırla" + }, "customInstructions": { "title": "Moda özgü özel talimatlar (isteğe bağlı)", "resetToDefault": "Varsayılana sıfırla", "description": "{{modeName}} modu için özel davranış yönergeleri ekleyin.", - "loadFromFile": "{{mode}} moduna özgü özel talimatlar ayrıca çalışma alanınızdaki .pearai-agent/rules-{{slug}}/ klasöründen yüklenebilir (.roorules-{{slug}} ve .clinerules-{{slug}} kullanımdan kaldırılmıştır ve yakında çalışmayı durduracaklardır)." + "loadFromFile": "{{mode}} moduna özgü özel talimatlar ayrıca çalışma alanınızdaki .roo/rules-{{slug}}/ klasöründen yüklenebilir (.roorules-{{slug}} ve .clinerules-{{slug}} kullanımdan kaldırılmıştır ve yakında çalışmayı durduracaklardır)." }, "globalCustomInstructions": { "title": "Tüm Modlar için Özel Talimatlar", - "description": "Bu talimatlar tüm modlara uygulanır. Aşağıdaki moda özgü talimatlarla geliştirilebilen temel davranış seti sağlarlar.\nRoo'nun editörünüzün görüntüleme dilinden ({{language}}) farklı bir dilde düşünmesini ve konuşmasını istiyorsanız, burada belirtebilirsiniz.", - "loadFromFile": "Talimatlar ayrıca çalışma alanınızdaki .pearai-agent/rules/ klasöründen de yüklenebilir (.roorules ve .clinerules kullanımdan kaldırılmıştır ve yakında çalışmayı durduracaklardır)." + "description": "Bu talimatlar tüm modlara uygulanır. Aşağıdaki moda özgü talimatlarla geliştirilebilen temel davranış seti sağlarlar. <0>Daha fazla bilgi edinin", + "loadFromFile": "Talimatlar ayrıca çalışma alanınızdaki .roo/rules/ klasöründen de yüklenebilir (.roorules ve .clinerules kullanımdan kaldırılmıştır ve yakında çalışmayı durduracaklardır)." }, "systemPrompt": { "preview": "Sistem promptunu önizle", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Gelişmiş: Sistem Promptunu Geçersiz Kıl", - "description": "Çalışma alanınızda .pearai-agent/system-prompt-{{slug}} adresinde bir dosya oluşturarak bu mod için sistem istemini tamamen değiştirebilirsiniz (rol tanımı ve özel talimatlar hariç). Bu, yerleşik güvenlik önlemlerini ve tutarlılık kontrollerini (özellikle araç kullanımıyla ilgili) aşan çok gelişmiş bir özelliktir, bu yüzden dikkatli olun!" + "description": "<2>⚠️ Uyarı: Bu gelişmiş özellik güvenlik önlemlerini atlar. <1>KULLANMADAN ÖNCE BUNU OKUYUN!Çalışma alanınızda .roo/system-prompt-{{slug}} adresinde bir dosya oluşturarak varsayılan sistem istemini geçersiz kılın." }, "createModeDialog": { "title": "Yeni Mod Oluştur", @@ -122,7 +128,7 @@ "description": "Tüm çalışma alanlarında kullanılabilir" }, "project": { - "label": "Projeye özgü (.pearai-agent-ignore)", + "label": "Projeye özgü (.roomodes)", "description": "Yalnızca bu çalışma alanında kullanılabilir, globale göre önceliklidir" } }, @@ -130,6 +136,10 @@ "label": "Rol Tanımı", "description": "Bu mod için Roo'nun uzmanlığını ve kişiliğini tanımlayın." }, + "whenToUse": { + "label": "Ne zaman kullanılmalı (isteğe bağlı)", + "description": "Bu modun ne zaman en etkili olduğu ve hangi tür görevlerde üstün olduğu hakkında net bir açıklama sağlayın." + }, "tools": { "label": "Kullanılabilir Araçlar", "description": "Bu modun hangi araçları kullanabileceğini seçin." diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 9ed3f29db08..f6da922fb83 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Sağlayıcılar", - "autoApprove": "Otomatik Onay", - "browser": "Tarayıcı / Bilgisayar Kullanımı", + "autoApprove": "Oto-Onay", + "browser": "Bilgisayar Erişimi", "checkpoints": "Kontrol Noktaları", "notifications": "Bildirimler", - "contextManagement": "Bağlam Yönetimi", + "contextManagement": "Bağlam", "terminal": "Terminal", - "advanced": "Gelişmiş", - "experimental": "Deneysel Özellikler", + "experimental": "Deneysel", "language": "Dil", - "about": "Agent Hakkında", - "interface": "Arayüz" + "about": "Roo Code Hakkında" }, "autoApprove": { "description": "Roo'nun onay gerektirmeden otomatik olarak işlemler gerçekleştirmesine izin verin. Bu ayarları yalnızca yapay zekaya tamamen güveniyorsanız ve ilgili güvenlik risklerini anlıyorsanız etkinleştirin.", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "Yapılandırma Profili", "providerDocumentation": "{{provider}} Dokümantasyonu", + "configProfile": "Yapılandırma Profili", "description": "Sağlayıcılar ve ayarlar arasında hızlıca geçiş yapmak için farklı API yapılandırmalarını kaydedin.", "apiProvider": "API Sağlayıcı", "model": "Model", @@ -118,14 +116,22 @@ "headerValue": "Başlık değeri", "noCustomHeaders": "Tanımlanmış özel başlık yok. Eklemek için + düğmesine tıklayın.", "requestyApiKey": "Requesty API Anahtarı", + "refreshModels": { + "label": "Modelleri Yenile", + "hint": "En son modelleri görmek için lütfen ayarları yeniden açın." + }, "getRequestyApiKey": "Requesty API Anahtarı Al", "openRouterTransformsText": "İstem ve mesaj zincirlerini bağlam boyutuna sıkıştır (OpenRouter Dönüşümleri)", "anthropicApiKey": "Anthropic API Anahtarı", "getAnthropicApiKey": "Anthropic API Anahtarı Al", "anthropicUseAuthToken": "Anthropic API Anahtarını X-Api-Key yerine Authorization başlığı olarak geçir", + "chutesApiKey": "Chutes API Anahtarı", + "getChutesApiKey": "Chutes API Anahtarı Al", "deepSeekApiKey": "DeepSeek API Anahtarı", "getDeepSeekApiKey": "DeepSeek API Anahtarı Al", "geminiApiKey": "Gemini API Anahtarı", + "getGroqApiKey": "Groq API Anahtarı Al", + "groqApiKey": "Groq API Anahtarı", "getGeminiApiKey": "Gemini API Anahtarı Al", "openAiApiKey": "OpenAI API Anahtarı", "openAiBaseUrl": "Temel URL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "Codestral modeli için alternatif URL ayarlayın.", "xaiApiKey": "xAI API Anahtarı", "getXaiApiKey": "xAI API Anahtarı Al", + "litellmApiKey": "LiteLLM API Anahtarı", + "litellmBaseUrl": "LiteLLM Temel URL", "awsCredentials": "AWS Kimlik Bilgileri", "awsProfile": "AWS Profili", "awsProfileName": "AWS Profil Adı", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Tarayıcı aracını etkinleştir", - "description": "Etkinleştirildiğinde, Roo bilgisayar kullanımını destekleyen modeller kullanırken web siteleriyle etkileşim kurmak için bir tarayıcı kullanabilir." + "description": "Etkinleştirildiğinde, Roo bilgisayar kullanımını destekleyen modeller kullanırken web siteleriyle etkileşim kurmak için bir tarayıcı kullanabilir. <0>Daha fazla bilgi" }, "viewport": { "label": "Görünüm alanı boyutu", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Otomatik kontrol noktalarını etkinleştir", - "description": "Etkinleştirildiğinde, Roo görev yürütme sırasında otomatik olarak kontrol noktaları oluşturarak değişiklikleri gözden geçirmeyi veya önceki durumlara dönmeyi kolaylaştırır." + "description": "Etkinleştirildiğinde, Roo görev yürütme sırasında otomatik olarak kontrol noktaları oluşturarak değişiklikleri gözden geçirmeyi veya önceki durumlara dönmeyi kolaylaştırır. <0>Daha fazla bilgi" } }, "notifications": { @@ -298,9 +306,9 @@ "label": "Çalışma alanı dosyaları bağlam sınırı", "description": "Mevcut çalışma dizini ayrıntılarına dahil edilecek maksimum dosya sayısı. Daha yüksek değerler daha fazla bağlam sağlar ancak token kullanımını artırır." }, - "pearai-agent-ignore": { - "label": "Listelerde ve aramalarda .pearai-agent-ignore dosyalarını göster", - "description": "Etkinleştirildiğinde, .pearai-agent-ignore'daki desenlerle eşleşen dosyalar kilit sembolü ile listelerde gösterilecektir. Devre dışı bırakıldığında, bu dosyalar dosya listelerinden ve aramalardan tamamen gizlenecektir." + "rooignore": { + "label": "Listelerde ve aramalarda .rooignore dosyalarını göster", + "description": "Etkinleştirildiğinde, .rooignore'daki desenlerle eşleşen dosyalar kilit sembolü ile listelerde gösterilecektir. Devre dışı bırakıldığında, bu dosyalar dosya listelerinden ve aramalardan tamamen gizlenecektir." }, "maxReadFile": { "label": "Dosya okuma otomatik kısaltma eşiği", @@ -310,45 +318,57 @@ } }, "terminal": { + "basic": { + "label": "Terminal Ayarları: Temel", + "description": "Temel terminal ayarları" + }, + "advanced": { + "label": "Terminal Ayarları: Gelişmiş", + "description": "Aşağıdaki seçeneklerin uygulanması için terminalin yeniden başlatılması gerekebilir." + }, "outputLineLimit": { "label": "Terminal çıktısı sınırı", - "description": "Komutları yürütürken terminal çıktısına dahil edilecek maksimum satır sayısı. Aşıldığında, token tasarrufu sağlayarak satırlar ortadan kaldırılacaktır." + "description": "Komutları yürütürken terminal çıktısına dahil edilecek maksimum satır sayısı. Aşıldığında, token tasarrufu sağlayarak satırlar ortadan kaldırılacaktır. <0>Daha fazla bilgi" }, "shellIntegrationTimeout": { "label": "Terminal kabuk entegrasyonu zaman aşımı", - "description": "Komutları yürütmeden önce kabuk entegrasyonunun başlatılması için beklenecek maksimum süre. Kabuk başlatma süresi uzun olan kullanıcılar için, terminalde \"Shell Integration Unavailable\" hatalarını görürseniz bu değerin artırılması gerekebilir." + "description": "Komutları yürütmeden önce kabuk entegrasyonunun başlatılması için beklenecek maksimum süre. Kabuk başlatma süresi uzun olan kullanıcılar için, terminalde \"Shell Integration Unavailable\" hatalarını görürseniz bu değerin artırılması gerekebilir. <0>Daha fazla bilgi" }, "shellIntegrationDisabled": { "label": "Terminal kabuk entegrasyonunu devre dışı bırak", - "description": "Terminal komutları düzgün çalışmıyorsa veya 'Shell Integration Unavailable' hataları görüyorsanız bunu etkinleştirin. Bu, bazı gelişmiş terminal özelliklerini atlayarak komutları çalıştırmak için daha basit bir yöntem kullanır." - }, - "compressProgressBar": { - "label": "İlerleme çubuğu çıktısını sıkıştır", - "description": "Etkinleştirildiğinde, satır başı karakteri (\\r) içeren terminal çıktısını işleyerek gerçek bir terminalin içeriği nasıl göstereceğini simüle eder. Bu, ilerleme çubuğunun ara durumlarını kaldırır, yalnızca son durumu korur ve daha alakalı bilgiler için bağlam alanından tasarruf sağlar." - }, - "zdotdir": { - "label": "ZDOTDIR işlemeyi etkinleştir", - "description": "Etkinleştirildiğinde, zsh kabuğu entegrasyonunu düzgün şekilde işlemek için ZDOTDIR için geçici bir dizin oluşturur. Bu, zsh yapılandırmanızı korurken VSCode kabuk entegrasyonunun zsh ile düzgün çalışmasını sağlar. (deneysel)" + "description": "Terminal komutları düzgün çalışmıyorsa veya 'Shell Integration Unavailable' hataları görüyorsanız bunu etkinleştirin. Bu, bazı gelişmiş terminal özelliklerini atlayarak komutları çalıştırmak için daha basit bir yöntem kullanır. <0>Daha fazla bilgi" }, "commandDelay": { "label": "Terminal komut gecikmesi", - "description": "Komut yürütmesinden sonra eklenecek gecikme süresi (milisaniye). 0 varsayılan ayarı gecikmeyi tamamen devre dışı bırakır. Bu, zamanlama sorunları olan terminallerde komut çıktısının tam olarak yakalanmasını sağlamaya yardımcı olabilir. Çoğu terminalde bu, `PROMPT_COMMAND='sleep N'` ayarlanarak uygulanır ve PowerShell her komutun sonuna `start-sleep` ekler. Başlangıçta VSCode hata#237208 için bir geçici çözümdü ve gerekli olmayabilir." + "description": "Komut yürütmesinden sonra eklenecek gecikme süresi (milisaniye). 0 varsayılan ayarı gecikmeyi tamamen devre dışı bırakır. Bu, zamanlama sorunları olan terminallerde komut çıktısının tam olarak yakalanmasını sağlamaya yardımcı olabilir. Çoğu terminalde bu, `PROMPT_COMMAND='sleep N'` ayarlanarak uygulanır ve PowerShell her komutun sonuna `start-sleep` ekler. Başlangıçta VSCode hata#237208 için bir geçici çözümdü ve gerekli olmayabilir. <0>Daha fazla bilgi" + }, + "compressProgressBar": { + "label": "İlerleme çubuğu çıktısını sıkıştır", + "description": "Etkinleştirildiğinde, satır başı karakteri (\\r) içeren terminal çıktısını işleyerek gerçek bir terminalin içeriği nasıl göstereceğini simüle eder. Bu, ilerleme çubuğunun ara durumlarını kaldırır, yalnızca son durumu korur ve daha alakalı bilgiler için bağlam alanından tasarruf sağlar. <0>Daha fazla bilgi" }, "powershellCounter": { "label": "PowerShell sayaç geçici çözümünü etkinleştir", - "description": "Etkinleştirildiğinde, komutların doğru şekilde yürütülmesini sağlamak için PowerShell komutlarına bir sayaç ekler. Bu, çıktı yakalama sorunları yaşayabilecek PowerShell terminallerinde yardımcı olur." + "description": "Etkinleştirildiğinde, komutların doğru şekilde yürütülmesini sağlamak için PowerShell komutlarına bir sayaç ekler. Bu, çıktı yakalama sorunları yaşayabilecek PowerShell terminallerinde yardımcı olur. <0>Daha fazla bilgi" }, "zshClearEolMark": { "label": "ZSH satır sonu işaretini temizle", - "description": "Etkinleştirildiğinde, PROMPT_EOL_MARK='' ayarlanarak ZSH satır sonu işaretini temizler. Bu, '%' gibi özel karakterlerle biten komut çıktılarının yorumlanmasında sorun yaşanmasını önler." + "description": "Etkinleştirildiğinde, PROMPT_EOL_MARK='' ayarlanarak ZSH satır sonu işaretini temizler. Bu, '%' gibi özel karakterlerle biten komut çıktılarının yorumlanmasında sorun yaşanmasını önler. <0>Daha fazla bilgi" }, "zshOhMy": { "label": "Oh My Zsh entegrasyonunu etkinleştir", - "description": "Etkinleştirildiğinde, Oh My Zsh kabuk entegrasyon özelliklerini etkinleştirmek için ITERM_SHELL_INTEGRATION_INSTALLED=Yes ayarlar. Bu ayarın uygulanması IDE'nin yeniden başlatılmasını gerektirebilir. (deneysel)" + "description": "Etkinleştirildiğinde, Oh My Zsh kabuk entegrasyon özelliklerini etkinleştirmek için ITERM_SHELL_INTEGRATION_INSTALLED=Yes ayarlar. Bu ayarın uygulanması IDE'nin yeniden başlatılmasını gerektirebilir. <0>Daha fazla bilgi" }, "zshP10k": { "label": "Powerlevel10k entegrasyonunu etkinleştir", - "description": "Etkinleştirildiğinde, Powerlevel10k kabuk entegrasyon özelliklerini etkinleştirmek için POWERLEVEL9K_TERM_SHELL_INTEGRATION=true ayarlar. (deneysel)" + "description": "Etkinleştirildiğinde, Powerlevel10k kabuk entegrasyon özelliklerini etkinleştirmek için POWERLEVEL9K_TERM_SHELL_INTEGRATION=true ayarlar. <0>Daha fazla bilgi" + }, + "zdotdir": { + "label": "ZDOTDIR işlemeyi etkinleştir", + "description": "Etkinleştirildiğinde, zsh kabuğu entegrasyonunu düzgün şekilde işlemek için ZDOTDIR için geçici bir dizin oluşturur. Bu, zsh yapılandırmanızı korurken VSCode kabuk entegrasyonunun zsh ile düzgün çalışmasını sağlar. <0>Daha fazla bilgi" + }, + "inheritEnv": { + "label": "Ortam değişkenlerini devral", + "description": "Etkinleştirildiğinde, terminal VSCode üst işleminden ortam değişkenlerini devralır, örneğin kullanıcı profilinde tanımlanan kabuk entegrasyon ayarları gibi. Bu, VSCode'un global ayarı olan `terminal.integrated.inheritEnv` değerini doğrudan değiştirir. <0>Daha fazla bilgi" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Bağlam penceresini akıllıca sıkıştır", + "description": "Görevin bağlam penceresi neredeyse dolduğunda, eski mesajları atmak yerine önceki konuşmayı özetlemek için bir LLM çağrısı kullanır. Not: Özetleme maliyeti şu anda arayüzde gösterilen API maliyetlerine dahil değildir." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Deneysel birleştirilmiş diff stratejisini kullan", "description": "Deneysel birleştirilmiş diff stratejisini etkinleştir. Bu strateji, model hatalarından kaynaklanan yeniden deneme sayısını azaltabilir, ancak beklenmeyen davranışlara veya hatalı düzenlemelere neden olabilir. Yalnızca riskleri anlıyorsanız ve tüm değişiklikleri dikkatlice incelemeye istekliyseniz etkinleştirin." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Prompt önbelleğini etkinleştir", - "description": "Etkinleştirildiğinde, Roo maliyetleri azaltmak için prompt önbelleği açık olan bu modeli kullanacaktır." + "label": "Prompt önbelleğini devre dışı bırak", + "description": "İşaretlendiğinde, Roo bu model için prompt önbelleğini kullanmayacaktır." }, "temperature": { "useCustom": "Özel sıcaklık kullan", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Herhangi bir sorunuz veya geri bildiriminiz varsa, github.com/RooVetGit/Roo-Code adresinde bir konu açmaktan veya reddit.com/r/RooCode ya da discord.gg/roocode'a katılmaktan çekinmeyin", - "version": "Agent v{{version}}", "telemetry": { "label": "Anonim hata ve kullanım raporlamaya izin ver", "description": "Anonim kullanım verileri ve hata raporları göndererek Roo Code'u geliştirmeye yardımcı olun. Hiçbir kod, istem veya kişisel bilgi asla gönderilmez. Daha fazla ayrıntı için gizlilik politikamıza bakın." diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index 4e505ee0b15..388fc284a6a 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -109,7 +109,7 @@ "diffError": { "title": "Chỉnh sửa không thành công" }, - "troubleMessage": "Agent đang gặp sự cố...", + "troubleMessage": "Roo đang gặp sự cố...", "apiRequest": { "title": "Yêu cầu API", "failed": "Yêu cầu API thất bại", @@ -135,47 +135,47 @@ "current": "Hiện tại" }, "instructions": { - "wantsToFetch": "Agent muốn lấy hướng dẫn chi tiết để hỗ trợ nhiệm vụ hiện tại" + "wantsToFetch": "Roo muốn lấy hướng dẫn chi tiết để hỗ trợ nhiệm vụ hiện tại" }, "fileOperations": { - "wantsToRead": "Agent muốn đọc tệp này:", - "wantsToReadOutsideWorkspace": "Agent muốn đọc tệp này bên ngoài không gian làm việc:", - "didRead": "Agent đã đọc tệp này:", - "wantsToEdit": "Agent muốn chỉnh sửa tệp này:", - "wantsToEditOutsideWorkspace": "Agent muốn chỉnh sửa tệp này bên ngoài không gian làm việc:", - "wantsToCreate": "Agent muốn tạo một tệp mới:", - "wantsToSearchReplace": "Agent muốn thực hiện tìm kiếm và thay thế trong tệp này:", - "didSearchReplace": "Agent đã thực hiện tìm kiếm và thay thế trong tệp này:", - "wantsToInsert": "Agent muốn chèn nội dung vào tệp này:", - "wantsToInsertWithLineNumber": "Agent muốn chèn nội dung vào dòng {{lineNumber}} của tệp này:", - "wantsToInsertAtEnd": "Agent muốn thêm nội dung vào cuối tệp này:" + "wantsToRead": "Roo muốn đọc tệp này:", + "wantsToReadOutsideWorkspace": "Roo muốn đọc tệp này bên ngoài không gian làm việc:", + "didRead": "Roo đã đọc tệp này:", + "wantsToEdit": "Roo muốn chỉnh sửa tệp này:", + "wantsToEditOutsideWorkspace": "Roo muốn chỉnh sửa tệp này bên ngoài không gian làm việc:", + "wantsToCreate": "Roo muốn tạo một tệp mới:", + "wantsToSearchReplace": "Roo muốn thực hiện tìm kiếm và thay thế trong tệp này:", + "didSearchReplace": "Roo đã thực hiện tìm kiếm và thay thế trong tệp này:", + "wantsToInsert": "Roo muốn chèn nội dung vào tệp này:", + "wantsToInsertWithLineNumber": "Roo muốn chèn nội dung vào dòng {{lineNumber}} của tệp này:", + "wantsToInsertAtEnd": "Roo muốn thêm nội dung vào cuối tệp này:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent muốn xem các tệp cấp cao nhất trong thư mục này:", - "didViewTopLevel": "Agent đã xem các tệp cấp cao nhất trong thư mục này:", - "wantsToViewRecursive": "Agent muốn xem đệ quy tất cả các tệp trong thư mục này:", - "didViewRecursive": "Agent đã xem đệ quy tất cả các tệp trong thư mục này:", - "wantsToViewDefinitions": "Agent muốn xem tên định nghĩa mã nguồn được sử dụng trong thư mục này:", - "didViewDefinitions": "Agent đã xem tên định nghĩa mã nguồn được sử dụng trong thư mục này:", - "wantsToSearch": "Agent muốn tìm kiếm trong thư mục này cho {{regex}}:", - "didSearch": "Agent đã tìm kiếm trong thư mục này cho {{regex}}:" + "wantsToViewTopLevel": "Roo muốn xem các tệp cấp cao nhất trong thư mục này:", + "didViewTopLevel": "Roo đã xem các tệp cấp cao nhất trong thư mục này:", + "wantsToViewRecursive": "Roo muốn xem đệ quy tất cả các tệp trong thư mục này:", + "didViewRecursive": "Roo đã xem đệ quy tất cả các tệp trong thư mục này:", + "wantsToViewDefinitions": "Roo muốn xem tên định nghĩa mã nguồn được sử dụng trong thư mục này:", + "didViewDefinitions": "Roo đã xem tên định nghĩa mã nguồn được sử dụng trong thư mục này:", + "wantsToSearch": "Roo muốn tìm kiếm trong thư mục này cho {{regex}}:", + "didSearch": "Roo đã tìm kiếm trong thư mục này cho {{regex}}:" }, "commandOutput": "Kết quả lệnh", "response": "Phản hồi", "arguments": "Tham số", "mcp": { - "wantsToUseTool": "Agent muốn sử dụng một công cụ trên máy chủ MCP {{serverName}}:", - "wantsToAccessResource": "Agent muốn truy cập một tài nguyên trên máy chủ MCP {{serverName}}:" + "wantsToUseTool": "Roo muốn sử dụng một công cụ trên máy chủ MCP {{serverName}}:", + "wantsToAccessResource": "Roo muốn truy cập một tài nguyên trên máy chủ MCP {{serverName}}:" }, "modes": { - "wantsToSwitch": "Agent muốn chuyển sang chế độ {{mode}}", - "wantsToSwitchWithReason": "Agent muốn chuyển sang chế độ {{mode}} vì: {{reason}}", - "didSwitch": "Agent đã chuyển sang chế độ {{mode}}", - "didSwitchWithReason": "Agent đã chuyển sang chế độ {{mode}} vì: {{reason}}" + "wantsToSwitch": "Roo muốn chuyển sang chế độ {{mode}}", + "wantsToSwitchWithReason": "Roo muốn chuyển sang chế độ {{mode}} vì: {{reason}}", + "didSwitch": "Roo đã chuyển sang chế độ {{mode}}", + "didSwitchWithReason": "Roo đã chuyển sang chế độ {{mode}} vì: {{reason}}" }, "subtasks": { - "wantsToCreate": "Agent muốn tạo một nhiệm vụ phụ mới trong chế độ {{mode}}:", - "wantsToFinish": "Agent muốn hoàn thành nhiệm vụ phụ này", + "wantsToCreate": "Roo muốn tạo một nhiệm vụ phụ mới trong chế độ {{mode}}:", + "wantsToFinish": "Roo muốn hoàn thành nhiệm vụ phụ này", "newTaskContent": "Hướng dẫn nhiệm vụ phụ", "completionContent": "Nhiệm vụ phụ đã hoàn thành", "resultContent": "Kết quả nhiệm vụ phụ", @@ -183,7 +183,7 @@ "completionInstructions": "Nhiệm vụ phụ đã hoàn thành! Bạn có thể xem lại kết quả và đề xuất các sửa đổi hoặc bước tiếp theo. Nếu mọi thứ có vẻ tốt, hãy xác nhận để trả kết quả về nhiệm vụ chính." }, "questions": { - "hasQuestion": "Agent có một câu hỏi:" + "hasQuestion": "Roo có một câu hỏi:" }, "taskCompleted": "Nhiệm vụ hoàn thành", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "Sao chép vào ô nhập liệu (hoặc Shift + nhấp chuột)" }, "announcement": { - "title": "🎉 Roo Code 3.15 Đã phát hành", - "description": "Agent Code 3.15 mang đến các tính năng và cải tiến mới dựa trên phản hồi của bạn.", + "title": "🎉 Roo Code 3.17 Đã phát hành", + "description": "Roo Code 3.17 mang đến các tính năng mạnh mẽ và cải tiến mới dựa trên phản hồi của bạn.", "whatsNew": "Có gì mới", - "feature1": "Bộ nhớ đệm lời nhắc cho Vertex: Đã thêm hỗ trợ bộ nhớ đệm lời nhắc cho Vertex AI, cải thiện thời gian phản hồi và giảm chi phí API", - "feature2": "Cơ chế dự phòng cho Terminal: Đã triển khai cơ chế dự phòng khi tích hợp shell terminal VSCode gặp sự cố", - "feature3": "Cải thiện đoạn mã: Nâng cao hiển thị và tương tác với đoạn mã trong giao diện trò chuyện", + "feature1": "Bộ nhớ đệm ngầm cho Gemini: Các lệnh gọi API Gemini hiện được tự động lưu vào bộ nhớ đệm, giảm chi phí API", + "feature2": "Lựa chọn chế độ thông minh hơn: Định nghĩa chế độ giờ đây có thể bao gồm hướng dẫn về thời điểm nên sử dụng mỗi chế độ, cho phép điều phối tốt hơn", + "feature3": "Ngắn gọn hóa bối cảnh thông minh: Tóm tắt thông minh lịch sử cuộc trò chuyện khi bối cảnh đầy thay vì cắt bỏ (bật trong Cài đặt -> Thử nghiệm)", "hideButton": "Ẩn thông báo", "detailsDiscussLinks": "Nhận thêm chi tiết và thảo luận tại DiscordReddit 🚀" }, "browser": { - "rooWantsToUse": "Agent muốn sử dụng trình duyệt:", + "rooWantsToUse": "Roo muốn sử dụng trình duyệt:", "consoleLogs": "Nhật ký bảng điều khiển", "noNewLogs": "(Không có nhật ký mới)", "screenshot": "Ảnh chụp màn hình trình duyệt", @@ -233,6 +233,15 @@ "close": "Đóng trình duyệt" } }, + "codeblock": { + "tooltips": { + "expand": "Mở rộng khối mã", + "collapse": "Thu gọn khối mã", + "enable_wrap": "Bật tự động xuống dòng", + "disable_wrap": "Tắt tự động xuống dòng", + "copy_code": "Sao chép mã" + } + }, "systemPromptWarning": "CẢNH BÁO: Đã kích hoạt ghi đè lệnh nhắc hệ thống tùy chỉnh. Điều này có thể phá vỡ nghiêm trọng chức năng và gây ra hành vi không thể dự đoán.", "shellIntegration": { "title": "Cảnh báo thực thi lệnh", diff --git a/webview-ui/src/i18n/locales/vi/mcp.json b/webview-ui/src/i18n/locales/vi/mcp.json index 1252094da4e..c0496a34fa9 100644 --- a/webview-ui/src/i18n/locales/vi/mcp.json +++ b/webview-ui/src/i18n/locales/vi/mcp.json @@ -1,18 +1,19 @@ { "title": "Máy chủ MCP", - "done": "Hoàn thành", - "description": "<0>Giao thức ngữ cảnh mô hình cho phép giao tiếp với các máy chủ MCP đang chạy cục bộ, cung cấp các công cụ và tài nguyên bổ sung để mở rộng khả năng của Roo. Bạn có thể sử dụng <1>các máy chủ do cộng đồng tạo hoặc yêu cầu Roo tạo các công cụ mới dành riêng cho quy trình làm việc của bạn (ví dụ: \"thêm công cụ lấy tài liệu npm mới nhất\").", + "done": "Xong", + "description": "Bật Model Context Protocol (MCP) để Roo Code có thể dùng thêm công cụ và dịch vụ từ máy chủ bên ngoài. Điều này mở rộng khả năng của Roo cho bạn. <0>Tìm hiểu thêm", "enableToggle": { "title": "Bật máy chủ MCP", - "description": "Khi được bật, Roo sẽ có thể tương tác với các máy chủ MCP cho chức năng nâng cao. Nếu bạn không sử dụng MCP, bạn có thể tắt tính năng này để giảm lượng token mà Roo sử dụng." + "description": "Bật lên để Roo dùng công cụ từ các máy chủ MCP đã kết nối. Roo sẽ có nhiều khả năng hơn. Nếu không dùng các công cụ này, hãy tắt để tiết kiệm chi phí token API." }, "enableServerCreation": { "title": "Bật tạo máy chủ MCP", - "description": "Khi được bật, Roo có thể giúp bạn tạo máy chủ MCP mới thông qua các lệnh như \"thêm công cụ mới để...\". Nếu bạn không cần tạo máy chủ MCP, bạn có thể tắt tính năng này để giảm lượng token mà Roo sử dụng." + "description": "Bật lên để Roo giúp bạn tạo <1>máy chủ MCP mới tuỳ chỉnh. <0>Tìm hiểu về tạo máy chủ", + "hint": "Mẹo: Để giảm chi phí token API, hãy tắt khi không cần Roo tạo máy chủ MCP mới." }, "editGlobalMCP": "Chỉnh sửa MCP toàn cục", "editProjectMCP": "Chỉnh sửa MCP dự án", - "editSettings": "Chỉnh sửa cài đặt MCP", + "learnMoreEditingSettings": "Tìm hiểu thêm về chỉnh sửa file cài đặt MCP", "tool": { "alwaysAllow": "Luôn cho phép", "parameters": "Tham số", @@ -20,15 +21,18 @@ }, "tabs": { "tools": "Công cụ", - "resources": "Tài nguyên" + "resources": "Tài nguyên", + "errors": "Lỗi" }, "emptyState": { - "noTools": "Không tìm thấy công cụ nào", - "noResources": "Không tìm thấy tài nguyên nào" + "noTools": "Không tìm thấy công cụ", + "noResources": "Không tìm thấy tài nguyên", + "noLogs": "Không tìm thấy nhật ký", + "noErrors": "Không tìm thấy lỗi" }, "networkTimeout": { "label": "Thời gian chờ mạng", - "description": "Thời gian tối đa để chờ phản hồi từ máy chủ", + "description": "Thời gian tối đa chờ phản hồi từ máy chủ", "options": { "15seconds": "15 giây", "30seconds": "30 giây", @@ -41,10 +45,10 @@ } }, "deleteDialog": { - "title": "Xóa máy chủ MCP", - "description": "Bạn có chắc chắn muốn xóa máy chủ MCP \"{{serverName}}\"? Hành động này không thể hoàn tác.", - "cancel": "Hủy", - "delete": "Xóa" + "title": "Xoá máy chủ MCP", + "description": "Bạn chắc chắn muốn xoá máy chủ MCP \"{{serverName}}\"? Hành động này không thể hoàn tác.", + "cancel": "Huỷ", + "delete": "Xoá" }, "serverStatus": { "retrying": "Đang thử lại...", diff --git a/webview-ui/src/i18n/locales/vi/prompts.json b/webview-ui/src/i18n/locales/vi/prompts.json index 5583a8757e0..e3bd643a935 100644 --- a/webview-ui/src/i18n/locales/vi/prompts.json +++ b/webview-ui/src/i18n/locales/vi/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "Tạo chế độ mới", "editModesConfig": "Chỉnh sửa cấu hình chế độ", "editGlobalModes": "Chỉnh sửa chế độ toàn cục", - "editProjectModes": "Chỉnh sửa chế độ dự án (.pearai-agent-ignore)", - "createModeHelpText": "Nhấn + để tạo chế độ tùy chỉnh mới, hoặc chỉ cần yêu cầu Roo trong chat tạo một chế độ cho bạn!" + "editProjectModes": "Chỉnh sửa chế độ dự án (.roomodes)", + "createModeHelpText": "Chế độ là các vai trò chuyên biệt điều chỉnh hành vi của Roo. <0>Tìm hiểu về Sử dụng Chế độ hoặc <1>Tùy chỉnh Chế độ.", + "selectMode": "Tìm kiếm chế độ" }, "apiConfiguration": { "title": "Cấu hình API", @@ -33,16 +34,21 @@ "resetToDefault": "Đặt lại về mặc định", "description": "Xác định chuyên môn và tính cách của Roo cho chế độ này. Mô tả này định hình cách Roo giới thiệu bản thân và tiếp cận nhiệm vụ." }, + "whenToUse": { + "title": "Khi nào nên sử dụng (tùy chọn)", + "description": "Mô tả khi nào nên sử dụng chế độ này. Điều này giúp Orchestrator chọn chế độ phù hợp cho một nhiệm vụ.", + "resetToDefault": "Đặt lại mô tả 'Khi nào nên sử dụng' về mặc định" + }, "customInstructions": { "title": "Hướng dẫn tùy chỉnh dành riêng cho chế độ (tùy chọn)", "resetToDefault": "Đặt lại về mặc định", "description": "Thêm hướng dẫn hành vi dành riêng cho chế độ {{modeName}}.", - "loadFromFile": "Hướng dẫn tùy chỉnh dành riêng cho chế độ {{modeName}} cũng có thể được tải từ thư mục .pearai-agent/rules-{{modeSlug}}/ trong không gian làm việc của bạn (.roorules-{{modeSlug}} và .clinerules-{{modeSlug}} đã lỗi thời và sẽ sớm ngừng hoạt động)." + "loadFromFile": "Hướng dẫn tùy chỉnh dành riêng cho chế độ {{mode}} cũng có thể được tải từ thư mục .roo/rules-{{slug}}/ trong không gian làm việc của bạn (.roorules-{{slug}} và .clinerules-{{slug}} đã lỗi thời và sẽ sớm ngừng hoạt động)." }, "globalCustomInstructions": { "title": "Hướng dẫn tùy chỉnh cho tất cả các chế độ", - "description": "Những hướng dẫn này áp dụng cho tất cả các chế độ. Chúng cung cấp một bộ hành vi cơ bản có thể được nâng cao bởi hướng dẫn dành riêng cho chế độ bên dưới.\nNếu bạn muốn Roo suy nghĩ và nói bằng ngôn ngữ khác với ngôn ngữ hiển thị trình soạn thảo của bạn ({{language}}), bạn có thể chỉ định ở đây.", - "loadFromFile": "Hướng dẫn cũng có thể được tải từ thư mục .pearai-agent/rules/ trong không gian làm việc của bạn (.roorules và .clinerules đã lỗi thời và sẽ sớm ngừng hoạt động)." + "description": "Những hướng dẫn này áp dụng cho tất cả các chế độ. Chúng cung cấp một bộ hành vi cơ bản có thể được nâng cao bởi hướng dẫn dành riêng cho chế độ bên dưới. <0>Tìm hiểu thêm", + "loadFromFile": "Hướng dẫn cũng có thể được tải từ thư mục .roo/rules/ trong không gian làm việc của bạn (.roorules và .clinerules đã lỗi thời và sẽ sớm ngừng hoạt động)." }, "systemPrompt": { "preview": "Xem trước lời nhắc hệ thống", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "Nâng cao: Ghi đè lời nhắc hệ thống", - "description": "Bạn có thể hoàn toàn thay thế lời nhắc hệ thống cho chế độ này (ngoài định nghĩa vai trò và hướng dẫn tùy chỉnh) bằng cách tạo một tệp tại .pearai-agent/system-prompt-{{modeSlug}} trong không gian làm việc của bạn. Đây là một tính năng rất nâng cao bỏ qua các biện pháp bảo vệ và kiểm tra nhất quán tích hợp sẵn (đặc biệt là xung quanh việc sử dụng công cụ), vì vậy hãy cẩn thận!" + "description": "<2>⚠️ Cảnh báo: Tính năng nâng cao này bỏ qua các biện pháp bảo vệ. <1>ĐỌC KỸ TRƯỚC KHI SỬ DỤNG!Ghi đè lời nhắc hệ thống mặc định bằng cách tạo một tệp tại .roo/system-prompt-{{slug}}." }, "createModeDialog": { "title": "Tạo chế độ mới", @@ -122,7 +128,7 @@ "description": "Có sẵn trong tất cả các không gian làm việc" }, "project": { - "label": "Dành riêng cho dự án (.pearai-agent-ignore)", + "label": "Dành riêng cho dự án (.roomodes)", "description": "Chỉ có sẵn trong không gian làm việc này, được ưu tiên hơn toàn cục" } }, @@ -130,6 +136,10 @@ "label": "Định nghĩa vai trò", "description": "Xác định chuyên môn và tính cách của Roo cho chế độ này." }, + "whenToUse": { + "label": "Khi nào nên sử dụng (tùy chọn)", + "description": "Cung cấp mô tả rõ ràng về thời điểm chế độ này hiệu quả nhất và những loại nhiệm vụ nào nó thực hiện tốt nhất." + }, "tools": { "label": "Công cụ có sẵn", "description": "Chọn công cụ nào chế độ này có thể sử dụng." diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index e536476f6c7..f448940b71f 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -22,17 +22,15 @@ }, "sections": { "providers": "Nhà cung cấp", - "autoApprove": "Tự động phê duyệt", - "browser": "Trình duyệt / Sử dụng máy tính", + "autoApprove": "Phê duyệt", + "browser": "Trình duyệt", "checkpoints": "Điểm kiểm tra", "notifications": "Thông báo", - "contextManagement": "Quản lý ngữ cảnh", + "contextManagement": "Ngữ cảnh", "terminal": "Terminal", - "advanced": "Nâng cao", - "experimental": "Tính năng thử nghiệm", + "experimental": "Thử nghiệm", "language": "Ngôn ngữ", - "about": "Về Roo Code", - "interface": "Giao diện" + "about": "Giới thiệu" }, "autoApprove": { "description": "Cho phép Roo tự động thực hiện các hoạt động mà không cần phê duyệt. Chỉ bật những cài đặt này nếu bạn hoàn toàn tin tưởng AI và hiểu rõ các rủi ro bảo mật liên quan.", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "Hồ sơ cấu hình", "providerDocumentation": "Tài liệu {{provider}}", + "configProfile": "Hồ sơ cấu hình", "description": "Lưu các cấu hình API khác nhau để nhanh chóng chuyển đổi giữa các nhà cung cấp và cài đặt.", "apiProvider": "Nhà cung cấp API", "model": "Mẫu", @@ -118,13 +116,22 @@ "headerValue": "Giá trị tiêu đề", "noCustomHeaders": "Chưa có tiêu đề tùy chỉnh nào được định nghĩa. Nhấp vào nút + để thêm.", "requestyApiKey": "Khóa API Requesty", + "refreshModels": { + "label": "Làm mới mô hình", + "hint": "Vui lòng mở lại cài đặt để xem các mô hình mới nhất." + }, "getRequestyApiKey": "Lấy khóa API Requesty", + "openRouterTransformsText": "Nén lời nhắc và chuỗi tin nhắn theo kích thước ngữ cảnh (OpenRouter Transforms)", "anthropicApiKey": "Khóa API Anthropic", "getAnthropicApiKey": "Lấy khóa API Anthropic", "anthropicUseAuthToken": "Truyền khóa API Anthropic dưới dạng tiêu đề Authorization thay vì X-Api-Key", + "chutesApiKey": "Khóa API Chutes", + "getChutesApiKey": "Lấy khóa API Chutes", "deepSeekApiKey": "Khóa API DeepSeek", "getDeepSeekApiKey": "Lấy khóa API DeepSeek", "geminiApiKey": "Khóa API Gemini", + "getGroqApiKey": "Lấy khóa API Groq", + "groqApiKey": "Khóa API Groq", "getGeminiApiKey": "Lấy khóa API Gemini", "openAiApiKey": "Khóa API OpenAI", "openAiBaseUrl": "URL cơ sở", @@ -135,6 +142,8 @@ "codestralBaseUrlDesc": "Đặt URL thay thế cho mô hình Codestral.", "xaiApiKey": "Khóa API xAI", "getXaiApiKey": "Lấy khóa API xAI", + "litellmApiKey": "Khóa API LiteLLM", + "litellmBaseUrl": "URL cơ sở LiteLLM", "awsCredentials": "Thông tin xác thực AWS", "awsProfile": "Hồ sơ AWS", "awsProfileName": "Tên hồ sơ AWS", @@ -174,7 +183,6 @@ "description": "Ollama cho phép bạn chạy các mô hình cục bộ trên máy tính của bạn. Để biết hướng dẫn về cách bắt đầu, xem hướng dẫn nhanh của họ.", "warning": "Lưu ý: Roo Code sử dụng các lời nhắc phức tạp và hoạt động tốt nhất với các mô hình Claude. Các mô hình kém mạnh hơn có thể không hoạt động như mong đợi." }, - "openRouterTransformsText": "Nén lời nhắc và chuỗi tin nhắn theo kích thước ngữ cảnh (OpenRouter Transforms)", "unboundApiKey": "Khóa API Unbound", "getUnboundApiKey": "Lấy khóa API Unbound", "humanRelay": { @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "Bật công cụ trình duyệt", - "description": "Khi được bật, Roo có thể sử dụng trình duyệt để tương tác với các trang web khi sử dụng các mô hình hỗ trợ sử dụng máy tính." + "description": "Khi được bật, Roo có thể sử dụng trình duyệt để tương tác với các trang web khi sử dụng các mô hình hỗ trợ sử dụng máy tính. <0>Tìm hiểu thêm" }, "viewport": { "label": "Kích thước khung nhìn", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "Bật điểm kiểm tra tự động", - "description": "Khi được bật, Roo sẽ tự động tạo các điểm kiểm tra trong quá trình thực hiện nhiệm vụ, giúp dễ dàng xem lại các thay đổi hoặc quay lại trạng thái trước đó." + "description": "Khi được bật, Roo sẽ tự động tạo các điểm kiểm tra trong quá trình thực hiện nhiệm vụ, giúp dễ dàng xem lại các thay đổi hoặc quay lại trạng thái trước đó. <0>Tìm hiểu thêm" } }, "notifications": { @@ -298,57 +306,69 @@ "label": "Giới hạn ngữ cảnh tệp workspace", "description": "Số lượng tệp tối đa để đưa vào chi tiết thư mục làm việc hiện tại. Giá trị cao hơn cung cấp nhiều ngữ cảnh hơn nhưng tăng sử dụng token." }, - "pearai-agent-ignore": { - "label": "Hiển thị tệp .pearai-agent-ignore trong danh sách và tìm kiếm", - "description": "Khi được bật, các tệp khớp với mẫu trong .pearai-agent-ignore sẽ được hiển thị trong danh sách với biểu tượng khóa. Khi bị tắt, các tệp này sẽ hoàn toàn bị ẩn khỏi danh sách tệp và tìm kiếm." + "rooignore": { + "label": "Hiển thị tệp .rooignore trong danh sách và tìm kiếm", + "description": "Khi được bật, các tệp khớp với mẫu trong .rooignore sẽ được hiển thị trong danh sách với biểu tượng khóa. Khi bị tắt, các tệp này sẽ hoàn toàn bị ẩn khỏi danh sách tệp và tìm kiếm." }, "maxReadFile": { "label": "Ngưỡng tự động cắt ngắn khi đọc tệp", - "description": "Agent đọc số dòng này khi mô hình không chỉ định giá trị bắt đầu/kết thúc. Nếu số này nhỏ hơn tổng số dòng của tệp, Roo sẽ tạo một chỉ mục số dòng của các định nghĩa mã. Trường hợp đặc biệt: -1 chỉ thị Roo đọc toàn bộ tệp (không tạo chỉ mục), và 0 chỉ thị không đọc dòng nào và chỉ cung cấp chỉ mục dòng cho ngữ cảnh tối thiểu. Giá trị thấp hơn giảm thiểu việc sử dụng ngữ cảnh ban đầu, cho phép đọc chính xác các phạm vi dòng sau này. Các yêu cầu có chỉ định bắt đầu/kết thúc rõ ràng không bị giới hạn bởi cài đặt này.", + "description": "Roo đọc số dòng này khi mô hình không chỉ định giá trị bắt đầu/kết thúc. Nếu số này nhỏ hơn tổng số dòng của tệp, Roo sẽ tạo một chỉ mục số dòng của các định nghĩa mã. Trường hợp đặc biệt: -1 chỉ thị Roo đọc toàn bộ tệp (không tạo chỉ mục), và 0 chỉ thị không đọc dòng nào và chỉ cung cấp chỉ mục dòng cho ngữ cảnh tối thiểu. Giá trị thấp hơn giảm thiểu việc sử dụng ngữ cảnh ban đầu, cho phép đọc chính xác các phạm vi dòng sau này. Các yêu cầu có chỉ định bắt đầu/kết thúc rõ ràng không bị giới hạn bởi cài đặt này.", "lines": "dòng", "always_full_read": "Luôn đọc toàn bộ tệp" } }, "terminal": { + "basic": { + "label": "Cài đặt Terminal: Cơ bản", + "description": "Cài đặt cơ bản cho terminal" + }, + "advanced": { + "label": "Cài đặt Terminal: Nâng cao", + "description": "Các tùy chọn sau có thể yêu cầu khởi động lại terminal để áp dụng cài đặt." + }, "outputLineLimit": { "label": "Giới hạn đầu ra terminal", - "description": "Số dòng tối đa để đưa vào đầu ra terminal khi thực hiện lệnh. Khi vượt quá, các dòng sẽ bị xóa khỏi phần giữa, tiết kiệm token." + "description": "Số dòng tối đa để đưa vào đầu ra terminal khi thực hiện lệnh. Khi vượt quá, các dòng sẽ bị xóa khỏi phần giữa, tiết kiệm token. <0>Tìm hiểu thêm" }, "shellIntegrationTimeout": { "label": "Thời gian chờ tích hợp shell terminal", - "description": "Thời gian tối đa để chờ tích hợp shell khởi tạo trước khi thực hiện lệnh. Đối với người dùng có thời gian khởi động shell dài, giá trị này có thể cần được tăng lên nếu bạn thấy lỗi \"Shell Integration Unavailable\" trong terminal." + "description": "Thời gian tối đa để chờ tích hợp shell khởi tạo trước khi thực hiện lệnh. Đối với người dùng có thời gian khởi động shell dài, giá trị này có thể cần được tăng lên nếu bạn thấy lỗi \"Shell Integration Unavailable\" trong terminal. <0>Tìm hiểu thêm" }, "shellIntegrationDisabled": { "label": "Tắt tích hợp shell terminal", - "description": "Bật tùy chọn này nếu lệnh terminal không hoạt động chính xác hoặc bạn thấy lỗi 'Shell Integration Unavailable'. Tùy chọn này sử dụng phương pháp đơn giản hơn để chạy lệnh, bỏ qua một số tính năng terminal nâng cao." - }, - "compressProgressBar": { - "label": "Nén đầu ra thanh tiến trình", - "description": "Khi được bật, xử lý đầu ra terminal với các ký tự carriage return (\\r) để mô phỏng cách terminal thật hiển thị nội dung. Điều này loại bỏ các trạng thái trung gian của thanh tiến trình, chỉ giữ lại trạng thái cuối cùng, giúp tiết kiệm không gian ngữ cảnh cho thông tin quan trọng hơn." - }, - "zdotdir": { - "label": "Bật xử lý ZDOTDIR", - "description": "Khi được bật, tạo thư mục tạm thời cho ZDOTDIR để xử lý tích hợp shell zsh một cách chính xác. Điều này đảm bảo tích hợp shell VSCode hoạt động chính xác với zsh trong khi vẫn giữ nguyên cấu hình zsh của bạn. (thử nghiệm)" + "description": "Bật tùy chọn này nếu lệnh terminal không hoạt động chính xác hoặc bạn thấy lỗi 'Shell Integration Unavailable'. Tùy chọn này sử dụng phương pháp đơn giản hơn để chạy lệnh, bỏ qua một số tính năng terminal nâng cao. <0>Tìm hiểu thêm" }, "commandDelay": { "label": "Độ trễ lệnh terminal", - "description": "Độ trễ tính bằng mili giây để thêm vào sau khi thực hiện lệnh. Cài đặt mặc định là 0 sẽ tắt hoàn toàn độ trễ. Điều này có thể giúp đảm bảo đầu ra lệnh được ghi lại đầy đủ trong các terminal có vấn đề về thời gian. Trong hầu hết các terminal, điều này được thực hiện bằng cách đặt `PROMPT_COMMAND='sleep N'` và PowerShell thêm `start-sleep` vào cuối mỗi lệnh. Ban đầu là giải pháp cho lỗi VSCode#237208 và có thể không cần thiết." + "description": "Độ trễ tính bằng mili giây để thêm vào sau khi thực hiện lệnh. Cài đặt mặc định là 0 sẽ tắt hoàn toàn độ trễ. Điều này có thể giúp đảm bảo đầu ra lệnh được ghi lại đầy đủ trong các terminal có vấn đề về thời gian. Trong hầu hết các terminal, điều này được thực hiện bằng cách đặt `PROMPT_COMMAND='sleep N'` và PowerShell thêm `start-sleep` vào cuối mỗi lệnh. Ban đầu là giải pháp cho lỗi VSCode#237208 và có thể không cần thiết. <0>Tìm hiểu thêm" + }, + "compressProgressBar": { + "label": "Nén đầu ra thanh tiến trình", + "description": "Khi được bật, xử lý đầu ra terminal với các ký tự carriage return (\\r) để mô phỏng cách terminal thật hiển thị nội dung. Điều này loại bỏ các trạng thái trung gian của thanh tiến trình, chỉ giữ lại trạng thái cuối cùng, giúp tiết kiệm không gian ngữ cảnh cho thông tin quan trọng hơn. <0>Tìm hiểu thêm" }, "powershellCounter": { "label": "Bật giải pháp bộ đếm PowerShell", - "description": "Khi được bật, thêm một bộ đếm vào các lệnh PowerShell để đảm bảo thực thi lệnh chính xác. Điều này giúp ích với các terminal PowerShell có thể gặp vấn đề về ghi lại đầu ra." + "description": "Khi được bật, thêm một bộ đếm vào các lệnh PowerShell để đảm bảo thực thi lệnh chính xác. Điều này giúp ích với các terminal PowerShell có thể gặp vấn đề về ghi lại đầu ra. <0>Tìm hiểu thêm" }, "zshClearEolMark": { "label": "Xóa dấu cuối dòng ZSH", - "description": "Khi được bật, xóa dấu cuối dòng ZSH bằng cách đặt PROMPT_EOL_MARK=''. Điều này ngăn chặn các vấn đề về diễn giải đầu ra lệnh khi kết thúc bằng các ký tự đặc biệt như '%'." + "description": "Khi được bật, xóa dấu cuối dòng ZSH bằng cách đặt PROMPT_EOL_MARK=''. Điều này ngăn chặn các vấn đề về diễn giải đầu ra lệnh khi kết thúc bằng các ký tự đặc biệt như '%'. <0>Tìm hiểu thêm" }, "zshOhMy": { "label": "Bật tích hợp Oh My Zsh", - "description": "Khi được bật, đặt ITERM_SHELL_INTEGRATION_INSTALLED=Yes để kích hoạt các tính năng tích hợp shell của Oh My Zsh. Việc áp dụng cài đặt này có thể yêu cầu khởi động lại IDE. (thử nghiệm)" + "description": "Khi được bật, đặt ITERM_SHELL_INTEGRATION_INSTALLED=Yes để kích hoạt các tính năng tích hợp shell của Oh My Zsh. Việc áp dụng cài đặt này có thể yêu cầu khởi động lại IDE. <0>Tìm hiểu thêm" }, "zshP10k": { "label": "Bật tích hợp Powerlevel10k", - "description": "Khi được bật, đặt POWERLEVEL9K_TERM_SHELL_INTEGRATION=true để kích hoạt các tính năng tích hợp shell của Powerlevel10k. (thử nghiệm)" + "description": "Khi được bật, đặt POWERLEVEL9K_TERM_SHELL_INTEGRATION=true để kích hoạt các tính năng tích hợp shell của Powerlevel10k. <0>Tìm hiểu thêm" + }, + "zdotdir": { + "label": "Bật xử lý ZDOTDIR", + "description": "Khi được bật, tạo thư mục tạm thời cho ZDOTDIR để xử lý tích hợp shell zsh một cách chính xác. Điều này đảm bảo tích hợp shell VSCode hoạt động chính xác với zsh trong khi vẫn giữ nguyên cấu hình zsh của bạn. <0>Tìm hiểu thêm" + }, + "inheritEnv": { + "label": "Kế thừa biến môi trường", + "description": "Khi được bật, terminal sẽ kế thừa các biến môi trường từ tiến trình cha của VSCode, như các cài đặt tích hợp shell được định nghĩa trong hồ sơ người dùng. Điều này trực tiếp chuyển đổi cài đặt toàn cục của VSCode `terminal.integrated.inheritEnv`. <0>Tìm hiểu thêm" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "Nén cửa sổ ngữ cảnh một cách thông minh", + "description": "Sử dụng một lệnh gọi LLM để tóm tắt cuộc trò chuyện trước đó khi cửa sổ ngữ cảnh của tác vụ gần đầy, thay vì loại bỏ các tin nhắn cũ. Lưu ý: chi phí tóm tắt hiện không được tính vào chi phí API hiển thị trong giao diện người dùng." + }, "DIFF_STRATEGY_UNIFIED": { "name": "Sử dụng chiến lược diff thống nhất thử nghiệm", "description": "Bật chiến lược diff thống nhất thử nghiệm. Chiến lược này có thể giảm số lần thử lại do lỗi mô hình nhưng có thể gây ra hành vi không mong muốn hoặc chỉnh sửa không chính xác. Chỉ bật nếu bạn hiểu rõ các rủi ro và sẵn sàng xem xét cẩn thận tất cả các thay đổi." @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "Bật bộ nhớ đệm prompt", - "description": "Khi được bật, Roo sẽ sử dụng mô hình này với bộ nhớ đệm prompt được bật để giảm chi phí." + "label": "Tắt bộ nhớ đệm prompt", + "description": "Khi được chọn, Roo sẽ không sử dụng bộ nhớ đệm prompt cho mô hình này." }, "temperature": { "useCustom": "Sử dụng nhiệt độ tùy chỉnh", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "Nếu bạn có bất kỳ câu hỏi hoặc phản hồi nào, vui lòng mở một vấn đề tại github.com/RooVetGit/Roo-Code hoặc tham gia reddit.com/r/RooCode hoặc discord.gg/roocode", - "version": "Agent v{{version}}", "telemetry": { "label": "Cho phép báo cáo lỗi và sử dụng ẩn danh", "description": "Giúp cải thiện Roo Code bằng cách gửi dữ liệu sử dụng ẩn danh và báo cáo lỗi. Không bao giờ gửi mã, lời nhắc hoặc thông tin cá nhân. Xem chính sách bảo mật của chúng tôi để biết thêm chi tiết." diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 5d74e7d7bbf..e97a40aeaca 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -135,7 +135,7 @@ "current": "当前" }, "instructions": { - "wantsToFetch": "Agent 想要获取详细指示以协助当前任务" + "wantsToFetch": "Roo 想要获取详细指示以协助当前任务" }, "fileOperations": { "wantsToRead": "需要读取文件:", @@ -202,12 +202,12 @@ "copyToInput": "复制到输入框(或按住Shift点击)" }, "announcement": { - "title": "🎉 Roo Code 3.15 已发布", - "description": "Agent Code 3.15 带来基于您反馈的新功能和改进。", + "title": "🎉 Roo Code 3.17 已发布", + "description": "Roo Code 3.17 带来基于您反馈的强大新功能和改进。", "whatsNew": "新特性", - "feature1": "Vertex 提示词缓存: 为 Vertex AI 添加提示词缓存支持,提高响应速度并降低 API 费用", - "feature2": "终端备选方案: 实现 VSCode 终端 shell 集成失败时的备选机制", - "feature3": "代码片段优化: 增强聊天界面中代码片段的渲染和交互体验", + "feature1": "Gemini 的隐式缓存: Gemini API 调用现在会自动缓存,降低 API 费用", + "feature2": "更智能的模式选择: 模式定义现在可以包含何时使用每种模式的指导,实现更好的协调", + "feature3": "智能上下文压缩: 当上下文填满时,智能汇总对话历史而非截断(在设置 -> 实验性功能中启用)", "hideButton": "隐藏公告", "detailsDiscussLinks": "在 DiscordReddit 获取更多详情并参与讨论 🚀" }, @@ -233,6 +233,15 @@ "close": "关闭浏览器" } }, + "codeblock": { + "tooltips": { + "expand": "展开代码块", + "collapse": "收起代码块", + "enable_wrap": "启用自动换行", + "disable_wrap": "禁用自动换行", + "copy_code": "复制代码" + } + }, "systemPromptWarning": "警告:自定义系统提示词覆盖已激活。这可能严重破坏功能并导致不可预测的行为。", "shellIntegration": { "title": "命令执行警告", diff --git a/webview-ui/src/i18n/locales/zh-CN/mcp.json b/webview-ui/src/i18n/locales/zh-CN/mcp.json index 858b2eeb0a7..8ca1aa433f6 100644 --- a/webview-ui/src/i18n/locales/zh-CN/mcp.json +++ b/webview-ui/src/i18n/locales/zh-CN/mcp.json @@ -1,18 +1,19 @@ { - "title": "MCP服务管理", + "title": "MCP 服务器", "done": "完成", - "description": "<0>Model Context Protocol 支持与本地MCP服务通信,提供扩展功能。您可以使用<1>社区服务器,或通过指令创建定制工具(例如:“新增获取最新npm文档的工具”)。", + "description": "启用 Model Context Protocol (MCP),让 Roo Code 可用外部服务器的工具和服务,扩展 Roo 的能力。<0>了解更多", "enableToggle": { - "title": "启用MCP服务", - "description": "启用后Roo可与MCP服务交互获取高级功能。未使用时建议关闭以节省Token消耗。" + "title": "启用 MCP 服务器", + "description": "开启后 Roo 可用已连接 MCP 服务器的工具,能力更强。不用这些工具时建议关闭,节省 API Token 费用。" }, "enableServerCreation": { - "title": "允许创建工具", - "description": "启用后模型可通过“添加新工具”等指令创建MCP服务。无需创建功能时建议关闭以节省Token。" + "title": "启用 MCP 服务器创建", + "description": "开启后 Roo 可帮你创建<1>新自定义 MCP 服务器。<0>了解服务器创建", + "hint": "提示:不需要 Roo 创建新 MCP 服务器时建议关闭,减少 API Token 费用。" }, - "editGlobalMCP": "编辑全局配置", - "editProjectMCP": "编辑项目配置", - "editSettings": "参数设置", + "editGlobalMCP": "编辑全局 MCP", + "editProjectMCP": "编辑项目 MCP", + "learnMoreEditingSettings": "了解如何编辑 MCP 设置文件", "tool": { "alwaysAllow": "始终允许", "parameters": "参数", @@ -20,15 +21,18 @@ }, "tabs": { "tools": "工具", - "resources": "资源" + "resources": "资源", + "errors": "错误" }, "emptyState": { "noTools": "未找到工具", - "noResources": "未找到资源" + "noResources": "未找到资源", + "noLogs": "未找到日志", + "noErrors": "未找到错误" }, "networkTimeout": { - "label": "请求超时", - "description": "服务响应最长等待时间", + "label": "网络超时", + "description": "服务器响应最大等待时间", "options": { "15seconds": "15秒", "30seconds": "30秒", @@ -37,12 +41,12 @@ "10minutes": "10分钟", "15minutes": "15分钟", "30minutes": "30分钟", - "60minutes": "1小时" + "60minutes": "60分钟" } }, "deleteDialog": { - "title": "删除 MCP 服务", - "description": "确认删除MCP服务 \"{{serverName}}\"?此操作不可逆。", + "title": "删除 MCP 服务器", + "description": "确认删除 MCP 服务器 \"{{serverName}}\"?此操作不可逆。", "cancel": "取消", "delete": "删除" }, diff --git a/webview-ui/src/i18n/locales/zh-CN/prompts.json b/webview-ui/src/i18n/locales/zh-CN/prompts.json index 9c8dbb504d0..a0d9ff304cc 100644 --- a/webview-ui/src/i18n/locales/zh-CN/prompts.json +++ b/webview-ui/src/i18n/locales/zh-CN/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "新建模式", "editModesConfig": "模式设置", "editGlobalModes": "修改全局模式", - "editProjectModes": "编辑项目模式 (.pearai-agent-ignore)", - "createModeHelpText": "点击 + 创建模式,或在对话时让Roo创建一个新模式。" + "editProjectModes": "编辑项目模式 (.roomodes)", + "createModeHelpText": "模式是Roo的专属角色,用于定制其行为。<0>了解如何使用模式或<1>自定义模式。", + "selectMode": "搜索模式" }, "apiConfiguration": { "title": "API配置", @@ -33,16 +34,21 @@ "resetToDefault": "重置为默认值", "description": "设定专业领域和应答风格" }, + "whenToUse": { + "title": "使用场景(可选)", + "description": "描述何时应该使用此模式。这有助于 Orchestrator 为任务选择合适的模式。", + "resetToDefault": "重置\"使用场景\"描述为默认值" + }, "customInstructions": { "title": "模式专属规则(可选)", "resetToDefault": "重置为默认值", "description": "{{modeName}}模式的专属规则", - "loadFromFile": "支持从.pearai-agent/rules-{{slug}}/目录读取配置(.roorules-{{slug}}和.clinerules-{{slug}}已弃用并将很快停止工作)。" + "loadFromFile": "支持从.roo/rules-{{slug}}/目录读取配置(.roorules-{{slug}}和.clinerules-{{slug}}已弃用并将很快停止工作)。" }, "globalCustomInstructions": { "title": "所有模式的自定义指令", - "description": "所有模式通用规则\n当前语言:{{language}}", - "loadFromFile": "支持从.pearai-agent/rules/目录读取全局配置(.roorules和.clinerules已弃用并将很快停止工作)。" + "description": "这些指令适用于所有模式。它们提供了一套基础行为,可以通过下面的模式特定指令进行增强。<0>了解更多", + "loadFromFile": "支持从.roo/rules/目录读取全局配置(.roorules和.clinerules已弃用并将很快停止工作)。" }, "systemPrompt": { "preview": "预览系统提示词", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "高级:覆盖系统提示词", - "description": "您可以通过在工作区创建文件 .pearai-agent/system-prompt-{{slug}},完全替换此模式的系统提示(角色定义和自定义指令除外)。这是一个非常高级的功能,会绕过内置的安全措施和一致性检查(尤其是与工具使用相关的部分),请谨慎操作!" + "description": "<2>⚠️ 警告: 此高级功能会绕过安全措施。<1>使用前请阅读!通过在您的工作区中创建文件 .roo/system-prompt-{{slug}} 来覆盖默认系统提示。" }, "createModeDialog": { "title": "创建新模式", @@ -122,7 +128,7 @@ "description": "全局可用" }, "project": { - "label": "项目特定 (.pearai-agent-ignore)", + "label": "项目特定 (.roomodes)", "description": "仅当前项目有效" } }, @@ -130,6 +136,10 @@ "label": "角色定义", "description": "设定专业方向" }, + "whenToUse": { + "label": "使用场景(可选)", + "description": "清晰描述此模式最适合的场景和任务类型" + }, "tools": { "label": "可用工具", "description": "选择可用工具" diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index e0482f7b335..6cb2a4b656a 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -23,14 +23,12 @@ "sections": { "providers": "提供商", "autoApprove": "自动批准", - "browser": "浏览器交互", - "checkpoints": "检查点", - "interface": "界面内容", + "browser": "计算机交互", + "checkpoints": "存档点", "notifications": "通知", - "contextManagement": "上下文管理", + "contextManagement": "上下文", "terminal": "终端", - "advanced": "高级", - "experimental": "实验性功能", + "experimental": "实验性", "language": "语言", "about": "关于 Roo Code" }, @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "配置文件", "providerDocumentation": "{{provider}} 文档", + "configProfile": "配置文件", "description": "保存多组API配置便于快速切换", "apiProvider": "API提供商", "model": "模型", @@ -108,6 +106,8 @@ "openRouterApiKey": "OpenRouter API 密钥", "getOpenRouterApiKey": "获取 OpenRouter API 密钥", "apiKeyStorageNotice": "API 密钥安全存储在 VSCode 的密钥存储中", + "glamaApiKey": "Glama API 密钥", + "getGlamaApiKey": "获取 Glama API 密钥", "useCustomBaseUrl": "使用自定义基础 URL", "useHostHeader": "使用自定义 Host 标头", "useLegacyFormat": "使用传统 OpenAI API 格式", @@ -115,17 +115,23 @@ "headerName": "标头名称", "headerValue": "标头值", "noCustomHeaders": "暂无自定义标头。点击 + 按钮添加。", - "glamaApiKey": "Glama API 密钥", - "getGlamaApiKey": "获取 Glama API 密钥", "requestyApiKey": "Requesty API 密钥", + "refreshModels": { + "label": "刷新模型", + "hint": "请重新打开设置以查看最新模型。" + }, "getRequestyApiKey": "获取 Requesty API 密钥", "openRouterTransformsText": "自动压缩提示词和消息链到上下文长度限制内 (OpenRouter转换)", "anthropicApiKey": "Anthropic API 密钥", "getAnthropicApiKey": "获取 Anthropic API 密钥", "anthropicUseAuthToken": "将 Anthropic API 密钥作为 Authorization 标头传递,而不是 X-Api-Key", + "chutesApiKey": "Chutes API 密钥", + "getChutesApiKey": "获取 Chutes API 密钥", "deepSeekApiKey": "DeepSeek API 密钥", "getDeepSeekApiKey": "获取 DeepSeek API 密钥", "geminiApiKey": "Gemini API 密钥", + "getGroqApiKey": "获取 Groq API 密钥", + "groqApiKey": "Groq API 密钥", "getGeminiApiKey": "获取 Gemini API 密钥", "openAiApiKey": "OpenAI API 密钥", "openAiBaseUrl": "OpenAI 基础 URL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "为 Codestral 模型设置替代 URL。", "xaiApiKey": "xAI API 密钥", "getXaiApiKey": "获取 xAI API 密钥", + "litellmApiKey": "LiteLLM API 密钥", + "litellmBaseUrl": "LiteLLM 基础 URL", "awsCredentials": "AWS 凭证", "awsProfile": "AWS 配置文件", "awsProfileName": "AWS 配置文件名称", @@ -231,7 +239,7 @@ "resetDefaults": "重置为默认值" }, "rateLimitSeconds": { - "label": "请求频率限制", + "label": "API 请求频率限制", "description": "设置API请求的最小间隔时间" }, "reasoningEffort": { @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "启用浏览器工具", - "description": "启用后,若模型支持计算机功能调用,Roo 可以使用浏览器与网站交互。" + "description": "启用后,若模型支持计算机功能调用,Roo 可以使用浏览器与网站交互。 <0>了解更多" }, "viewport": { "label": "视口大小", @@ -272,8 +280,8 @@ }, "checkpoints": { "enable": { - "label": "启用自动检查点", - "description": "开启后自动创建任务检查点,方便回溯修改。" + "label": "启用自动存档点", + "description": "开启后自动创建任务存档点,方便回溯修改。 <0>了解更多" } }, "notifications": { @@ -298,57 +306,69 @@ "label": "工作区文件限制", "description": "允许纳入上下文的最大文件数(值越大消耗token越多)" }, - "pearai-agent-ignore": { - "label": "在列表和搜索中显示 .pearai-agent-ignore 文件", - "description": "启用后,与 .pearai-agent-ignore 中模式匹配的文件将在列表中显示锁定符号。禁用时,这些文件将从文件列表和搜索中完全隐藏。" + "rooignore": { + "label": "在列表和搜索中显示 .rooignore 文件", + "description": "启用后,与 .rooignore 中模式匹配的文件将在列表中显示锁定符号。禁用时,这些文件将从文件列表和搜索中完全隐藏。" }, "maxReadFile": { "label": "文件读取自动截断阈值", - "description": "自动读取文件行数设置:-1=完整读取 0=仅生成行号索引,较小值可节省token,支持后续使用行号进行读取。", + "description": "自动读取文件行数设置:-1=完整读取 0=仅生成行号索引,较小值可节省token,支持后续使用行号进行读取。 <0>了解更多", "lines": "行", "always_full_read": "始终读取整个文件" } }, "terminal": { + "basic": { + "label": "终端设置:基础", + "description": "基础终端设置" + }, + "advanced": { + "label": "终端设置:高级", + "description": "以下选项可能需要重启终端才能应用设置" + }, "outputLineLimit": { "label": "终端输出限制", - "description": "执行命令时在终端输出中包含的最大行数。超过时将从中间删除行,节省 token。" + "description": "执行命令时在终端输出中包含的最大行数。超过时将从中间删除行,节省 token。 <0>了解更多" }, "shellIntegrationTimeout": { "label": "终端初始化等待时间", - "description": "执行命令前等待 Shell 集成初始化的最长时间。对于 Shell 启动时间较长的用户,如果在终端中看到\"Shell Integration Unavailable\"错误,可能需要增加此值。" + "description": "执行命令前等待 Shell 集成初始化的最长时间。对于 Shell 启动时间较长的用户,如果在终端中看到\"Shell Integration Unavailable\"错误,可能需要增加此值。 <0>了解更多" }, "shellIntegrationDisabled": { "label": "禁用终端 Shell 集成", - "description": "如果终端命令无法正常工作或看到 'Shell Integration Unavailable' 错误,请启用此项。这将使用更简单的方法运行命令,绕过一些高级终端功能。" - }, - "compressProgressBar": { - "label": "压缩进度条输出", - "description": "启用后,将处理包含回车符 (\\r) 的终端输出,模拟真实终端显示内容的方式。这会移除进度条的中间状态,只保留最终状态,为更重要的信息节省上下文空间。" - }, - "zdotdir": { - "label": "启用 ZDOTDIR 处理", - "description": "启用后将创建临时目录用于 ZDOTDIR,以正确处理 zsh shell 集成。这确保 VSCode shell 集成能与 zsh 正常工作,同时保留您的 zsh 配置。(实验性)" + "description": "如果终端命令无法正常工作或看到 'Shell Integration Unavailable' 错误,请启用此项。这将使用更简单的方法运行命令,绕过一些高级终端功能。 <0>了解更多" }, "commandDelay": { "label": "终端命令延迟", - "description": "命令执行后添加的延迟时间(毫秒)。默认设置为 0 时完全禁用延迟。这可以帮助确保在有计时问题的终端中完全捕获命令输出。在大多数终端中,这是通过设置 `PROMPT_COMMAND='sleep N'` 实现的,而 PowerShell 会在每个命令末尾添加 `start-sleep`。最初是为了解决 VSCode 错误#237208,现在可能不再需要。" + "description": "命令执行后添加的延迟时间(毫秒)。默认设置为 0 时完全禁用延迟。这可以帮助确保在有计时问题的终端中完全捕获命令输出。在大多数终端中,这是通过设置 `PROMPT_COMMAND='sleep N'` 实现的,而 PowerShell 会在每个命令末尾添加 `start-sleep`。最初是为了解决 VSCode 错误#237208,现在可能不再需要。 <0>了解更多" + }, + "compressProgressBar": { + "label": "压缩进度条输出", + "description": "启用后,将处理包含回车符 (\\r) 的终端输出,模拟真实终端显示内容的方式。这会移除进度条的中间状态,只保留最终状态,为更重要的信息节省上下文空间。 <0>了解更多" }, "powershellCounter": { "label": "启用 PowerShell 计数器解决方案", - "description": "启用后,会在 PowerShell 命令中添加计数器以确保命令正确执行。这有助于解决可能存在输出捕获问题的 PowerShell 终端。" + "description": "启用后,会在 PowerShell 命令中添加计数器以确保命令正确执行。这有助于解决可能存在输出捕获问题的 PowerShell 终端。 <0>了解更多" }, "zshClearEolMark": { "label": "清除 ZSH 行尾标记", - "description": "启用后,通过设置 PROMPT_EOL_MARK='' 清除 ZSH 行尾标记。这可以防止命令输出以特殊字符(如 '%')结尾时的解析问题。" + "description": "启用后,通过设置 PROMPT_EOL_MARK='' 清除 ZSH 行尾标记。这可以防止命令输出以特殊字符(如 '%')结尾时的解析问题。 <0>了解更多" }, "zshOhMy": { "label": "启用 Oh My Zsh 集成", - "description": "启用后,设置 ITERM_SHELL_INTEGRATION_INSTALLED=Yes 以启用 Oh My Zsh shell 集成功能。应用此设置可能需要重启 IDE。(实验性)" + "description": "启用后,设置 ITERM_SHELL_INTEGRATION_INSTALLED=Yes 以启用 Oh My Zsh shell 集成功能。应用此设置可能需要重启 IDE。 <0>了解更多" }, "zshP10k": { "label": "启用 Powerlevel10k 集成", - "description": "启用后,设置 POWERLEVEL9K_TERM_SHELL_INTEGRATION=true 以启用 Powerlevel10k shell 集成功能。(实验性)" + "description": "启用后,设置 POWERLEVEL9K_TERM_SHELL_INTEGRATION=true 以启用 Powerlevel10k shell 集成功能。 <0>了解更多" + }, + "zdotdir": { + "label": "启用 ZDOTDIR 处理", + "description": "启用后将创建临时目录用于 ZDOTDIR,以正确处理 zsh shell 集成。这确保 VSCode shell 集成能与 zsh 正常工作,同时保留您的 zsh 配置。 <0>了解更多" + }, + "inheritEnv": { + "label": "继承环境变量", + "description": "启用后,终端将从 VSCode 父进程继承环境变量,如用户配置文件中定义的 shell 集成设置。这直接切换 VSCode 全局设置 `terminal.integrated.inheritEnv`。 <0>了解更多" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "智能压缩上下文窗口", + "description": "当任务上下文窗口接近填满时,使用 LLM 调用来总结过去的对话,而不是删除旧消息。注意:目前 UI 中显示的 API 费用不包括总结的成本。" + }, "DIFF_STRATEGY_UNIFIED": { "name": "启用diff更新工具", "description": "可减少因模型错误导致的重复尝试,但可能引发意外操作。启用前请确保理解风险并会仔细检查所有修改。" @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "启用提示词缓存", - "description": "启用后 Roo 将使用提示词缓存功能以降低成本。" + "label": "禁用提示词缓存", + "description": "选中后,Roo 将不会为此模型使用提示词缓存。" }, "temperature": { "useCustom": "使用自定义温度", @@ -438,7 +462,6 @@ }, "footer": { "feedback": "如果您有任何问题或反馈,请随时在 github.com/RooVetGit/Roo-Code 上提出问题或加入 reddit.com/r/RooCodediscord.gg/roocode", - "version": "Agent v{{version}}", "telemetry": { "label": "允许匿名数据收集", "description": "匿名收集错误报告和使用数据(不含代码/提示/个人信息),详情见隐私政策" diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 797ffdaf85d..8d142da6c06 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -109,7 +109,7 @@ "diffError": { "title": "編輯失敗" }, - "troubleMessage": "Agent 遇到問題...", + "troubleMessage": "Roo 遇到問題...", "apiRequest": { "title": "API 請求", "failed": "API 請求失敗", @@ -135,47 +135,47 @@ "current": "目前" }, "instructions": { - "wantsToFetch": "Agent 想要取得詳細指示以協助目前任務" + "wantsToFetch": "Roo 想要取得詳細指示以協助目前任務" }, "fileOperations": { - "wantsToRead": "Agent 想要讀取此檔案:", - "wantsToReadOutsideWorkspace": "Agent 想要讀取此工作區外的檔案:", - "didRead": "Agent 已讀取此檔案:", - "wantsToEdit": "Agent 想要編輯此檔案:", - "wantsToEditOutsideWorkspace": "Agent 想要編輯此工作區外的檔案:", - "wantsToCreate": "Agent 想要建立新檔案:", - "wantsToSearchReplace": "Agent 想要在此檔案中搜尋和取代:", - "didSearchReplace": "Agent 已在此檔案執行搜尋和取代:", - "wantsToInsert": "Agent 想要在此檔案中插入內容:", - "wantsToInsertWithLineNumber": "Agent 想要在此檔案第 {{lineNumber}} 行插入內容:", - "wantsToInsertAtEnd": "Agent 想要在此檔案末尾新增內容:" + "wantsToRead": "Roo 想要讀取此檔案:", + "wantsToReadOutsideWorkspace": "Roo 想要讀取此工作區外的檔案:", + "didRead": "Roo 已讀取此檔案:", + "wantsToEdit": "Roo 想要編輯此檔案:", + "wantsToEditOutsideWorkspace": "Roo 想要編輯此工作區外的檔案:", + "wantsToCreate": "Roo 想要建立新檔案:", + "wantsToSearchReplace": "Roo 想要在此檔案中搜尋和取代:", + "didSearchReplace": "Roo 已在此檔案執行搜尋和取代:", + "wantsToInsert": "Roo 想要在此檔案中插入內容:", + "wantsToInsertWithLineNumber": "Roo 想要在此檔案第 {{lineNumber}} 行插入內容:", + "wantsToInsertAtEnd": "Roo 想要在此檔案末尾新增內容:" }, "directoryOperations": { - "wantsToViewTopLevel": "Agent 想要檢視此目錄中最上層的檔案:", - "didViewTopLevel": "Agent 已檢視此目錄中最上層的檔案:", - "wantsToViewRecursive": "Agent 想要遞迴檢視此目錄中的所有檔案:", - "didViewRecursive": "Agent 已遞迴檢視此目錄中的所有檔案:", - "wantsToViewDefinitions": "Agent 想要檢視此目錄中使用的原始碼定義名稱:", - "didViewDefinitions": "Agent 已檢視此目錄中使用的原始碼定義名稱:", - "wantsToSearch": "Agent 想要在此目錄中搜尋 {{regex}}:", - "didSearch": "Agent 已在此目錄中搜尋 {{regex}}:" + "wantsToViewTopLevel": "Roo 想要檢視此目錄中最上層的檔案:", + "didViewTopLevel": "Roo 已檢視此目錄中最上層的檔案:", + "wantsToViewRecursive": "Roo 想要遞迴檢視此目錄中的所有檔案:", + "didViewRecursive": "Roo 已遞迴檢視此目錄中的所有檔案:", + "wantsToViewDefinitions": "Roo 想要檢視此目錄中使用的原始碼定義名稱:", + "didViewDefinitions": "Roo 已檢視此目錄中使用的原始碼定義名稱:", + "wantsToSearch": "Roo 想要在此目錄中搜尋 {{regex}}:", + "didSearch": "Roo 已在此目錄中搜尋 {{regex}}:" }, "commandOutput": "命令輸出", "response": "回應", "arguments": "參數", "mcp": { - "wantsToUseTool": "Agent 想要在 {{serverName}} MCP 伺服器上使用工具:", - "wantsToAccessResource": "Agent 想要存取 {{serverName}} MCP 伺服器上的資源:" + "wantsToUseTool": "Roo 想要在 {{serverName}} MCP 伺服器上使用工具:", + "wantsToAccessResource": "Roo 想要存取 {{serverName}} MCP 伺服器上的資源:" }, "modes": { - "wantsToSwitch": "Agent 想要切換至 {{mode}} 模式", - "wantsToSwitchWithReason": "Agent 想要切換至 {{mode}} 模式,原因:{{reason}}", - "didSwitch": "Agent 已切換至 {{mode}} 模式", - "didSwitchWithReason": "Agent 已切換至 {{mode}} 模式,原因:{{reason}}" + "wantsToSwitch": "Roo 想要切換至 {{mode}} 模式", + "wantsToSwitchWithReason": "Roo 想要切換至 {{mode}} 模式,原因:{{reason}}", + "didSwitch": "Roo 已切換至 {{mode}} 模式", + "didSwitchWithReason": "Roo 已切換至 {{mode}} 模式,原因:{{reason}}" }, "subtasks": { - "wantsToCreate": "Agent 想要在 {{mode}} 模式下建立新的子工作:", - "wantsToFinish": "Agent 想要完成此子工作", + "wantsToCreate": "Roo 想要在 {{mode}} 模式下建立新的子工作:", + "wantsToFinish": "Roo 想要完成此子工作", "newTaskContent": "子工作指示", "completionContent": "子工作已完成", "resultContent": "子工作結果", @@ -183,7 +183,7 @@ "completionInstructions": "子工作已完成!您可以檢閱結果並提出修正或下一步建議。如果一切看起來良好,請確認以將結果傳回主工作。" }, "questions": { - "hasQuestion": "Agent 有一個問題:" + "hasQuestion": "Roo 有一個問題:" }, "taskCompleted": "工作完成", "powershell": { @@ -202,17 +202,17 @@ "copyToInput": "複製到輸入框(或按住 Shift 並點選)" }, "announcement": { - "title": "🎉 Roo Code 3.15 已發布", - "description": "Agent Code 3.15 帶來基於您意見回饋的新功能與改進。", + "title": "🎉 Roo Code 3.17 已發布", + "description": "Roo Code 3.17 帶來基於您意見回饋的強大新功能與改進。", "whatsNew": "新功能", - "feature1": "Vertex 提示詞快取: 為 Vertex AI 新增提示詞快取支援,改善回應時間並降低 API 費用", - "feature2": "終端機備援機制: 實作 VSCode 終端機 shell 整合失敗時的備援機制", - "feature3": "程式碼片段強化: 增強聊天介面中程式碼片段的渲染與互動體驗", + "feature1": "Gemini 的隱式快取: Gemini API 呼叫現在自動快取,降低 API 費用", + "feature2": "更智慧的模式選擇: 模式定義現在可以包含何時應使用各個模式的指引,實現更好的協調", + "feature3": "智慧上下文壓縮: 當上下文填滿時,智慧地摘要對話歷史而非截斷(在設定 -> 實驗性功能中啟用)", "hideButton": "隱藏公告", "detailsDiscussLinks": "在 DiscordReddit 取得更多詳細資訊並參與討論 🚀" }, "browser": { - "rooWantsToUse": "Agent 想要使用瀏覽器:", + "rooWantsToUse": "Roo 想要使用瀏覽器:", "consoleLogs": "主控台記錄", "noNewLogs": "(沒有新記錄)", "screenshot": "瀏覽器螢幕擷圖", @@ -233,6 +233,15 @@ "close": "關閉瀏覽器" } }, + "codeblock": { + "tooltips": { + "expand": "展開程式碼區塊", + "collapse": "摺疊程式碼區塊", + "enable_wrap": "啟用自動換行", + "disable_wrap": "停用自動換行", + "copy_code": "複製程式碼" + } + }, "systemPromptWarning": "警告:自訂系統提示詞覆蓋已啟用。這可能嚴重破壞功能並導致不可預測的行為。", "shellIntegration": { "title": "命令執行警告", diff --git a/webview-ui/src/i18n/locales/zh-TW/mcp.json b/webview-ui/src/i18n/locales/zh-TW/mcp.json index 5ac0afd0458..ae38fa2b161 100644 --- a/webview-ui/src/i18n/locales/zh-TW/mcp.json +++ b/webview-ui/src/i18n/locales/zh-TW/mcp.json @@ -1,18 +1,19 @@ { "title": "MCP 伺服器", "done": "完成", - "description": "<0>Model Context Protocol 能與本機執行的 MCP 伺服器通訊,提供額外的工具和資源來擴展 Roo 的功能。您可以使用<1>社群開發的伺服器,或請 Roo 為您的工作流程建立新工具(例如「新增一個取得最新 npm 文件的工具」)。", + "description": "啟用 Model Context Protocol (MCP),讓 Roo Code 可用外部伺服器的工具和服務,擴展 Roo 的能力。<0>了解更多", "enableToggle": { "title": "啟用 MCP 伺服器", - "description": "啟用後,Roo 將能與 MCP 伺服器互動以取得進階功能。如果您不使用 MCP,可以停用此功能以減少 Roo 的 token 使用量。" + "description": "開啟後 Roo 可用已連接 MCP 伺服器的工具,能力更強。不用這些工具時建議關閉,節省 API Token 費用。" }, "enableServerCreation": { "title": "啟用 MCP 伺服器建立", - "description": "啟用後,Roo 可以透過如「新增工具到...」等命令協助您建立新的 MCP 伺服器。如果您不需要建立 MCP 伺服器,可以停用此功能以減少 Roo 的 token 使用量。" + "description": "開啟後 Roo 可協助你建立<1>新自訂 MCP 伺服器。<0>了解伺服器建立", + "hint": "提示:不需要 Roo 建立新 MCP 伺服器時建議關閉,減少 API Token 費用。" }, "editGlobalMCP": "編輯全域 MCP", "editProjectMCP": "編輯專案 MCP", - "editSettings": "編輯 MCP 設定", + "learnMoreEditingSettings": "了解如何編輯 MCP 設定檔", "tool": { "alwaysAllow": "總是允許", "parameters": "參數", @@ -20,15 +21,18 @@ }, "tabs": { "tools": "工具", - "resources": "資源" + "resources": "資源", + "errors": "錯誤" }, "emptyState": { "noTools": "找不到工具", - "noResources": "找不到資源" + "noResources": "找不到資源", + "noLogs": "找不到日誌", + "noErrors": "找不到錯誤" }, "networkTimeout": { "label": "網路逾時", - "description": "等待伺服器回應的最長時間", + "description": "伺服器回應最大等待時間", "options": { "15seconds": "15 秒", "30seconds": "30 秒", @@ -42,7 +46,7 @@ }, "deleteDialog": { "title": "刪除 MCP 伺服器", - "description": "您確定要刪除 MCP 伺服器「{{serverName}}」嗎?此操作無法復原。", + "description": "你確定要刪除 MCP 伺服器「{{serverName}}」嗎?此操作無法復原。", "cancel": "取消", "delete": "刪除" }, diff --git a/webview-ui/src/i18n/locales/zh-TW/prompts.json b/webview-ui/src/i18n/locales/zh-TW/prompts.json index 876175b83fd..27d3843fc12 100644 --- a/webview-ui/src/i18n/locales/zh-TW/prompts.json +++ b/webview-ui/src/i18n/locales/zh-TW/prompts.json @@ -6,8 +6,9 @@ "createNewMode": "建立新模式", "editModesConfig": "編輯模式設定", "editGlobalModes": "編輯全域模式", - "editProjectModes": "編輯專案模式 (.pearai-agent-ignore)", - "createModeHelpText": "點選 + 建立新的自訂模式,或者在聊天中直接請 Roo 為您建立!" + "editProjectModes": "編輯專案模式 (.roomodes)", + "createModeHelpText": "模式是 Roo 的專屬角色,用於客製化其行為。<0>了解如何使用模式或<1>自訂模式。", + "selectMode": "搜尋模式" }, "apiConfiguration": { "title": "API 設定", @@ -33,16 +34,21 @@ "resetToDefault": "重設為預設值", "description": "定義此模式下 Roo 的專業知識和個性。此描述會形塑 Roo 如何展現自己並處理工作。" }, + "whenToUse": { + "title": "使用時機(選用)", + "description": "描述何時應使用此模式。這有助於 Orchestrator 為任務選擇適當的模式。", + "resetToDefault": "重設「使用時機」描述為預設值" + }, "customInstructions": { "title": "模式專屬自訂指令(選用)", "resetToDefault": "重設為預設值", "description": "為 {{modeName}} 模式新增專屬的行為指南。", - "loadFromFile": "{{mode}} 模式的自訂指令也可以從工作區的 .pearai-agent/rules-{{slug}}/ 資料夾載入(.roorules-{{slug}} 和 .clinerules-{{slug}} 已棄用並將很快停止運作)。" + "loadFromFile": "{{mode}} 模式的自訂指令也可以從工作區的 .roo/rules-{{slug}}/ 資料夾載入(.roorules-{{slug}} 和 .clinerules-{{slug}} 已棄用並將很快停止運作)。" }, "globalCustomInstructions": { "title": "所有模式的自訂指令", - "description": "這些指令適用於所有模式。它們提供了一組基本行為,可以透過下方的模式專屬自訂指令來強化。\n如果您希望 Roo 使用與編輯器顯示語言 ({{language}}) 不同的語言來思考和對話,您可以在這裡指定。", - "loadFromFile": "指令也可以從工作區的 .pearai-agent/rules/ 資料夾載入(.roorules 和 .clinerules 已棄用並將很快停止運作)。" + "description": "這些指令適用於所有模式。它們提供了一組基本行為,可以透過下方的模式專屬自訂指令來強化。<0>了解更多", + "loadFromFile": "指令也可以從工作區的 .roo/rules/ 資料夾載入(.roorules 和 .clinerules 已棄用並將很快停止運作)。" }, "systemPrompt": { "preview": "預覽系統提示詞", @@ -101,7 +107,7 @@ }, "advancedSystemPrompt": { "title": "進階:覆寫系統提示詞", - "description": "您可以透過在工作區建立檔案 .pearai-agent/system-prompt-{{slug}} 來完全替換此模式的系統提示詞(角色定義和自訂指令除外)。這是一個非常進階的功能,會繞過內建的安全措施和一致性檢查(尤其是與工具使用相關的檢查),請謹慎使用!" + "description": "<2>⚠️ 警告: 此進階功能會略過安全防護。<1>使用前請閱讀!透過在您的工作區中建立檔案 .roo/system-prompt-{{slug}} 來覆寫預設的系統提示詞。" }, "createModeDialog": { "title": "建立新模式", @@ -122,7 +128,7 @@ "description": "在所有工作區可用" }, "project": { - "label": "專案特定 (.pearai-agent-ignore)", + "label": "專案特定 (.roomodes)", "description": "僅在此工作區可用,優先於全域模式" } }, @@ -130,6 +136,10 @@ "label": "角色定義", "description": "定義此模式下 Roo 的專業知識和個性。" }, + "whenToUse": { + "label": "使用時機(選用)", + "description": "提供清晰的描述,說明此模式最適合什麼時候使用,以及適合哪些類型的任務。" + }, "tools": { "label": "可用工具", "description": "選擇此模式可使用的工具。" diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index cce91229045..67fb92126b5 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -23,16 +23,14 @@ "sections": { "providers": "供應商", "autoApprove": "自動核准", - "browser": "瀏覽器/電腦使用", + "browser": "電腦存取", "checkpoints": "檢查點", "notifications": "通知", - "contextManagement": "上下文管理", + "contextManagement": "上下文", "terminal": "終端機", - "advanced": "進階", - "experimental": "實驗性功能", + "experimental": "實驗性", "language": "語言", - "about": "關於 Roo Code", - "interface": "介面" + "about": "關於 Roo Code" }, "autoApprove": { "description": "允許 Roo 無需核准即執行操作。僅在您完全信任 AI 並了解相關安全風險時啟用這些設定。", @@ -84,8 +82,8 @@ } }, "providers": { - "configProfile": "配置設定檔", "providerDocumentation": "{{provider}} 文件", + "configProfile": "配置設定檔", "description": "儲存不同的 API 設定以快速切換供應商和設定。", "apiProvider": "API 供應商", "model": "模型", @@ -100,11 +98,11 @@ "enterProfileName": "輸入設定檔名稱", "createProfile": "建立設定檔", "cannotDeleteOnlyProfile": "無法刪除唯一的設定檔", + "searchPlaceholder": "搜尋設定檔", + "noMatchFound": "找不到符合的設定檔", "vscodeLmDescription": "VS Code 語言模型 API 可以讓您使用其他擴充功能(如 GitHub Copilot)提供的模型。最簡單的方式是從 VS Code Marketplace 安裝 Copilot 和 Copilot Chat 擴充套件。", "awsCustomArnUse": "輸入您要使用的模型的有效 Amazon Bedrock ARN。格式範例:", "awsCustomArnDesc": "確保 ARN 中的區域與您上面選擇的 AWS 區域相符。", - "searchPlaceholder": "搜尋設定檔", - "noMatchFound": "找不到符合的設定檔", "openRouterApiKey": "OpenRouter API 金鑰", "getOpenRouterApiKey": "取得 OpenRouter API 金鑰", "apiKeyStorageNotice": "API 金鑰安全儲存於 VSCode 金鑰儲存中", @@ -118,14 +116,22 @@ "headerValue": "標頭值", "noCustomHeaders": "尚未定義自訂標頭。點擊 + 按鈕以新增。", "requestyApiKey": "Requesty API 金鑰", + "refreshModels": { + "label": "重新整理模型", + "hint": "請重新開啟設定以查看最新模型。" + }, "getRequestyApiKey": "取得 Requesty API 金鑰", "openRouterTransformsText": "將提示和訊息鏈壓縮到上下文大小 (OpenRouter 轉換)", "anthropicApiKey": "Anthropic API 金鑰", "getAnthropicApiKey": "取得 Anthropic API 金鑰", "anthropicUseAuthToken": "將 Anthropic API 金鑰作為 Authorization 標頭傳遞,而非使用 X-Api-Key", + "chutesApiKey": "Chutes API 金鑰", + "getChutesApiKey": "取得 Chutes API 金鑰", "deepSeekApiKey": "DeepSeek API 金鑰", "getDeepSeekApiKey": "取得 DeepSeek API 金鑰", "geminiApiKey": "Gemini API 金鑰", + "getGroqApiKey": "取得 Groq API 金鑰", + "groqApiKey": "Groq API 金鑰", "getGeminiApiKey": "取得 Gemini API 金鑰", "openAiApiKey": "OpenAI API 金鑰", "openAiBaseUrl": "基礎 URL", @@ -136,6 +142,8 @@ "codestralBaseUrlDesc": "設定 Codestral 模型的替代 URL。", "xaiApiKey": "xAI API 金鑰", "getXaiApiKey": "取得 xAI API 金鑰", + "litellmApiKey": "LiteLLM API 金鑰", + "litellmBaseUrl": "LiteLLM 基礎 URL", "awsCredentials": "AWS 認證", "awsProfile": "AWS Profile", "awsProfileName": "AWS Profile 名稱", @@ -245,7 +253,7 @@ "browser": { "enable": { "label": "啟用瀏覽器工具", - "description": "啟用後,Roo 可在使用支援電腦使用的模型時使用瀏覽器與網站互動。" + "description": "啟用後,Roo 可在使用支援電腦使用的模型時使用瀏覽器與網站互動。 <0>瞭解更多" }, "viewport": { "label": "視窗大小", @@ -273,7 +281,7 @@ "checkpoints": { "enable": { "label": "啟用自動檢查點", - "description": "啟用後,Roo 將在工作執行期間自動建立檢查點,使審核變更或回到早期狀態變得容易。" + "description": "啟用後,Roo 將在工作執行期間自動建立檢查點,使審核變更或回到早期狀態變得容易。 <0>瞭解更多" } }, "notifications": { @@ -304,51 +312,63 @@ }, "maxReadFile": { "label": "檔案讀取自動截斷閾值", - "description": "當模型未指定起始/結束值時,Roo 讀取的行數。如果此數值小於檔案總行數,Roo 將產生程式碼定義的行號索引。特殊情況:-1 指示 Roo 讀取整個檔案(不建立索引),0 指示不讀取任何行並僅提供行索引以取得最小上下文。較低的值可最小化初始上下文使用,允許後續精確的行範圍讀取。明確指定起始/結束的請求不受此設定限制。", + "description": "當模型未指定起始/結束值時,Roo 讀取的行數。如果此數值小於檔案總行數,Roo 將產生程式碼定義的行號索引。特殊情況:-1 指示 Roo 讀取整個檔案(不建立索引),0 指示不讀取任何行並僅提供行索引以取得最小上下文。較低的值可最小化初始上下文使用,允許後續精確的行範圍讀取。明確指定起始/結束的請求不受此設定限制。 <0>瞭解更多", "lines": "行", "always_full_read": "始終讀取整個檔案" } }, "terminal": { + "basic": { + "label": "終端機設定:基本", + "description": "基本終端機設定" + }, + "advanced": { + "label": "終端機設定:進階", + "description": "以下選項可能需要重新啟動終端機才能套用設定" + }, "outputLineLimit": { "label": "終端機輸出行數限制", - "description": "執行命令時終端機輸出的最大行數。超過此限制時,會從中間移除多餘的行數,以節省 token 用量。" + "description": "執行命令時終端機輸出的最大行數。超過此限制時,會從中間移除多餘的行數,以節省 token 用量。 <0>瞭解更多" }, "shellIntegrationTimeout": { "label": "終端機 Shell 整合逾時", - "description": "執行命令前等待 Shell 整合初始化的最長時間。如果您的 Shell 啟動較慢,且終端機出現「Shell 整合無法使用」的錯誤訊息,可能需要提高此數值。" + "description": "執行命令前等待 Shell 整合初始化的最長時間。如果您的 Shell 啟動較慢,且終端機出現「Shell 整合無法使用」的錯誤訊息,可能需要提高此數值。 <0>瞭解更多" }, "shellIntegrationDisabled": { "label": "停用終端機 Shell 整合", - "description": "如果終端機指令無法正常運作或看到 'Shell Integration Unavailable' 錯誤,請啟用此項。這會使用較簡單的方法執行指令,繞過一些進階終端機功能。" - }, - "compressProgressBar": { - "label": "壓縮進度條輸出", - "description": "啟用後,將處理包含歸位字元 (\\r) 的終端機輸出,模擬真實終端機顯示內容的方式。這會移除進度條的中間狀態,只保留最終狀態,為更重要的資訊節省上下文空間。" - }, - "zdotdir": { - "label": "啟用 ZDOTDIR 處理", - "description": "啟用後將建立暫存目錄用於 ZDOTDIR,以正確處理 zsh shell 整合。這確保 VSCode shell 整合能與 zsh 正常運作,同時保留您的 zsh 設定。(實驗性)" + "description": "如果終端機指令無法正常運作或看到 'Shell Integration Unavailable' 錯誤,請啟用此項。這會使用較簡單的方法執行指令,繞過一些進階終端機功能。 <0>瞭解更多" }, "commandDelay": { "label": "終端機命令延遲", - "description": "命令執行後添加的延遲時間(毫秒)。預設值為 0 時完全停用延遲。這可以幫助確保在有計時問題的終端機中完整擷取命令輸出。在大多數終端機中,這是透過設定 `PROMPT_COMMAND='sleep N'` 實現的,而 PowerShell 會在每個命令結尾加入 `start-sleep`。最初是為了解決 VSCode 錯誤#237208,現在可能不再需要。" + "description": "命令執行後添加的延遲時間(毫秒)。預設值為 0 時完全停用延遲。這可以幫助確保在有計時問題的終端機中完整擷取命令輸出。在大多數終端機中,這是透過設定 `PROMPT_COMMAND='sleep N'` 實現的,而 PowerShell 會在每個命令結尾加入 `start-sleep`。最初是為了解決 VSCode 錯誤#237208,現在可能不再需要。 <0>瞭解更多" + }, + "compressProgressBar": { + "label": "壓縮進度條輸出", + "description": "啟用後,將處理包含歸位字元 (\\r) 的終端機輸出,模擬真實終端機顯示內容的方式。這會移除進度條的中間狀態,只保留最終狀態,為更重要的資訊節省上下文空間。 <0>瞭解更多" }, "powershellCounter": { "label": "啟用 PowerShell 計數器解決方案", - "description": "啟用後,會在 PowerShell 命令中加入計數器以確保命令正確執行。這有助於解決可能存在輸出擷取問題的 PowerShell 終端機。" + "description": "啟用後,會在 PowerShell 命令中加入計數器以確保命令正確執行。這有助於解決可能存在輸出擷取問題的 PowerShell 終端機。 <0>瞭解更多" }, "zshClearEolMark": { "label": "清除 ZSH 行尾標記", - "description": "啟用後,透過設定 PROMPT_EOL_MARK='' 清除 ZSH 行尾標記。這可以防止命令輸出以特殊字元(如 '%')結尾時的解析問題。" + "description": "啟用後,透過設定 PROMPT_EOL_MARK='' 清除 ZSH 行尾標記。這可以防止命令輸出以特殊字元(如 '%')結尾時的解析問題。 <0>瞭解更多" }, "zshOhMy": { "label": "啟用 Oh My Zsh 整合", - "description": "啟用後,設定 ITERM_SHELL_INTEGRATION_INSTALLED=Yes 以啟用 Oh My Zsh shell 整合功能。套用此設定可能需要重新啟動 IDE。(實驗性)" + "description": "啟用後,設定 ITERM_SHELL_INTEGRATION_INSTALLED=Yes 以啟用 Oh My Zsh shell 整合功能。套用此設定可能需要重新啟動 IDE。 <0>瞭解更多" }, "zshP10k": { "label": "啟用 Powerlevel10k 整合", - "description": "啟用後,設定 POWERLEVEL9K_TERM_SHELL_INTEGRATION=true 以啟用 Powerlevel10k shell 整合功能。(實驗性)" + "description": "啟用後,設定 POWERLEVEL9K_TERM_SHELL_INTEGRATION=true 以啟用 Powerlevel10k shell 整合功能。 <0>瞭解更多" + }, + "zdotdir": { + "label": "啟用 ZDOTDIR 處理", + "description": "啟用後將建立暫存目錄用於 ZDOTDIR,以正確處理 zsh shell 整合。這確保 VSCode shell 整合能與 zsh 正常運作,同時保留您的 zsh 設定。 <0>瞭解更多" + }, + "inheritEnv": { + "label": "繼承環境變數", + "description": "啟用後,終端機將從 VSCode 父程序繼承環境變數,如使用者設定檔中定義的 shell 整合設定。這直接切換 VSCode 全域設定 `terminal.integrated.inheritEnv`。 <0>瞭解更多" } }, "advanced": { @@ -376,6 +396,10 @@ }, "experimental": { "warning": "⚠️", + "AUTO_CONDENSE_CONTEXT": { + "name": "智慧壓縮上下文視窗", + "description": "當工作的上下文視窗接近填滿時,使用 LLM 呼叫來摘要過去的對話,而非捨棄舊訊息。注意:目前 UI 中顯示的 API 費用並未包含摘要的成本。" + }, "DIFF_STRATEGY_UNIFIED": { "name": "使用實驗性統一差異比對策略", "description": "啟用實驗性的統一差異比對策略。此策略可能減少因模型錯誤而導致的重試次數,但也可能導致意外行為或錯誤的編輯。請務必了解風險,並願意仔細檢查所有變更後再啟用。" @@ -398,8 +422,8 @@ } }, "promptCaching": { - "label": "啟用提示快取", - "description": "啟用後,Roo 將使用提示快取功能以降低成本。" + "label": "停用提示詞快取", + "description": "勾選後,Roo 將不會為此模型使用提示詞快取。" }, "temperature": { "useCustom": "使用自訂溫度", diff --git a/webview-ui/src/index.css b/webview-ui/src/index.css index 50603c6ac58..9468958c695 100644 --- a/webview-ui/src/index.css +++ b/webview-ui/src/index.css @@ -407,13 +407,20 @@ input[cmdk-input]:focus { } /* - * Use geometric precision for codicons to avoid blurriness + * Use geometric precision for codicons to avoid blurriness */ .codicon[class*="codicon-"] { text-rendering: geometricPrecision !important; } +/* + * Fix the color of in ChatView + */ + + a:focus { + outline: 1px solid var(--vscode-focusBorder); +} /* _-_-_-_-_-_-_-_- PEARAI CREATOR STYLES _-_-_-_-_-_-_-_- */ diff --git a/webview-ui/src/oauth/urls.ts b/webview-ui/src/oauth/urls.ts index 46a1815377c..205c19cf53e 100644 --- a/webview-ui/src/oauth/urls.ts +++ b/webview-ui/src/oauth/urls.ts @@ -1,6 +1,5 @@ export function getCallbackUrl(provider: string, uriScheme?: string) { - const callbackUrl = `${uriScheme || "vscode"}://rooveterinaryinc.roo-cline/${provider}` - return encodeURIComponent(callbackUrl) + return encodeURIComponent(`${uriScheme || "vscode"}://rooveterinaryinc.roo-cline/${provider}`) } export function getGlamaAuthUrl(uriScheme?: string) { diff --git a/webview-ui/src/stories/Welcome.mdx b/webview-ui/src/stories/Welcome.mdx index a89bd6864fa..de88649f80e 100644 --- a/webview-ui/src/stories/Welcome.mdx +++ b/webview-ui/src/stories/Welcome.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/blocks"; +import { Meta } from "@storybook/blocks" diff --git a/webview-ui/src/utils/TelemetryClient.ts b/webview-ui/src/utils/TelemetryClient.ts index 450f4da73db..dd8c6647548 100644 --- a/webview-ui/src/utils/TelemetryClient.ts +++ b/webview-ui/src/utils/TelemetryClient.ts @@ -17,6 +17,7 @@ class TelemetryClient { loaded: () => posthog.identify(distinctId), capture_pageview: false, capture_pageleave: false, + autocapture: false, }) } else { TelemetryClient.telemetryEnabled = false diff --git a/webview-ui/src/utils/__tests__/command-validation.test.ts b/webview-ui/src/utils/__tests__/command-validation.test.ts index c1c73548b9c..9d89beec465 100644 --- a/webview-ui/src/utils/__tests__/command-validation.test.ts +++ b/webview-ui/src/utils/__tests__/command-validation.test.ts @@ -1,3 +1,8 @@ +/* eslint-disable no-useless-escape */ +/* eslint-disable no-template-curly-in-string */ + +// npx jest src/utils/__tests__/command-validation.test.ts + import { parseCommand, isAllowedSingleCommand, validateCommand } from "../command-validation" describe("Command Validation", () => { @@ -119,3 +124,160 @@ describe("Command Validation", () => { }) }) }) + +/** + * Tests for the command-validation module + * + * These tests include a reproduction of a bug where the shell-quote library + * used in parseCommand throws an error when parsing commands that contain + * the Bash $RANDOM variable in array indexing expressions. + * + * Error: "Bad substitution: levels[$RANDOM" + * + * The issue occurs specifically with complex expressions like: + * ${levels[$RANDOM % ${#levels[@]}]} + */ +describe("command-validation", () => { + describe("parseCommand", () => { + it("should correctly parse simple commands", () => { + const result = parseCommand("echo hello") + expect(result).toEqual(["echo hello"]) + }) + + it("should correctly parse commands with chaining operators", () => { + const result = parseCommand("echo hello && echo world") + expect(result).toEqual(["echo hello", "echo world"]) + }) + + it("should correctly parse commands with quotes", () => { + const result = parseCommand('echo "hello world"') + expect(result).toEqual(['echo "hello world"']) + }) + + it("should correctly parse commands with redirections", () => { + const result = parseCommand("echo hello 2>&1") + expect(result).toEqual(["echo hello 2>&1"]) + }) + + it("should correctly parse commands with subshells", () => { + const result = parseCommand("echo $(date)") + expect(result).toEqual(["echo", "date"]) + }) + + it("should not throw an error when parsing commands with simple array indexing", () => { + // Simple array indexing works fine + const commandWithArrayIndex = "value=${array[$index]}" + + expect(() => { + parseCommand(commandWithArrayIndex) + }).not.toThrow() + }) + + it("should not throw an error when parsing commands with $RANDOM in array index", () => { + // This test reproduces the specific bug reported in the error + const commandWithRandom = "level=${levels[$RANDOM % ${#levels[@]}]}" + + expect(() => { + parseCommand(commandWithRandom) + }).not.toThrow() + }) + + it("should not throw an error with simple $RANDOM variable", () => { + // Simple $RANDOM usage works fine + const commandWithRandom = "echo $RANDOM" + + expect(() => { + parseCommand(commandWithRandom) + }).not.toThrow() + }) + + it("should not throw an error with simple array indexing using $RANDOM", () => { + // Simple array indexing with $RANDOM works fine + const commandWithRandomIndex = "echo ${array[$RANDOM]}" + + expect(() => { + parseCommand(commandWithRandomIndex) + }).not.toThrow() + }) + + it("should not throw an error with complex array indexing using $RANDOM and arithmetic", () => { + // This test reproduces the exact expression from the original error + const commandWithComplexRandom = "echo ${levels[$RANDOM % ${#levels[@]}]}" + + expect(() => { + parseCommand(commandWithComplexRandom) + }).not.toThrow("Bad substitution") + }) + + it("should not throw an error when parsing the full log generator command", () => { + // This is the exact command from the original error message + const logGeneratorCommand = `while true; do \\ + levels=(INFO WARN ERROR DEBUG); \\ + msgs=("User logged in" "Connection timeout" "Processing request" "Cache miss" "Database query"); \\ + level=\${levels[$RANDOM % \${#levels[@]}]}; \\ + msg=\${msgs[$RANDOM % \${#msgs[@]}]}; \\ + echo "\$(date '+%Y-%m-%d %H:%M:%S') [$level] $msg"; \\ + sleep 1; \\ +done` + + // This reproduces the original error + expect(() => { + parseCommand(logGeneratorCommand) + }).not.toThrow("Bad substitution: levels[$RANDOM") + }) + + it("should not throw an error when parsing just the problematic part", () => { + // This isolates just the part mentioned in the error message + const problematicPart = "level=${levels[$RANDOM" + + expect(() => { + parseCommand(problematicPart) + }).not.toThrow("Bad substitution") + }) + }) + + describe("validateCommand", () => { + it("should validate allowed commands", () => { + const result = validateCommand("echo hello", ["echo"]) + expect(result).toBe(true) + }) + + it("should reject disallowed commands", () => { + const result = validateCommand("rm -rf /", ["echo", "ls"]) + expect(result).toBe(false) + }) + + it("should not fail validation for commands with simple $RANDOM variable", () => { + const commandWithRandom = "echo $RANDOM" + + expect(() => { + validateCommand(commandWithRandom, ["echo"]) + }).not.toThrow() + }) + + it("should not fail validation for commands with simple array indexing using $RANDOM", () => { + const commandWithRandomIndex = "echo ${array[$RANDOM]}" + + expect(() => { + validateCommand(commandWithRandomIndex, ["echo"]) + }).not.toThrow() + }) + + it("should return false for the full log generator command due to subshell detection", () => { + // This is the exact command from the original error message + const logGeneratorCommand = `while true; do \\ + levels=(INFO WARN ERROR DEBUG); \\ + msgs=("User logged in" "Connection timeout" "Processing request" "Cache miss" "Database query"); \\ + level=\${levels[$RANDOM % \${#levels[@]}]}; \\ + msg=\${msgs[$RANDOM % \${#msgs[@]}]}; \\ + echo "\$(date '+%Y-%m-%d %H:%M:%S') [$level] $msg"; \\ + sleep 1; \\ +done` + + // validateCommand should return false due to subshell detection + // without throwing an error + const result = validateCommand(logGeneratorCommand, ["while"]) + expect(result).toBe(false) + }) + }) +}) diff --git a/webview-ui/src/utils/command-validation.ts b/webview-ui/src/utils/command-validation.ts index 3f8fd593b19..7ad21b1675c 100644 --- a/webview-ui/src/utils/command-validation.ts +++ b/webview-ui/src/utils/command-validation.ts @@ -15,15 +15,25 @@ type ShellToken = string | { op: string } | { command: string } export function parseCommand(command: string): string[] { if (!command?.trim()) return [] - // First handle PowerShell redirections by temporarily replacing them + // Storage for replaced content const redirections: string[] = [] + const subshells: string[] = [] + const quotes: string[] = [] + const arrayIndexing: string[] = [] + + // First handle PowerShell redirections by temporarily replacing them let processedCommand = command.replace(/\d*>&\d*/g, (match) => { redirections.push(match) return `__REDIR_${redirections.length - 1}__` }) + // Handle array indexing expressions: ${array[...]} pattern and partial expressions + processedCommand = processedCommand.replace(/\$\{[^}]*\[[^\]]*(\]([^}]*\})?)?/g, (match) => { + arrayIndexing.push(match) + return `__ARRAY_${arrayIndexing.length - 1}__` + }) + // Then handle subshell commands - const subshells: string[] = [] processedCommand = processedCommand .replace(/\$\((.*?)\)/g, (_, inner) => { subshells.push(inner.trim()) @@ -35,7 +45,6 @@ export function parseCommand(command: string): string[] { }) // Then handle quoted strings - const quotes: string[] = [] processedCommand = processedCommand.replace(/"[^"]*"/g, (match) => { quotes.push(match) return `__QUOTE_${quotes.length - 1}__` @@ -84,6 +93,8 @@ export function parseCommand(command: string): string[] { result = result.replace(/__QUOTE_(\d+)__/g, (_, i) => quotes[parseInt(i)]) // Restore redirections result = result.replace(/__REDIR_(\d+)__/g, (_, i) => redirections[parseInt(i)]) + // Restore array indexing expressions + result = result.replace(/__ARRAY_(\d+)__/g, (_, i) => arrayIndexing[parseInt(i)]) return result }) } diff --git a/webview-ui/src/utils/context-mentions.ts b/webview-ui/src/utils/context-mentions.ts index e783f4c6253..c57f9156c28 100644 --- a/webview-ui/src/utils/context-mentions.ts +++ b/webview-ui/src/utils/context-mentions.ts @@ -1,7 +1,7 @@ import { mentionRegex } from "@roo/shared/context-mentions" import { Fzf } from "fzf" import { ModeConfig } from "@roo/shared/modes" -import * as path from "path" + import { escapeSpaces } from "./path-mentions" export interface SearchResult { @@ -9,6 +9,11 @@ export interface SearchResult { type: "file" | "folder" label?: string } + +function getBasename(filepath: string): string { + return filepath.split("/").pop() || filepath +} + export function insertMention( text: string, position: number, @@ -130,13 +135,13 @@ export function getContextMenuOptions( type: ContextMenuOptionType.Mode, value: result.item.original.slug, label: result.item.original.name, - description: result.item.original.roleDefinition.split("\n")[0], + description: (result.item.original.whenToUse || result.item.original.roleDefinition).split("\n")[0], })) : modes.map((mode) => ({ type: ContextMenuOptionType.Mode, value: mode.slug, label: mode.name, - description: mode.roleDefinition.split("\n")[0], + description: (mode.whenToUse || mode.roleDefinition).split("\n")[0], })) return matchingModes.length > 0 ? matchingModes : [{ type: ContextMenuOptionType.NoResults }] @@ -254,7 +259,7 @@ export function getContextMenuOptions( // For display purposes, we don't escape spaces in the label or description const displayPath = formattedPath - const displayName = result.label || path.basename(result.path) + const displayName = result.label || getBasename(result.path) // We don't need to escape spaces here because the insertMention function // will handle that when the user selects a suggestion diff --git a/webview-ui/src/utils/docLinks.ts b/webview-ui/src/utils/docLinks.ts new file mode 100644 index 00000000000..5d8b517b866 --- /dev/null +++ b/webview-ui/src/utils/docLinks.ts @@ -0,0 +1,14 @@ +/** + * Utility for building Roo Code documentation links with UTM telemetry. + * + * @param path - The path after the docs root (no leading slash) + * @param campaign - The UTM campaign context (e.g. "welcome", "provider_docs", "tips", "error_tooltip") + * @returns The full docs URL with UTM parameters + */ +export function buildDocLink(path: string, campaign: string): string { + // Remove any leading slash from path + const cleanPath = path.replace(/^\//, "") + const [basePath, hash] = cleanPath.split("#") + const baseUrl = `https://docs.roocode.com/${basePath}?utm_source=extension&utm_medium=ide&utm_campaign=${encodeURIComponent(campaign)}` + return hash ? `${baseUrl}#${hash}` : baseUrl +} diff --git a/webview-ui/src/utils/model-utils.ts b/webview-ui/src/utils/model-utils.ts index 29adb11a963..255c25bedb6 100644 --- a/webview-ui/src/utils/model-utils.ts +++ b/webview-ui/src/utils/model-utils.ts @@ -100,7 +100,8 @@ export const calculateTokenDistribution = ( // Get the actual max tokens value from the model // If maxTokens is valid, use it, otherwise reserve 20% of the context window as a default - const reservedForOutput = maxTokens && maxTokens > 0 ? maxTokens : Math.ceil(safeContextWindow * 0.2) + const reservedForOutput = + maxTokens && maxTokens > 0 && maxTokens !== safeContextWindow ? maxTokens : Math.ceil(safeContextWindow * 0.2) // Calculate sizes directly without buffer display const availableSize = Math.max(0, safeContextWindow - safeContextTokens - reservedForOutput) diff --git a/webview-ui/src/utils/removeLeadingNonAlphanumeric.ts b/webview-ui/src/utils/removeLeadingNonAlphanumeric.ts new file mode 100644 index 00000000000..869ca293b06 --- /dev/null +++ b/webview-ui/src/utils/removeLeadingNonAlphanumeric.ts @@ -0,0 +1,10 @@ +// We need to remove certain leading characters from the path in order for our +// leading ellipses trick to work. +// However, we want to preserve all language characters (including CJK, +// Cyrillic, etc.) and only remove specific punctuation that might interfere +// with the ellipsis display. +// +// Only remove specific punctuation characters that might interfere with +// ellipsis display. Keep all language characters (including CJK, Cyrillic +// etc.) and numbers. +export const removeLeadingNonAlphanumeric = (path: string): string => path.replace(/^[/\\:*?"<>|]+/, "") diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 5c072edc0d8..0765fffedad 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -1,8 +1,8 @@ import i18next from "i18next" -import { ApiConfiguration, isRouterName, RouterModels } from "@roo/shared/api" +import { ProviderSettings, isRouterName, RouterModels } from "@roo/shared/api" -export function validateApiConfiguration(apiConfiguration: ApiConfiguration): string | undefined { +export function validateApiConfiguration(apiConfiguration: ProviderSettings): string | undefined { switch (apiConfiguration.apiProvider) { case "openrouter": if (!apiConfiguration.openRouterApiKey) { @@ -24,6 +24,11 @@ export function validateApiConfiguration(apiConfiguration: ApiConfiguration): st return i18next.t("settings:validation.apiKey") } break + case "litellm": + if (!apiConfiguration.litellmApiKey) { + return i18next.t("settings:validation.apiKey") + } + break case "anthropic": if (!apiConfiguration.apiKey) { return i18next.t("settings:validation.apiKey") @@ -113,7 +118,7 @@ export function validateBedrockArn(arn: string, region?: string) { return { isValid: true, arnRegion, errorMessage: undefined } } -export function validateModelId(apiConfiguration: ApiConfiguration, routerModels?: RouterModels): string | undefined { +export function validateModelId(apiConfiguration: ProviderSettings, routerModels?: RouterModels): string | undefined { const provider = apiConfiguration.apiProvider ?? "" if (!isRouterName(provider)) { @@ -135,6 +140,9 @@ export function validateModelId(apiConfiguration: ApiConfiguration, routerModels case "requesty": modelId = apiConfiguration.requestyModelId break + case "litellm": + modelId = apiConfiguration.litellmModelId + break } if (!modelId) { diff --git a/webview-ui/tsconfig.json b/webview-ui/tsconfig.json index cd00e126a66..14da5f31f5f 100644 --- a/webview-ui/tsconfig.json +++ b/webview-ui/tsconfig.json @@ -10,6 +10,8 @@ "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", + "sourceMap": true, + "inlineSources": false, "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, diff --git a/webview-ui/vite.config.ts b/webview-ui/vite.config.ts index 061a6dfd894..69163efc3b7 100644 --- a/webview-ui/vite.config.ts +++ b/webview-ui/vite.config.ts @@ -58,6 +58,7 @@ export default defineConfig({ build: { outDir: "build", reportCompressedSize: false, + sourcemap: true, rollupOptions: { output: { entryFileNames: `assets/[name].js`,