diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 7e140ec08c..489533de69 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -64,7 +64,7 @@ body: attributes: value: | --- - Optional (for contributors): You can stop here if you're just proposing the improvement. + Optional: You can stop here if you're just proposing the improvement. - type: textarea id: acceptance-criteria diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index e83e44cd66..0000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,75 +0,0 @@ - - -### Related GitHub Issue - - - -Closes: # - -### Roo Code Task Context (Optional) - - - -### Description - - - -### Test Procedure - - - -### Pre-Submission Checklist - - - -- [ ] **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. -- [ ] **Testing**: New and/or updated tests have been added to cover my changes (if applicable). -- [ ] **Documentation Impact**: I have considered if my changes require documentation updates (see "Documentation Updates" section below). -- [ ] **Contribution Guidelines**: I have read and agree to the [Contributor Guidelines](/CONTRIBUTING.md). - -### Screenshots / Videos - - - -### Documentation Updates - - - -### Additional Notes - - - -### Get in Touch - - diff --git a/.github/workflows/docs-pages.yml b/.github/workflows/docs-pages.yml new file mode 100644 index 0000000000..089ed8a68c --- /dev/null +++ b/.github/workflows/docs-pages.yml @@ -0,0 +1,55 @@ +name: Deploy docs to GitHub Pages + +on: + push: + branches: + - main + paths: + - "apps/docs/**" + - ".github/workflows/docs-pages.yml" + - ".github/actions/setup-node-pnpm/**" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + workflow_dispatch: + +concurrency: + group: docs-pages + cancel-in-progress: true + +permissions: + contents: read + pages: write + id-token: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Node.js and pnpm + uses: ./.github/actions/setup-node-pnpm + with: + install-args: "--frozen-lockfile" + - name: Run type check + run: pnpm --filter @roo-code/docs check-types + - name: Run lint + run: pnpm --filter @roo-code/docs lint + - name: Build docs + run: pnpm --filter @roo-code/docs build + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: apps/docs/build + + deploy: + runs-on: ubuntu-latest + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml deleted file mode 100644 index b99fd7659e..0000000000 --- a/.github/workflows/evals.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Evals - -on: - pull_request: - types: [labeled] - workflow_dispatch: - -env: - DOCKER_BUILDKIT: 1 - COMPOSE_DOCKER_CLI_BUILD: 1 - -jobs: - evals: - # Run if triggered manually or if PR has 'evals' label. - if: github.event_name == 'workflow_dispatch' || contains(github.event.label.name, 'evals') - runs-on: blacksmith-16vcpu-ubuntu-2404 - timeout-minutes: 45 - - defaults: - run: - working-directory: packages/evals - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Create environment - run: | - cat > .env.local << EOF - OPENROUTER_API_KEY=${{ secrets.OPENROUTER_API_KEY || 'test-key-for-build' }} - EOF - - cat > .env.development << EOF - NODE_ENV=development - DATABASE_URL=postgresql://postgres:password@db:5432/evals_development - REDIS_URL=redis://redis:6379 - HOST_EXECUTION_METHOD=docker - EOF - - - name: Build image - uses: docker/build-push-action@v6 - with: - context: . - file: packages/evals/Dockerfile.runner - tags: evals-runner:latest - cache-from: type=gha - cache-to: type=gha,mode=max - push: false - load: true - - - name: Tag image - run: docker tag evals-runner:latest evals-runner - - - name: Start containers - run: | - docker compose up -d db redis - timeout 60 bash -c 'until docker compose exec -T db pg_isready -U postgres; do sleep 2; done' - timeout 60 bash -c 'until docker compose exec -T redis redis-cli ping | grep -q PONG; do sleep 2; done' - docker compose run --rm runner sh -c 'nc -z db 5432 && echo "✓ Runner -> Database connection successful"' - docker compose run --rm runner sh -c 'nc -z redis 6379 && echo "✓ Runner -> Redis connection successful"' - docker compose run --rm runner docker ps - - - name: Run database migrations - run: docker compose run --rm runner pnpm --filter @roo-code/evals db:migrate - - - name: Run evals - run: docker compose run --rm runner pnpm --filter @roo-code/evals cli --ci - - - name: Cleanup - if: always() - run: docker compose down -v --remove-orphans diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml deleted file mode 100644 index 5709bdc10a..0000000000 --- a/.github/workflows/update-contributors.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Update Contributors # Refresh contrib.rocks image cache - -on: - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - -jobs: - refresh-contrib-cache: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Bump cacheBust in all README files - run: | - set -euo pipefail - TS="$(date +%s)" - # Target only the root README.md and localized READMEs under locales/*/README.md - mapfile -t FILES < <(git ls-files README.md 'locales/*/README.md' || true) - - if [ "${#FILES[@]}" -eq 0 ]; then - echo "No target README files found." >&2 - exit 1 - fi - - UPDATED=0 - for f in "${FILES[@]}"; do - if grep -q 'cacheBust=' "$f"; then - # Use portable sed in GNU environment of ubuntu-latest - sed -i -E "s/cacheBust=[0-9]+/cacheBust=${TS}/g" "$f" - echo "Updated cacheBust in $f" - UPDATED=1 - else - echo "Warning: cacheBust parameter not found in $f" >&2 - fi - done - - if [ "$UPDATED" -eq 0 ]; then - echo "No files were updated. Ensure READMEs embed contrib.rocks with cacheBust param." >&2 - exit 1 - fi - - - name: Detect changes - id: changes - run: | - if git diff --quiet; then - echo "changed=false" >> $GITHUB_OUTPUT - else - echo "changed=true" >> $GITHUB_OUTPUT - fi - - - name: Create Pull Request - if: steps.changes.outputs.changed == 'true' - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "docs: update contributors list [skip ci]" - committer: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" - branch: refresh-contrib-cache - delete-branch: true - title: "Refresh contrib.rocks image cache (all READMEs)" - body: | - Automated refresh of the contrib.rocks image cache by bumping the cacheBust parameter in README.md and locales/*/README.md. - base: main diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b25d32598..06432ae25d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1274,7 +1274,6 @@ - Reposition Add Image button inside ChatTextArea (thanks @roomote!) - Bring back a way to temporarily and globally pause auto-approve without losing your toggle state (thanks @brunobergher!) - Makes text area buttons appear only when there's text (thanks @brunobergher!) -- CONTRIBUTING.md tweaks and issue template rewrite (thanks @hannesrudolph!) - Bump axios from 1.9.0 to 1.12.0 (thanks @dependabot!) ## [3.28.2] - 2025-09-14 @@ -1734,7 +1733,6 @@ - Fix Claude model detection by name for API protocol selection (thanks @daniel-lxs!) - Move marketplace icon from overflow menu to top navigation - Optional setting to prevent completion with open todos -- Added YouTube to website footer (thanks @thill2323!) ## [3.23.14] - 2025-07-17 @@ -2128,7 +2126,6 @@ - Fix bug with context condensing in Amazon Bedrock - Fix UTF-8 encoding in ExecaTerminalProcess (thanks @mr-ryan-james!) - Set sidebar name bugfix (thanks @chrarnoldus!) -- Fix link to CONTRIBUTING.md in feature request template (thanks @cannuri!) - Add task metadata to Unbound and improve caching logic (thanks @pugazhendhi-m!) ## [3.19.0] - 2025-05-29 @@ -3096,7 +3093,6 @@ - Ask and Architect modes can now edit markdown files - Custom modes can now be restricted to specific file patterns (for example, a technical writer who can only edit markdown files 👋) - Support for configuring the Bedrock provider with AWS Profiles -- New Roo Code community Discord at https://roocode.com/discord! ## [3.2.8] @@ -3136,8 +3132,6 @@ - Create specialized assistants for any workflow - Just type "Create a new mode for " or visit the Prompts tab in the top menu to get started -Join us at https://www.reddit.com/r/RooCode to share your custom modes and be part of our next chapter! - ## [3.1.7] - DeepSeek-R1 support (thanks @philipnext!) @@ -3185,12 +3179,8 @@ Join us at https://www.reddit.com/r/RooCode to share your custom modes and be pa ## [3.0.1] -- Fix the reddit link and a small visual glitch in the chat input - ## [3.0.0] -- This release adds chat modes! Now you can ask Roo Code questions about system architecture or the codebase without immediately jumping into writing code. You can even assign different API configuration profiles to each mode if you prefer to use different models for thinking vs coding. Would love feedback in the new Roo Code Reddit! https://www.reddit.com/r/RooCode - ## [2.2.46] - Only parse @-mentions in user input (not in files) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 328bb5c1b2..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,90 +0,0 @@ -
- - -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) • [Bahasa Indonesia](locales/id/CODE_OF_CONDUCT.md) • [Italiano](locales/it/CODE_OF_CONDUCT.md) • [日本語](locales/ja/CODE_OF_CONDUCT.md) - - - - -[한국어](locales/ko/CODE_OF_CONDUCT.md) • [Nederlands](locales/nl/CODE_OF_CONDUCT.md) • [Polski](locales/pl/CODE_OF_CONDUCT.md) • [Português (BR)](locales/pt-BR/CODE_OF_CONDUCT.md) • [Русский](locales/ru/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 - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to make participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or - advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic - address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at support@roocode.com. All complaints -will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from [Cline's version][cline_coc] of the [Contributor Covenant][homepage], version 1.4, -available at 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 - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 869b59a16d..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,141 +0,0 @@ -
- - -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) • [Bahasa Indonesia](locales/id/CONTRIBUTING.md) • [Italiano](locales/it/CONTRIBUTING.md) • [日本語](locales/ja/CONTRIBUTING.md) - - - - -[한국어](locales/ko/CONTRIBUTING.md) • [Nederlands](locales/nl/CONTRIBUTING.md) • [Polski](locales/pl/CONTRIBUTING.md) • [Português (BR)](locales/pt-BR/CONTRIBUTING.md) • [Русский](locales/ru/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) - - -
- -# Contributing to Roo Code - -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. - -## Table of Contents - -- [Before You Contribute](#before-you-contribute) -- [Finding & Planning Your Contribution](#finding--planning-your-contribution) -- [Development & Submission Process](#development--submission-process) -- [Legal](#legal) - -## Before You Contribute - -### 1. Code of Conduct - -All contributors must adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md). - -### 2. Project Roadmap - -Our roadmap guides the project's direction. Align your contributions with these key goals: - -### Reliability First - -- 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. - -### Enhanced User Experience - -- Streamline the UI/UX for clarity and intuitiveness. -- Continuously improve the workflow to meet the high expectations developers have for daily-use tools. - -### Leading on Agent Performance - -- 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. - -Mention alignment with these areas in your PRs. - -### 3. Join the Roo Code Community - -- **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/RooCodeInc/projects/1). - -## Finding & Planning Your Contribution - -### Types of Contributions - -- **Bug Fixes:** Addressing code issues. -- **New Features:** Adding functionality. -- **Documentation:** Improving guides and clarity. - -### Issue-First Approach - -All contributions start with a GitHub Issue using our skinny templates. - -- **Check existing issues**: Search [GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues). -- **Create an issue** using: - - **Enhancements:** "Enhancement Request" template (plain language focused on user benefit). - - **Bugs:** "Bug Report" template (minimal repro + expected vs actual + version). -- **Want to work on it?** Comment "Claiming" on the issue and DM **Hannes Rudolph (`hrudolph`)** on [Discord](https://discord.gg/roocode) to get assigned. Assignment will be confirmed in the thread. -- **PRs must link to the issue.** Unlinked PRs may be closed. - -### Deciding What to Work On - -- Check the [GitHub Project](https://github.com/orgs/RooCodeInc/projects/1) for "Issue [Unassigned]" issues. -- For docs, visit [Roo Code Docs](https://github.com/RooCodeInc/Roo-Code-Docs). - -### Reporting Bugs - -- Check for existing reports first. -- Create a new bug using the ["Bug Report" template](https://github.com/RooCodeInc/Roo-Code/issues/new/choose) with: - - Clear, numbered reproduction steps - - Expected vs actual result - - Roo Code version (required); API provider/model if relevant -- **Security issues**: Report privately via [security advisories](https://github.com/RooCodeInc/Roo-Code/security/advisories/new). - -## Development & Submission Process - -### Development Setup - -1. **Fork & Clone:** - -``` -git clone https://github.com/YOUR_USERNAME/Roo-Code.git -``` - -2. **Install Dependencies:** - -``` -pnpm install -``` - -3. **Debugging:** Open with VS Code (`F5`). - -### Writing Code Guidelines - -- 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. - -### Submitting a Pull Request - -- Begin as a **Draft PR** if seeking early feedback. -- Clearly describe your changes following the Pull Request Template. -- Link the issue in the PR description/title (e.g., "Fixes #123"). -- Provide screenshots/videos for UI changes. -- Indicate if documentation updates are necessary. - -### Pull Request Policy - -- Must reference an assigned GitHub Issue. To get assigned: comment "Claiming" on the issue and DM **Hannes Rudolph (`hrudolph`)** on [Discord](https://discord.gg/roocode). Assignment will be confirmed in the thread. -- Unlinked PRs may be closed. -- PRs should pass CI tests, align with the roadmap, and have clear documentation. - -### Review Process - -- **Daily Triage:** Quick checks by maintainers. -- **Weekly In-depth Review:** Comprehensive assessment. -- **Iterate promptly** based on feedback. - -## Legal - -By contributing, you agree your contributions will be licensed under the Apache 2.0 License, consistent with Roo Code's licensing. diff --git a/PRIVACY.md b/PRIVACY.md index b08a1ec39c..353b8ae560 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -6,9 +6,9 @@ Roo Code respects your privacy and is committed to transparency about how we han ### **Where Your Data Goes (And Where It Doesn’t)** -- **Code & Files**: Roo Code accesses files on your local machine when needed for AI-assisted features. When you send commands to Roo Code, relevant files may be transmitted to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. If you select Roo Code Cloud as the model provider (proxy mode), your code may transit Roo Code servers only to forward it to the upstream provider. We do not store your code; it is deleted immediately after forwarding. Otherwise, your code is sent directly to the provider. AI providers may store data per their privacy policies. +- **Code & Files**: Roo Code accesses files on your local machine when needed for AI-assisted features. When you send commands to Roo Code, relevant files may be transmitted to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. AI providers may store data per their privacy policies. - **Commands**: Any commands executed through Roo Code happen on your local environment. However, when you use AI-powered features, the relevant code and context from your commands may be transmitted to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. We do not have access to or store this data, but AI providers may process it per their privacy policies. -- **Prompts & AI Requests**: When you use AI-powered features, your prompts and relevant project context are sent to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. We do not store or process this data. These AI providers have their own privacy policies and may store data per their terms of service. If you choose Roo Code Cloud as the provider (proxy mode), prompts may transit Roo Code servers only to forward them to the upstream model and are not stored. +- **Prompts & AI Requests**: When you use AI-powered features, your prompts and relevant project context are sent to your chosen AI model provider (e.g., OpenAI, Anthropic, OpenRouter) to generate responses. We do not store or process this data. These AI providers have their own privacy policies and may store data per their terms of service. - **API Keys & Credentials**: If you enter an API key (e.g., to connect an AI model), it is stored locally on your device and never sent to us or any third party, except the provider you have chosen. - **Telemetry (Usage Data)**: We collect anonymous feature usage and error data to help us improve Roo Code. This telemetry is powered by PostHog and includes your VS Code machine ID, feature usage patterns, and exception reports. This telemetry does **not** collect personally identifiable information, your code, or AI prompts. You can opt out of this telemetry at any time through the settings. diff --git a/README.md b/README.md index 498a847aeb..0346dfc457 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,11 @@

VS Code Marketplace X - YouTube - Join Discord - Join r/RooCode

-

- Get help fast → Join Discord • Prefer async? → Join r/RooCode -

- # Roo Code > Your AI-Powered Dev Team, Right in Your Editor -## What's New in v3.53.0 - -> ### The Roo Code plugin is not going away. -> -> You may have seen the [recent announcement](https://x.com/mattrubens/status/2046636598859559114) that Roo Code hit 3 million installs and the original team is going all-in on Roomote. We know that news was hard for a lot of you. This plugin means a lot to us and to you, and we hear you. -> -> The good news: **a community team has stepped up to carry Roo Code forward**, and we're working with them on an official handoff so the plugin you rely on keeps getting maintained and improved. - -**What's new in this release:** - -- Add GPT-5.5 support via the OpenAI Codex provider. -- Add Claude Opus 4.7 support on Vertex AI. -- Add previous checkpoint navigation controls in chat so you can step back through prior checkpoints more easily. -
🌐 Available languages @@ -49,7 +28,7 @@ - [简体中文](locales/zh-CN/README.md) - [繁體中文](locales/zh-TW/README.md) - ... -
+ --- @@ -75,115 +54,24 @@ Roo Code adapts to how you work: Learn more: [Using Modes](https://docs.roocode.com/basic-usage/using-modes) • [Custom Modes](https://docs.roocode.com/advanced-usage/custom-modes) -## Tutorial & Feature Videos - -
- -| | | | -| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -|
Installing Roo Code |
Configuring Profiles |
Codebase Indexing | -|
Custom Modes |
Checkpoints |
Context Management | - -
-

-More quick tutorial and feature videos... -

- ## Resources - **[Documentation](https://docs.roocode.com):** The official guide to installing, configuring, and mastering Roo Code. -- **[YouTube Channel](https://youtube.com/@roocodeyt?feature=shared):** Watch tutorials and see features in action. -- **[Discord Server](https://discord.gg/roocode):** Join the community for real-time help and discussion. -- **[Reddit Community](https://www.reddit.com/r/RooCode):** Share your experiences and see what others are building. - **[GitHub Issues](https://github.com/RooCodeInc/Roo-Code/issues):** Report bugs and track development. -- **[Feature Requests](https://github.com/RooCodeInc/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop):** Have an idea? Share it with the developers. - ---- - -## Local Setup & Development - -1. **Clone** the repo: - -```sh -git clone https://github.com/RooCodeInc/Roo-Code.git -``` - -2. **Install dependencies**: - -```sh -pnpm install -``` - -3. **Run the extension**: - -There are several ways to run the Roo Code extension: - -### Development Mode (F5) - -For active development, use VSCode's built-in debugging: - -Press `F5` (or go to **Run** → **Start Debugging**) in VSCode. This will open a new VSCode window with the Roo Code extension running. - -- Changes to the webview will appear immediately. -- Changes to the core extension will also hot reload automatically. - -### Automated VSIX Installation - -To build and install the extension as a VSIX package directly into VSCode: - -```sh -pnpm install:vsix [-y] [--editor=] -``` - -This command will: - -- Ask which editor command to use (code/cursor/code-insiders) - defaults to 'code' -- Uninstall any existing version of the extension. -- Build the latest VSIX package. -- Install the newly built VSIX. -- Prompt you to restart VS Code for changes to take effect. - -Options: - -- `-y`: Skip all confirmation prompts and use defaults -- `--editor=`: Specify the editor command (e.g., `--editor=cursor` or `--editor=code-insiders`) - -### Manual VSIX Installation - -If you prefer to install the VSIX package manually: - -1. First, build the VSIX package: - ```sh - pnpm vsix - ``` -2. A `.vsix` file will be generated in the `bin/` directory (e.g., `bin/roo-cline-.vsix`). -3. Install it manually using the VSCode CLI: - ```sh - code --install-extension bin/roo-cline-.vsix - ``` - ---- - -We use [changesets](https://github.com/changesets/changesets) for versioning and publishing. Check our `CHANGELOG.md` for release notes. --- ## Disclaimer +The Roo Code Extension was shut down on May 15th. + +- If you're looking for an alternative, check out [ZooCode](https://github.com/Zoo-Code-Org/Zoo-Code/) (a fork started by the Roo Code community) and [Cline](https://cline.bot/) (from where Roo Code originated). +- If you were a paying user and have billing questions, please write [billing@roocode.com](mailto:billing@roocode.com). + **Please note** that Roo Code, Inc does **not** make any representations or warranties regarding any code, models, or other tools provided or made available in connection with Roo Code, any associated third-party tools, or any resulting outputs. You assume **all risks** associated with the use of any such tools or outputs; such tools are provided on an **"AS IS"** and **"AS AVAILABLE"** basis. Such risks may include, without limitation, intellectual property infringement, cyber vulnerabilities or attacks, bias, inaccuracies, errors, defects, viruses, downtime, property loss or damage, and/or personal injury. You are solely responsible for your use of any such tools or outputs (including, without limitation, the legality, appropriateness, and results thereof). --- -## Contributing - -We love community contributions! Get started by reading our [CONTRIBUTING.md](CONTRIBUTING.md). - ---- - ## License [Apache 2.0 © 2025 Roo Code, Inc.](./LICENSE) - ---- - -**Enjoy Roo Code!** Whether you keep it on a short leash or let it roam autonomously, we can’t wait to see what you build. If you have questions or feature ideas, drop by our [Reddit community](https://www.reddit.com/r/RooCode/) or [Discord](https://discord.gg/roocode). Happy coding! diff --git a/apps/cli/README.md b/apps/cli/README.md index 8dec1f3a1c..6c9b73fa69 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -53,21 +53,6 @@ roo upgrade rm -rf ~/.roo/cli ~/.local/bin/roo ``` -### Development Installation - -For contributing or development: - -```bash -# From the monorepo root. -pnpm install - -# Build the main extension first. -pnpm --filter roo-cline bundle - -# Build the CLI. -pnpm --filter @roo-code/cli build -``` - ## Usage ### Interactive Mode (Default) @@ -130,81 +115,29 @@ printf '{"command":"start","requestId":"1","prompt":"1+1=?"}\n' | roo --print -- printf '{"command":"start","requestId":"1","taskId":"018f7fc8-7c96-7f7c-98aa-2ec4ff7f6d87","prompt":"1+1=?"}\n' | roo --print --stdin-prompt-stream --output-format stream-json ``` -### Roo Code Cloud Authentication - -To use Roo Code Cloud features (like the provider proxy), you need to authenticate: - -```bash -# Log in to Roo Code Cloud (opens browser) -roo auth login - -# Check authentication status -roo auth status - -# Log out -roo auth logout -``` - -The `auth login` command: - -1. Opens your browser to authenticate with Roo Code Cloud -2. Receives a secure token via localhost callback -3. Stores the token in `~/.config/roo/credentials.json` - -Tokens are valid for 90 days. The CLI will prompt you to re-authenticate when your token expires. - -**Authentication Flow:** - -``` -┌──────┐ ┌─────────┐ ┌───────────────┐ -│ CLI │ │ Browser │ │ Roo Code Cloud│ -└──┬───┘ └────┬────┘ └───────┬───────┘ - │ │ │ - │ Open auth URL │ │ - │─────────────────>│ │ - │ │ │ - │ │ Authenticate │ - │ │─────────────────────>│ - │ │ │ - │ │<─────────────────────│ - │ │ Token via callback │ - │<─────────────────│ │ - │ │ │ - │ Store token │ │ - │ │ │ -``` - ## Options -| Option | Description | Default | -| --------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------- | -| `[prompt]` | Your prompt (positional argument, optional) | None | -| `--prompt-file ` | Read prompt from a file instead of command line argument | None | -| `--create-with-session-id ` | Create a new task using the provided session ID (UUID) | None | -| `-w, --workspace ` | Workspace path to operate in | Current directory | -| `-p, --print` | Print response and exit (non-interactive mode) | `false` | -| `--stdin-prompt-stream` | Read NDJSON control commands from stdin (requires `--print`) | `false` | -| `-e, --extension ` | Path to the extension bundle directory | Auto-detected | -| `-d, --debug` | Enable debug output (includes detailed debug information, prompts, paths, etc) | `false` | -| `-a, --require-approval` | Require manual approval before actions execute | `false` | -| `-k, --api-key ` | API key for the LLM provider | From env var | -| `--provider ` | API provider (roo, anthropic, openai, openrouter, etc.) | `openrouter` (or `roo` if authenticated) | -| `-m, --model ` | Model to use | `anthropic/claude-opus-4.6` | -| `--mode ` | Mode to start in (code, architect, ask, debug, etc.) | `code` | -| `--terminal-shell ` | Absolute shell path for inline terminal command execution | Auto-detected shell | -| `-r, --reasoning-effort ` | Reasoning effort level (unspecified, disabled, none, minimal, low, medium, high, xhigh) | `medium` | -| `--consecutive-mistake-limit ` | Consecutive error/repetition limit before guidance prompt (`0` disables the limit) | `10` | -| `--ephemeral` | Run without persisting state (uses temporary storage) | `false` | -| `--oneshot` | Exit upon task completion | `false` | -| `--output-format ` | Output format with `--print`: `text`, `json`, or `stream-json` | `text` | - -## Auth Commands - -| Command | Description | -| ----------------- | ---------------------------------- | -| `roo auth login` | Authenticate with Roo Code Cloud | -| `roo auth logout` | Clear stored authentication token | -| `roo auth status` | Show current authentication status | +| Option | Description | Default | +| --------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------- | +| `[prompt]` | Your prompt (positional argument, optional) | None | +| `--prompt-file ` | Read prompt from a file instead of command line argument | None | +| `--create-with-session-id ` | Create a new task using the provided session ID (UUID) | None | +| `-w, --workspace ` | Workspace path to operate in | Current directory | +| `-p, --print` | Print response and exit (non-interactive mode) | `false` | +| `--stdin-prompt-stream` | Read NDJSON control commands from stdin (requires `--print`) | `false` | +| `-e, --extension ` | Path to the extension bundle directory | Auto-detected | +| `-d, --debug` | Enable debug output (includes detailed debug information, prompts, paths, etc) | `false` | +| `-a, --require-approval` | Require manual approval before actions execute | `false` | +| `-k, --api-key ` | API key for the LLM provider | From env var | +| `--provider ` | API provider (anthropic, openai, openrouter, etc.) | `openrouter` | +| `-m, --model ` | Model to use | `anthropic/claude-opus-4.6` | +| `--mode ` | Mode to start in (code, architect, ask, debug, etc.) | `code` | +| `--terminal-shell ` | Absolute shell path for inline terminal command execution | Auto-detected shell | +| `-r, --reasoning-effort ` | Reasoning effort level (unspecified, disabled, none, minimal, low, medium, high, xhigh) | `medium` | +| `--consecutive-mistake-limit ` | Consecutive error/repetition limit before guidance prompt (`0` disables the limit) | `10` | +| `--ephemeral` | Run without persisting state (uses temporary storage) | `false` | +| `--oneshot` | Exit upon task completion | `false` | +| `--output-format ` | Output format with `--print`: `text`, `json`, or `stream-json` | `text` | ## Environment Variables @@ -212,19 +145,12 @@ The CLI will look for API keys in environment variables if not provided via `--a | Provider | Environment Variable | | ----------------- | --------------------------- | -| roo | `ROO_API_KEY` | | anthropic | `ANTHROPIC_API_KEY` | | openai-native | `OPENAI_API_KEY` | | openrouter | `OPENROUTER_API_KEY` | | gemini | `GOOGLE_API_KEY` | | vercel-ai-gateway | `VERCEL_AI_GATEWAY_API_KEY` | -**Authentication Environment Variables:** - -| Variable | Description | -| ----------------- | -------------------------------------------------------------------- | -| `ROO_WEB_APP_URL` | Override the Roo Code Cloud URL (default: `https://app.roocode.com`) | - ## Architecture ``` @@ -268,7 +194,7 @@ The CLI will look for API keys in environment variables if not provided via `--a ```bash # Run directly from source (no build required) -pnpm dev --provider roo --api-key $ROO_API_KEY --print "Hello" +pnpm dev --provider openrouter --api-key $OPENROUTER_API_KEY --print "Hello" # Run tests pnpm test @@ -280,12 +206,6 @@ pnpm check-types pnpm lint ``` -By default the `start` script points `ROO_CODE_PROVIDER_URL` at `http://localhost:8080/proxy` for local development. To point at the production API instead, override the environment variable: - -```bash -ROO_CODE_PROVIDER_URL=https://api.roocode.com/proxy pnpm dev --provider roo --api-key $ROO_API_KEY --print "Hello" -``` - ## Releasing Official releases are created via the GitHub Actions workflow at `.github/workflows/cli-release.yml`. diff --git a/apps/cli/package.json b/apps/cli/package.json index 9276f17053..3920c6b75f 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -16,8 +16,8 @@ "test:integration": "tsx scripts/integration/run.ts", "build": "tsup", "build:extension": "pnpm --filter roo-cline bundle", - "dev": "ROO_AUTH_BASE_URL=https://app.roocode.com ROO_SDK_BASE_URL=https://cloud-api.roocode.com ROO_CODE_PROVIDER_URL=https://api.roocode.com/proxy tsx src/index.ts", - "dev:local": "ROO_AUTH_BASE_URL=http://localhost:3000 ROO_SDK_BASE_URL=http://localhost:3001 ROO_CODE_PROVIDER_URL=http://localhost:8080/proxy tsx src/index.ts", + "dev": "tsx src/index.ts", + "dev:local": "tsx src/index.ts", "clean": "rimraf dist .turbo" }, "dependencies": { diff --git a/apps/cli/scripts/integration/cases/create-with-session-id-resume-loads-correct-session.ts b/apps/cli/scripts/integration/cases/create-with-session-id-resume-loads-correct-session.ts index cbefd26525..ded1656e1e 100644 --- a/apps/cli/scripts/integration/cases/create-with-session-id-resume-loads-correct-session.ts +++ b/apps/cli/scripts/integration/cases/create-with-session-id-resume-loads-correct-session.ts @@ -88,7 +88,7 @@ async function createSessionWithCustomId( "dev", "--print", "--provider", - "roo", + "openrouter", "--output-format", "stream-json", "--workspace", @@ -148,7 +148,7 @@ async function resumeSessionAndSendMarker( "--print", "--stdin-prompt-stream", "--provider", - "roo", + "openrouter", "--output-format", "stream-json", "--workspace", diff --git a/apps/cli/scripts/integration/lib/stream-harness.ts b/apps/cli/scripts/integration/lib/stream-harness.ts index 73b756c7c3..2693103309 100644 --- a/apps/cli/scripts/integration/lib/stream-harness.ts +++ b/apps/cli/scripts/integration/lib/stream-harness.ts @@ -69,7 +69,7 @@ export async function runStreamCase(options: RunStreamCaseOptions): Promise) this.sendToExtension({ type: "updateSettings", updatedSettings: this.initialSettings }) diff --git a/apps/cli/src/commands/auth/index.ts b/apps/cli/src/commands/auth/index.ts deleted file mode 100644 index 52ae7673a7..0000000000 --- a/apps/cli/src/commands/auth/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./login.js" -export * from "./logout.js" -export * from "./status.js" diff --git a/apps/cli/src/commands/auth/login.ts b/apps/cli/src/commands/auth/login.ts deleted file mode 100644 index ab85385b0f..0000000000 --- a/apps/cli/src/commands/auth/login.ts +++ /dev/null @@ -1,177 +0,0 @@ -import http from "http" -import { randomBytes } from "crypto" -import net from "net" -import { exec } from "child_process" - -import { AUTH_BASE_URL } from "@/types/index.js" -import { saveToken } from "@/lib/storage/index.js" - -export interface LoginOptions { - timeout?: number - verbose?: boolean -} - -export type LoginResult = - | { - success: true - token: string - } - | { - success: false - error: string - } - -const LOCALHOST = "127.0.0.1" - -export async function login({ timeout = 5 * 60 * 1000, verbose = false }: LoginOptions = {}): Promise { - const state = randomBytes(16).toString("hex") - const port = await getAvailablePort() - const host = `http://${LOCALHOST}:${port}` - - if (verbose) { - console.log(`[Auth] Starting local callback server on port ${port}`) - } - - // Create promise that will be resolved when we receive the callback. - const tokenPromise = new Promise<{ token: string; state: string }>((resolve, reject) => { - const server = http.createServer((req, res) => { - const url = new URL(req.url!, host) - - if (url.pathname === "/callback") { - const receivedState = url.searchParams.get("state") - const token = url.searchParams.get("token") - const error = url.searchParams.get("error") - - if (error) { - const errorUrl = new URL(`${AUTH_BASE_URL}/cli/sign-in?error=error-in-callback`) - errorUrl.searchParams.set("message", error) - res.writeHead(302, { Location: errorUrl.toString() }) - res.end(() => { - server.close() - reject(new Error(error)) - }) - } else if (!token) { - const errorUrl = new URL(`${AUTH_BASE_URL}/cli/sign-in?error=missing-token`) - errorUrl.searchParams.set("message", "Missing token in callback") - res.writeHead(302, { Location: errorUrl.toString() }) - res.end(() => { - server.close() - reject(new Error("Missing token in callback")) - }) - } else if (receivedState !== state) { - const errorUrl = new URL(`${AUTH_BASE_URL}/cli/sign-in?error=invalid-state-parameter`) - errorUrl.searchParams.set("message", "Invalid state parameter") - res.writeHead(302, { Location: errorUrl.toString() }) - res.end(() => { - server.close() - reject(new Error("Invalid state parameter")) - }) - } else { - res.writeHead(302, { Location: `${AUTH_BASE_URL}/cli/sign-in?success=true` }) - res.end(() => { - server.close() - resolve({ token, state: receivedState }) - }) - } - } else { - res.writeHead(404, { "Content-Type": "text/plain" }) - res.end("Not found") - } - }) - - server.listen(port, LOCALHOST) - - const timeoutId = setTimeout(() => { - server.close() - reject(new Error("Authentication timed out")) - }, timeout) - - server.on("close", () => { - clearTimeout(timeoutId) - }) - }) - - const authUrl = new URL(`${AUTH_BASE_URL}/cli/sign-in`) - authUrl.searchParams.set("state", state) - authUrl.searchParams.set("callback", `${host}/callback`) - - console.log("Opening browser for authentication...") - console.log(`If the browser doesn't open, visit: ${authUrl.toString()}`) - - try { - await openBrowser(authUrl.toString()) - } catch (error) { - if (verbose) { - console.warn("[Auth] Failed to open browser automatically:", error) - } - - console.log("Please open the URL above in your browser manually.") - } - - try { - const { token } = await tokenPromise - await saveToken(token) - console.log("✓ Successfully authenticated!") - return { success: true, token } - } catch (error) { - const message = error instanceof Error ? error.message : String(error) - console.error(`✗ Authentication failed: ${message}`) - return { success: false, error: message } - } -} - -async function getAvailablePort(startPort = 49152, endPort = 65535): Promise { - return new Promise((resolve, reject) => { - const server = net.createServer() - let port = startPort - - const tryPort = () => { - server.once("error", (err: NodeJS.ErrnoException) => { - if (err.code === "EADDRINUSE" && port < endPort) { - port++ - tryPort() - } else { - reject(err) - } - }) - - server.once("listening", () => { - server.close(() => { - resolve(port) - }) - }) - - server.listen(port, LOCALHOST) - } - - tryPort() - }) -} - -function openBrowser(url: string): Promise { - return new Promise((resolve, reject) => { - const platform = process.platform - let command: string - - switch (platform) { - case "darwin": - command = `open "${url}"` - break - case "win32": - command = `start "" "${url}"` - break - default: - // Linux and other Unix-like systems. - command = `xdg-open "${url}"` - break - } - - exec(command, (error) => { - if (error) { - reject(error) - } else { - resolve() - } - }) - }) -} diff --git a/apps/cli/src/commands/auth/logout.ts b/apps/cli/src/commands/auth/logout.ts deleted file mode 100644 index 61c3cb37a4..0000000000 --- a/apps/cli/src/commands/auth/logout.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { clearToken, hasToken, getCredentialsPath } from "@/lib/storage/index.js" - -export interface LogoutOptions { - verbose?: boolean -} - -export interface LogoutResult { - success: boolean - wasLoggedIn: boolean -} - -export async function logout({ verbose = false }: LogoutOptions = {}): Promise { - const wasLoggedIn = await hasToken() - - if (!wasLoggedIn) { - console.log("You are not currently logged in.") - return { success: true, wasLoggedIn: false } - } - - if (verbose) { - console.log(`[Auth] Removing credentials from ${getCredentialsPath()}`) - } - - await clearToken() - console.log("✓ Successfully logged out") - return { success: true, wasLoggedIn: true } -} diff --git a/apps/cli/src/commands/auth/status.ts b/apps/cli/src/commands/auth/status.ts deleted file mode 100644 index 9e81adfda8..0000000000 --- a/apps/cli/src/commands/auth/status.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { loadToken, loadCredentials, getCredentialsPath } from "@/lib/storage/index.js" -import { isTokenExpired, isTokenValid, getTokenExpirationDate } from "@/lib/auth/index.js" - -export interface StatusOptions { - verbose?: boolean -} - -export interface StatusResult { - authenticated: boolean - expired?: boolean - expiringSoon?: boolean - userId?: string - orgId?: string | null - expiresAt?: Date - createdAt?: Date -} - -export async function status(options: StatusOptions = {}): Promise { - const { verbose = false } = options - - const token = await loadToken() - - if (!token) { - console.log("✗ Not authenticated") - console.log("") - console.log("Run: roo auth login") - return { authenticated: false } - } - - const expiresAt = getTokenExpirationDate(token) - const expired = !isTokenValid(token) - const expiringSoon = isTokenExpired(token, 24 * 60 * 60) && !expired - - const credentials = await loadCredentials() - const createdAt = credentials?.createdAt ? new Date(credentials.createdAt) : undefined - - if (expired) { - console.log("✗ Authentication token expired") - console.log("") - console.log("Run: roo auth login") - - return { - authenticated: false, - expired: true, - expiresAt: expiresAt ?? undefined, - } - } - - if (expiringSoon) { - console.log("⚠ Expires soon; refresh with `roo auth login`") - } else { - console.log("✓ Authenticated") - } - - if (expiresAt) { - const remaining = getTimeRemaining(expiresAt) - console.log(` Expires: ${formatDate(expiresAt)} (${remaining})`) - } - - if (createdAt && verbose) { - console.log(` Created: ${formatDate(createdAt)}`) - } - - if (verbose) { - console.log(` Credentials: ${getCredentialsPath()}`) - } - - return { - authenticated: true, - expired: false, - expiringSoon, - expiresAt: expiresAt ?? undefined, - createdAt, - } -} - -function formatDate(date: Date): string { - return date.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }) -} - -function getTimeRemaining(date: Date): string { - const now = new Date() - const diff = date.getTime() - now.getTime() - - if (diff <= 0) { - return "expired" - } - - const days = Math.floor(diff / (1000 * 60 * 60 * 24)) - const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) - - if (days > 0) { - return `${days} day${days === 1 ? "" : "s"}` - } - - return `${hours} hour${hours === 1 ? "" : "s"}` -} diff --git a/apps/cli/src/commands/cli/list.ts b/apps/cli/src/commands/cli/list.ts index 31898c59cd..1489a5feb9 100644 --- a/apps/cli/src/commands/cli/list.ts +++ b/apps/cli/src/commands/cli/list.ts @@ -6,11 +6,10 @@ import pWaitFor from "p-wait-for" import type { TaskSessionEntry } from "@roo-code/core/cli" import type { Command, ModelRecord, WebviewMessage } from "@roo-code/types" -import { getProviderDefaultModelId } from "@roo-code/types" +import { openRouterDefaultModelId } from "@roo-code/types" import { ExtensionHost, type ExtensionHostOptions } from "@/agent/index.js" import { readWorkspaceTaskSessions } from "@/lib/task-history/index.js" -import { loadToken } from "@/lib/storage/index.js" import { getDefaultExtensionPath } from "@/lib/utils/extension.js" import { getApiKeyFromEnv } from "@/lib/utils/provider.js" import { isRecord } from "@/lib/utils/guards.js" @@ -106,14 +105,14 @@ function outputSessionsText(sessions: SessionLike[]): void { async function createListHost(options: BaseListOptions, hostOptions: ListHostOptions): Promise { const workspacePath = resolveWorkspacePath(options.workspace) const extensionPath = resolveExtensionPath(options.extension) - const apiKey = options.apiKey || (await loadToken()) || getApiKeyFromEnv("roo") + const apiKey = options.apiKey || getApiKeyFromEnv("openrouter") const extensionHostOptions: ExtensionHostOptions = { mode: "code", reasoningEffort: undefined, user: null, - provider: "roo", - model: getProviderDefaultModelId("roo"), + provider: "openrouter", + model: openRouterDefaultModelId, apiKey, workspacePath, extensionPath, @@ -215,26 +214,15 @@ function requestModes(host: ExtensionHost): Promise { }) } -function requestRooModels(host: ExtensionHost): Promise { - return requestFromExtension(host, "requestRooModels", (message) => { - if (message.type !== "singleRouterModelFetchResponse") { +function requestOpenRouterModels(host: ExtensionHost): Promise { + return requestFromExtension(host, "requestRouterModels", (message) => { + if (message.type !== "routerModels") { return undefined } - const values = isRecord(message.values) ? message.values : undefined - if (values?.provider !== "roo") { - return undefined - } - - if (message.success === false) { - const errorMessage = - typeof message.error === "string" && message.error.length > 0 - ? message.error - : "Failed to fetch Roo models" - throw new Error(errorMessage) - } - - return isRecord(values.models) ? (values.models as ModelRecord) : {} + const routerModels = isRecord(message.routerModels) ? message.routerModels : {} + const openRouterModels = routerModels.openrouter + return isRecord(openRouterModels) ? (openRouterModels as ModelRecord) : {} }) } @@ -299,7 +287,7 @@ export async function listModels(options: BaseListOptions): Promise { const format = parseFormat(options.format) await withHostAndSignalHandlers(options, { ephemeral: true }, async (host) => { - const models = await requestRooModels(host) + const models = await requestOpenRouterModels(host) if (format === "json") { outputJson({ models }) diff --git a/apps/cli/src/commands/cli/run.ts b/apps/cli/src/commands/cli/run.ts index 62760919e7..f16d41cab1 100644 --- a/apps/cli/src/commands/cli/run.ts +++ b/apps/cli/src/commands/cli/run.ts @@ -10,22 +10,17 @@ import { setLogger } from "@roo-code/vscode-shim" import { FlagOptions, isSupportedProvider, - OnboardingProviderChoice, supportedProviders, DEFAULT_FLAGS, REASONING_EFFORTS, - SDK_BASE_URL, OutputFormat, } from "@/types/index.js" import { isValidOutputFormat } from "@/types/json-events.js" import { JsonEventEmitter } from "@/agent/json-event-emitter.js" -import { createClient } from "@/lib/sdk/index.js" -import { loadToken, loadSettings } from "@/lib/storage/index.js" +import { loadSettings } from "@/lib/storage/index.js" import { readWorkspaceTaskSessions, resolveWorkspaceResumeSessionId } from "@/lib/task-history/index.js" -import { isRecord } from "@/lib/utils/guards.js" import { getEnvVarName, getApiKeyFromEnv } from "@/lib/utils/provider.js" -import { runOnboarding } from "@/lib/utils/onboarding.js" import { validateTerminalShellPath } from "@/lib/utils/shell.js" import { getDefaultExtensionPath } from "@/lib/utils/extension.js" import { isValidSessionId } from "@/lib/utils/session-id.js" @@ -36,7 +31,6 @@ import { isExpectedControlFlowError } from "./cancellation.js" import { runStdinStreamMode } from "./stdin-stream.js" const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const ROO_MODEL_WARMUP_TIMEOUT_MS = 10_000 const SIGNAL_ONLY_EXIT_KEEPALIVE_MS = 60_000 const STREAM_RESUME_WAIT_TIMEOUT_MS = 2_000 @@ -54,59 +48,6 @@ function normalizeError(error: unknown): Error { return error instanceof Error ? error : new Error(String(error)) } -async function warmRooModels(host: ExtensionHost): Promise { - await new Promise((resolve, reject) => { - let settled = false - - const cleanup = () => { - clearTimeout(timeoutId) - host.off("extensionWebviewMessage", onMessage) - } - - const finish = (fn: () => void) => { - if (settled) return - settled = true - cleanup() - fn() - } - - const onMessage = (message: unknown) => { - if (!isRecord(message)) { - return - } - - if (message.type !== "singleRouterModelFetchResponse") { - return - } - - const values = isRecord(message.values) ? message.values : undefined - - if (values?.provider !== "roo") { - return - } - - if (message.success === false) { - const errorMessage = - typeof message.error === "string" && message.error.length > 0 - ? message.error - : "failed to refresh Roo models" - - finish(() => reject(new Error(errorMessage))) - return - } - - finish(() => resolve()) - } - - const timeoutId = setTimeout(() => { - finish(() => reject(new Error(`timed out waiting for Roo models after ${ROO_MODEL_WARMUP_TIMEOUT_MS}ms`))) - }, ROO_MODEL_WARMUP_TIMEOUT_MS) - - host.on("extensionWebviewMessage", onMessage) - host.sendToExtension({ type: "requestRooModels" }) - }) -} - export async function run(promptArg: string | undefined, flagOptions: FlagOptions) { setLogger({ info: () => {}, @@ -169,19 +110,17 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption // Options - let rooToken = await loadToken() const settings = await loadSettings() const isTuiSupported = process.stdin.isTTY && process.stdout.isTTY const isTuiEnabled = !flagOptions.print && isTuiSupported - const isOnboardingEnabled = isTuiEnabled && !rooToken && !flagOptions.provider && !settings.provider // Determine effective values: CLI flags > settings file > DEFAULT_FLAGS. const effectiveMode = flagOptions.mode || settings.mode || DEFAULT_FLAGS.mode const effectiveModel = flagOptions.model || settings.model || DEFAULT_FLAGS.model const effectiveReasoningEffort = flagOptions.reasoningEffort || settings.reasoningEffort || DEFAULT_FLAGS.reasoningEffort - const effectiveProvider = flagOptions.provider ?? settings.provider ?? (rooToken ? "roo" : "openrouter") + const effectiveProvider = flagOptions.provider ?? settings.provider ?? "openrouter" const effectiveWorkspacePath = flagOptions.workspace ? path.resolve(flagOptions.workspace) : process.cwd() const legacyRequireApprovalFromSettings = settings.requireApproval ?? @@ -229,49 +168,6 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption terminalShell, } - // Roo Code Cloud Authentication - - if (isOnboardingEnabled) { - let { onboardingProviderChoice } = settings - - if (!onboardingProviderChoice) { - const { choice, token } = await runOnboarding() - onboardingProviderChoice = choice - rooToken = token ?? null - } - - if (onboardingProviderChoice === OnboardingProviderChoice.Roo) { - extensionHostOptions.provider = "roo" - } - } - - if (extensionHostOptions.provider === "roo") { - if (rooToken) { - try { - const client = createClient({ url: SDK_BASE_URL, authToken: rooToken }) - const me = await client.auth.me.query() - - if (me?.type !== "user") { - throw new Error("Invalid token") - } - - extensionHostOptions.apiKey = rooToken - extensionHostOptions.user = me.user - } catch { - // If an explicit API key was provided via flag or env var, fall through - // to the general API key resolution below instead of exiting. - if (!flagOptions.apiKey && !getApiKeyFromEnv(extensionHostOptions.provider)) { - console.error("[CLI] Your Roo Code Router token is not valid.") - console.error("[CLI] Please run: roo auth login") - console.error("[CLI] Or use --api-key or set ROO_API_KEY to provide your own API key.") - process.exit(1) - } - } - } - // If no rooToken, fall through to the general API key resolution below - // which will check flagOptions.apiKey and ROO_API_KEY env var. - } - // Validations // TODO: Validate the API key for the chosen provider. // TODO: Validate the model for the chosen provider. @@ -287,18 +183,8 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption extensionHostOptions.apiKey || flagOptions.apiKey || getApiKeyFromEnv(extensionHostOptions.provider) if (!extensionHostOptions.apiKey) { - if (extensionHostOptions.provider === "roo") { - console.error("[CLI] Error: Authentication with Roo Code Cloud failed or was cancelled.") - console.error("[CLI] Please run: roo auth login") - console.error("[CLI] Or use --api-key to provide your own API key.") - } else { - console.error( - `[CLI] Error: No API key provided. Use --api-key or set the appropriate environment variable.`, - ) - console.error( - `[CLI] For ${extensionHostOptions.provider}, set ${getEnvVarName(extensionHostOptions.provider)}`, - ) - } + console.error(`[CLI] Error: No API key provided. Use --api-key or set the appropriate environment variable.`) + console.error(`[CLI] For ${extensionHostOptions.provider}, set ${getEnvVarName(extensionHostOptions.provider)}`) process.exit(1) } @@ -606,16 +492,6 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption try { await host.activate() - if (extensionHostOptions.provider === "roo") { - try { - await warmRooModels(host) - } catch (warmupError) { - if (flagOptions.debug) { - const message = warmupError instanceof Error ? warmupError.message : String(warmupError) - console.error(`[CLI] Warning: Roo model warmup failed: ${message}`) - } - } - } if (jsonEmitter) { jsonEmitter.attachToClient(host.client) diff --git a/apps/cli/src/commands/index.ts b/apps/cli/src/commands/index.ts index 717a7040ef..54fb955464 100644 --- a/apps/cli/src/commands/index.ts +++ b/apps/cli/src/commands/index.ts @@ -1,2 +1 @@ -export * from "./auth/index.js" export * from "./cli/index.js" diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 2805e6c909..79a4a72475 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -2,17 +2,7 @@ import { Command } from "commander" import { DEFAULT_FLAGS } from "@/types/constants.js" import { VERSION } from "@/lib/utils/version.js" -import { - run, - login, - logout, - status, - listCommands, - listModes, - listModels, - listSessions, - upgrade, -} from "@/commands/index.js" +import { run, listCommands, listModes, listModels, listSessions, upgrade } from "@/commands/index.js" const program = new Command() @@ -45,7 +35,7 @@ program .option("-d, --debug", "Enable debug output (includes detailed debug information)", false) .option("-a, --require-approval", "Require manual approval for actions", false) .option("-k, --api-key ", "API key for the LLM provider") - .option("--provider ", "API provider (roo, anthropic, openai, openrouter, etc.)") + .option("--provider ", "API provider (anthropic, openai, openrouter, etc.)") .option("-m, --model ", "Model to use", DEFAULT_FLAGS.model) .option("--mode ", "Mode to start in (code, architect, ask, debug, etc.)", DEFAULT_FLAGS.mode) .option("--terminal-shell ", "Absolute path to shell executable for inline terminal commands") @@ -79,7 +69,7 @@ const applyListOptions = (command: Command) => command .option("-w, --workspace ", "Workspace directory path (defaults to current working directory)") .option("-e, --extension ", "Path to the extension bundle directory") - .option("-k, --api-key ", "Roo API key (falls back to saved login/session token)") + .option("-k, --api-key ", "API key for the LLM provider") .option("--format ", 'Output format: "json" (default) or "text"', "json") .option("-d, --debug", "Enable debug output", false) @@ -117,7 +107,7 @@ applyListOptions(listCommand.command("modes").description("List available modes" }, ) -applyListOptions(listCommand.command("models").description("List available Roo models")).action( +applyListOptions(listCommand.command("models").description("List available models")).action( async (options: Parameters[0]) => { await runListAction(() => listModels(options)) }, @@ -136,33 +126,4 @@ program await runUpgradeAction(() => upgrade()) }) -const authCommand = program.command("auth").description("Manage authentication for Roo Code Cloud") - -authCommand - .command("login") - .description("Authenticate with Roo Code Cloud") - .option("-v, --verbose", "Enable verbose output", false) - .action(async (options: { verbose: boolean }) => { - const result = await login({ verbose: options.verbose }) - process.exit(result.success ? 0 : 1) - }) - -authCommand - .command("logout") - .description("Log out from Roo Code Cloud") - .option("-v, --verbose", "Enable verbose output", false) - .action(async (options: { verbose: boolean }) => { - const result = await logout({ verbose: options.verbose }) - process.exit(result.success ? 0 : 1) - }) - -authCommand - .command("status") - .description("Show authentication status") - .option("-v, --verbose", "Enable verbose output", false) - .action(async (options: { verbose: boolean }) => { - const result = await status({ verbose: options.verbose }) - process.exit(result.authenticated ? 0 : 1) - }) - program.parse() diff --git a/apps/cli/src/lib/auth/index.ts b/apps/cli/src/lib/auth/index.ts deleted file mode 100644 index ec1a459835..0000000000 --- a/apps/cli/src/lib/auth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./token.js" diff --git a/apps/cli/src/lib/auth/token.ts b/apps/cli/src/lib/auth/token.ts deleted file mode 100644 index 34365223a4..0000000000 --- a/apps/cli/src/lib/auth/token.ts +++ /dev/null @@ -1,61 +0,0 @@ -export interface DecodedToken { - iss: string - sub: string - exp: number - iat: number - nbf: number - v: number - r?: { - u?: string - o?: string - t: string - } -} - -function decodeToken(token: string): DecodedToken | null { - try { - const parts = token.split(".") - - if (parts.length !== 3) { - return null - } - - const payload = parts[1] - - if (!payload) { - return null - } - - const padded = payload + "=".repeat((4 - (payload.length % 4)) % 4) - const decoded = Buffer.from(padded, "base64url").toString("utf-8") - return JSON.parse(decoded) as DecodedToken - } catch { - return null - } -} - -export function isTokenExpired(token: string, bufferSeconds = 24 * 60 * 60): boolean { - const decoded = decodeToken(token) - - if (!decoded?.exp) { - return true - } - - const expiresAt = decoded.exp - const bufferTime = Math.floor(Date.now() / 1000) + bufferSeconds - return expiresAt < bufferTime -} - -export function isTokenValid(token: string): boolean { - return !isTokenExpired(token, 0) -} - -export function getTokenExpirationDate(token: string): Date | null { - const decoded = decodeToken(token) - - if (!decoded?.exp) { - return null - } - - return new Date(decoded.exp * 1000) -} diff --git a/apps/cli/src/lib/storage/__tests__/credentials.test.ts b/apps/cli/src/lib/storage/__tests__/credentials.test.ts deleted file mode 100644 index 574b1b6bf4..0000000000 --- a/apps/cli/src/lib/storage/__tests__/credentials.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import fs from "fs/promises" -import path from "path" - -// Use vi.hoisted to make the test directory available to the mock -// This must return the path synchronously since CREDENTIALS_FILE is computed at import time -const { getTestConfigDir } = vi.hoisted(() => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const os = require("os") - // eslint-disable-next-line @typescript-eslint/no-require-imports - const path = require("path") - const testRunId = Date.now().toString() - const testConfigDir = path.join(os.tmpdir(), `roo-cli-test-${testRunId}`) - return { getTestConfigDir: () => testConfigDir } -}) - -vi.mock("../config-dir.js", () => ({ - getConfigDir: getTestConfigDir, -})) - -// Import after mocking -import { saveToken, loadToken, loadCredentials, clearToken, hasToken, getCredentialsPath } from "../credentials.js" - -// Re-derive the test config dir for use in tests (must match the hoisted one) -const actualTestConfigDir = getTestConfigDir() - -describe("Token Storage", () => { - const expectedCredentialsFile = path.join(actualTestConfigDir, "cli-credentials.json") - - beforeEach(async () => { - // Clear test directory before each test - await fs.rm(actualTestConfigDir, { recursive: true, force: true }) - }) - - afterAll(async () => { - // Clean up test directory - await fs.rm(actualTestConfigDir, { recursive: true, force: true }) - }) - - describe("getCredentialsPath", () => { - it("should return the correct credentials file path", () => { - expect(getCredentialsPath()).toBe(expectedCredentialsFile) - }) - }) - - describe("saveToken", () => { - it("should save token to disk", async () => { - const token = "test-token-123" - await saveToken(token) - - const savedData = await fs.readFile(expectedCredentialsFile, "utf-8") - const credentials = JSON.parse(savedData) - - expect(credentials.token).toBe(token) - expect(credentials.createdAt).toBeDefined() - }) - - it("should save token with user info", async () => { - const token = "test-token-456" - await saveToken(token, { userId: "user_123", orgId: "org_456" }) - - const savedData = await fs.readFile(expectedCredentialsFile, "utf-8") - const credentials = JSON.parse(savedData) - - expect(credentials.token).toBe(token) - expect(credentials.userId).toBe("user_123") - expect(credentials.orgId).toBe("org_456") - }) - - it("should create config directory if it doesn't exist", async () => { - const token = "test-token-789" - await saveToken(token) - - const dirStats = await fs.stat(actualTestConfigDir) - expect(dirStats.isDirectory()).toBe(true) - }) - - // Unix file permissions don't apply on Windows - skip this test - it.skipIf(process.platform === "win32")("should set restrictive file permissions", async () => { - const token = "test-token-perms" - await saveToken(token) - - const stats = await fs.stat(expectedCredentialsFile) - // Check that only owner has read/write (mode 0o600) - const mode = stats.mode & 0o777 - expect(mode).toBe(0o600) - }) - }) - - describe("loadToken", () => { - it("should load saved token", async () => { - const token = "test-token-abc" - await saveToken(token) - - const loaded = await loadToken() - expect(loaded).toBe(token) - }) - - it("should return null if no token exists", async () => { - const loaded = await loadToken() - expect(loaded).toBeNull() - }) - }) - - describe("loadCredentials", () => { - it("should load full credentials", async () => { - const token = "test-token-def" - await saveToken(token, { userId: "user_789" }) - - const credentials = await loadCredentials() - - expect(credentials).not.toBeNull() - expect(credentials?.token).toBe(token) - expect(credentials?.userId).toBe("user_789") - expect(credentials?.createdAt).toBeDefined() - }) - - it("should return null if no credentials exist", async () => { - const credentials = await loadCredentials() - expect(credentials).toBeNull() - }) - }) - - describe("clearToken", () => { - it("should remove saved token", async () => { - const token = "test-token-ghi" - await saveToken(token) - - await clearToken() - - const loaded = await loadToken() - expect(loaded).toBeNull() - }) - - it("should not throw if no token exists", async () => { - await expect(clearToken()).resolves.not.toThrow() - }) - }) - - describe("hasToken", () => { - it("should return true if token exists", async () => { - await saveToken("test-token-jkl") - - const exists = await hasToken() - expect(exists).toBe(true) - }) - - it("should return false if no token exists", async () => { - const exists = await hasToken() - expect(exists).toBe(false) - }) - }) -}) diff --git a/apps/cli/src/lib/storage/__tests__/settings.test.ts b/apps/cli/src/lib/storage/__tests__/settings.test.ts index f19b5c3a25..3427a8a89d 100644 --- a/apps/cli/src/lib/storage/__tests__/settings.test.ts +++ b/apps/cli/src/lib/storage/__tests__/settings.test.ts @@ -51,7 +51,7 @@ describe("Settings Storage", () => { it("should load saved settings", async () => { const settingsData = { - onboardingProviderChoice: OnboardingProviderChoice.Roo, + onboardingProviderChoice: OnboardingProviderChoice.Byok, mode: "architect", provider: "anthropic" as const, model: "claude-sonnet-4-20250514", @@ -138,7 +138,7 @@ describe("Settings Storage", () => { describe("resetOnboarding", () => { it("should reset onboarding provider choice", async () => { - await saveSettings({ onboardingProviderChoice: OnboardingProviderChoice.Roo }) + await saveSettings({ onboardingProviderChoice: OnboardingProviderChoice.Byok }) await resetOnboarding() diff --git a/apps/cli/src/lib/storage/credentials.ts b/apps/cli/src/lib/storage/credentials.ts deleted file mode 100644 index b687111c16..0000000000 --- a/apps/cli/src/lib/storage/credentials.ts +++ /dev/null @@ -1,72 +0,0 @@ -import fs from "fs/promises" -import path from "path" - -import { getConfigDir } from "./index.js" - -const CREDENTIALS_FILE = path.join(getConfigDir(), "cli-credentials.json") - -export interface Credentials { - token: string - createdAt: string - userId?: string - orgId?: string -} - -export async function saveToken(token: string, options?: { userId?: string; orgId?: string }): Promise { - await fs.mkdir(getConfigDir(), { recursive: true }) - - const credentials: Credentials = { - token, - createdAt: new Date().toISOString(), - userId: options?.userId, - orgId: options?.orgId, - } - - await fs.writeFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), { - mode: 0o600, // Read/write for owner only - }) -} - -export async function loadToken(): Promise { - try { - const data = await fs.readFile(CREDENTIALS_FILE, "utf-8") - const credentials: Credentials = JSON.parse(data) - return credentials.token - } catch (error) { - if ((error as NodeJS.ErrnoException).code === "ENOENT") { - return null - } - throw error - } -} - -export async function loadCredentials(): Promise { - try { - const data = await fs.readFile(CREDENTIALS_FILE, "utf-8") - return JSON.parse(data) as Credentials - } catch (error) { - if ((error as NodeJS.ErrnoException).code === "ENOENT") { - return null - } - throw error - } -} - -export async function clearToken(): Promise { - try { - await fs.unlink(CREDENTIALS_FILE) - } catch (error) { - if ((error as NodeJS.ErrnoException).code !== "ENOENT") { - throw error - } - } -} - -export async function hasToken(): Promise { - const token = await loadToken() - return token !== null -} - -export function getCredentialsPath(): string { - return CREDENTIALS_FILE -} diff --git a/apps/cli/src/lib/storage/index.ts b/apps/cli/src/lib/storage/index.ts index 53424472c2..8924637643 100644 --- a/apps/cli/src/lib/storage/index.ts +++ b/apps/cli/src/lib/storage/index.ts @@ -1,4 +1,3 @@ export * from "./config-dir.js" export * from "./settings.js" -export * from "./credentials.js" export * from "./ephemeral.js" diff --git a/apps/cli/src/lib/utils/onboarding.ts b/apps/cli/src/lib/utils/onboarding.ts deleted file mode 100644 index 15da68f540..0000000000 --- a/apps/cli/src/lib/utils/onboarding.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { createElement } from "react" - -import { type OnboardingResult, OnboardingProviderChoice } from "@/types/index.js" -import { login } from "@/commands/index.js" -import { saveSettings } from "@/lib/storage/index.js" - -export async function runOnboarding(): Promise { - const { render } = await import("ink") - const { OnboardingScreen } = await import("../../ui/components/onboarding/index.js") - - return new Promise((resolve) => { - const onSelect = async (choice: OnboardingProviderChoice) => { - await saveSettings({ onboardingProviderChoice: choice }) - - app.unmount() - - console.log("") - - if (choice === OnboardingProviderChoice.Roo) { - const result = await login() - await saveSettings({ onboardingProviderChoice: choice }) - - resolve({ - choice: OnboardingProviderChoice.Roo, - token: result.success ? result.token : undefined, - skipped: false, - }) - } else { - console.log("Using your own API key.") - console.log("Set your API key via --api-key or environment variable.") - console.log("") - resolve({ choice: OnboardingProviderChoice.Byok, skipped: false }) - } - } - - const app = render(createElement(OnboardingScreen, { onSelect })) - }) -} diff --git a/apps/cli/src/lib/utils/provider.ts b/apps/cli/src/lib/utils/provider.ts index 64aec430c1..26beaf90c4 100644 --- a/apps/cli/src/lib/utils/provider.ts +++ b/apps/cli/src/lib/utils/provider.ts @@ -8,7 +8,6 @@ const envVarMap: Record = { gemini: "GOOGLE_API_KEY", openrouter: "OPENROUTER_API_KEY", "vercel-ai-gateway": "VERCEL_AI_GATEWAY_API_KEY", - roo: "ROO_API_KEY", } export function getEnvVarName(provider: SupportedProvider): string { @@ -48,10 +47,6 @@ export function getProviderSettings( if (apiKey) config.vercelAiGatewayApiKey = apiKey if (model) config.vercelAiGatewayModelId = model break - case "roo": - if (apiKey) config.rooApiKey = apiKey - if (model) config.apiModelId = model - break default: if (apiKey) config.apiKey = apiKey if (model) config.apiModelId = model diff --git a/apps/cli/src/types/constants.ts b/apps/cli/src/types/constants.ts index b291b5f90e..007cfa0783 100644 --- a/apps/cli/src/types/constants.ts +++ b/apps/cli/src/types/constants.ts @@ -21,7 +21,3 @@ export const ASCII_ROO = ` _,' ___ \\,\\ / \\\\ // \\\\ ,/' \`\\_,` - -export const AUTH_BASE_URL = process.env.ROO_AUTH_BASE_URL ?? "https://app.roocode.com" - -export const SDK_BASE_URL = process.env.ROO_SDK_BASE_URL ?? "https://cloud-api.roocode.com" diff --git a/apps/cli/src/types/types.ts b/apps/cli/src/types/types.ts index ecd3922aa1..0a9f3d2259 100644 --- a/apps/cli/src/types/types.ts +++ b/apps/cli/src/types/types.ts @@ -7,7 +7,6 @@ export const supportedProviders = [ "gemini", "openrouter", "vercel-ai-gateway", - "roo", ] as const satisfies ProviderName[] export type SupportedProvider = (typeof supportedProviders)[number] @@ -44,7 +43,6 @@ export type FlagOptions = { } export enum OnboardingProviderChoice { - Roo = "roo", Byok = "byok", } diff --git a/apps/cli/src/ui/components/onboarding/OnboardingScreen.tsx b/apps/cli/src/ui/components/onboarding/OnboardingScreen.tsx deleted file mode 100644 index 86c15f5b27..0000000000 --- a/apps/cli/src/ui/components/onboarding/OnboardingScreen.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Box, Text } from "ink" -import { Select } from "@inkjs/ui" - -import { OnboardingProviderChoice, ASCII_ROO } from "@/types/index.js" - -export interface OnboardingScreenProps { - onSelect: (choice: OnboardingProviderChoice) => void -} - -export function OnboardingScreen({ onSelect }: OnboardingScreenProps) { - return ( - - - {ASCII_ROO} - - Welcome! How would you like to connect to an LLM provider? - +
+ {searchBarShortcut && + searchBarShortcutHint && + (inputValue !== "" ? ( + + ) : ( + isBrowser && ( +
+ {isMac ? "⌘" : "ctrl"} + K +
+ ) + ))} +
+ ) +} diff --git a/apps/docs/src/theme/SearchPage/index.tsx b/apps/docs/src/theme/SearchPage/index.tsx new file mode 100644 index 0000000000..eed518ff07 --- /dev/null +++ b/apps/docs/src/theme/SearchPage/index.tsx @@ -0,0 +1,268 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* Custom SearchPage override that always pushes Release Notes results to the bottom */ +import React, { useCallback, useEffect, useMemo, useState } from "react" +import useDocusaurusContext from "@docusaurus/useDocusaurusContext" +import Layout from "@theme/Layout" +import Head from "@docusaurus/Head" +import Link from "@docusaurus/Link" +import { translate } from "@docusaurus/Translate" +import { usePluralForm } from "@docusaurus/theme-common" +import clsx from "clsx" + +// Plugin internals (mirrors original SearchPage) +import useSearchQuery from "@easyops-cn/docusaurus-search-local/dist/client/client/theme/hooks/useSearchQuery" +import { + fetchIndexesByWorker, + searchByWorker, +} from "@easyops-cn/docusaurus-search-local/dist/client/client/theme/searchByWorker" +import { SearchDocumentType } from "@easyops-cn/docusaurus-search-local/dist/client/shared/interfaces" +import { highlight } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/highlight" +import { highlightStemmed } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/highlightStemmed" +import { getStemmedPositions } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/getStemmedPositions" +import LoadingRing from "@easyops-cn/docusaurus-search-local/dist/client/client/theme/LoadingRing/LoadingRing" +import { concatDocumentPath } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/concatDocumentPath" +import { + Mark, + searchContextByPaths, + useAllContextsWithNoSearchContext, +} from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/proxiedGenerated" +import styles from "@easyops-cn/docusaurus-search-local/dist/client/client/theme/SearchPage/SearchPage.module.css" +import { normalizeContextByPath } from "@easyops-cn/docusaurus-search-local/dist/client/client/utils/normalizeContextByPath" + +// Ensure release notes always sink to bottom +function deprioritizeReleaseNotes(results: any[]) { + if (!Array.isArray(results)) return results + const isRN = (u: string) => typeof u === "string" && u.includes("/update-notes") + const nonRN: any[] = [] + const rn: any[] = [] + for (const r of results) { + const url = r?.document?.u ?? "" + ;(isRN(url) ? rn : nonRN).push(r) + } + return [...nonRN, ...rn] +} + +export default function SearchPage() { + return ( + + + + ) +} + +function SearchPageContent() { + const { + siteConfig: { baseUrl }, + i18n: { currentLocale }, + } = useDocusaurusContext() + const { selectMessage } = usePluralForm() + const { searchValue, searchContext, searchVersion, updateSearchPath, updateSearchContext } = useSearchQuery() as any + + const [searchQuery, setSearchQuery] = useState(searchValue) + const [searchResults, setSearchResults] = useState() + const versionUrl = `${baseUrl}${searchVersion}` + const pageTitle = useMemo( + () => + searchQuery + ? translate( + { + id: "theme.SearchPage.existingResultsTitle", + message: 'Search results for "{query}"', + description: "The search page title for non-empty query", + }, + { + query: searchQuery, + }, + ) + : translate({ + id: "theme.SearchPage.emptyResultsTitle", + message: "Search the documentation", + description: "The search page title for empty query", + }), + [searchQuery], + ) + + useEffect(() => { + updateSearchPath(searchQuery) + if (searchQuery) { + ;(async () => { + const results = await searchByWorker(versionUrl, searchContext, searchQuery, 100) + setSearchResults(deprioritizeReleaseNotes(results)) + })() + } else { + setSearchResults(undefined) + } + // eslint-disable-next-line react-hooks/exhaustive-deps -- updateSearchPath intentionally omitted (matches plugin behavior) + }, [searchQuery, versionUrl, searchContext]) + + const handleSearchInputChange = useCallback((e: React.ChangeEvent) => { + setSearchQuery(e.target.value) + }, []) + + useEffect(() => { + if (searchValue && searchValue !== searchQuery) { + setSearchQuery(searchValue) + } + // eslint-disable-next-line react-hooks/exhaustive-deps -- searchQuery intentionally omitted to prevent loops + }, [searchValue]) + + const [searchWorkerReady, setSearchWorkerReady] = useState(false) + useEffect(() => { + async function doFetchIndexes() { + if (!Array.isArray(searchContextByPaths) || searchContext || useAllContextsWithNoSearchContext) { + await fetchIndexesByWorker(versionUrl, searchContext) + } + setSearchWorkerReady(true) + } + doFetchIndexes() + }, [searchContext, versionUrl]) + + return ( + + + {/* Do not index search pages */} + + {pageTitle} + + +
+

{pageTitle}

+ +
+
+ +
+ {Array.isArray(searchContextByPaths) ? ( +
+ +
+ ) : null} +
+ + {!searchWorkerReady && searchQuery && ( +
+ +
+ )} + + {searchResults && + (searchResults.length > 0 ? ( +

+ {selectMessage( + searchResults.length, + translate( + { + id: "theme.SearchPage.documentsFound.plurals", + message: "1 document found|{count} documents found", + description: + 'Pluralized label for "{count} documents found". See https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html', + }, + { count: searchResults.length }, + ), + )} +

+ ) : process.env.NODE_ENV === "production" ? ( +

+ {translate({ + id: "theme.SearchPage.noResultsText", + message: "No documents were found", + description: "The paragraph for empty search result", + })} +

+ ) : ( +

⚠️ The search index is only available when you run docusaurus build!

+ ))} + +
+ {searchResults?.map((item) => )} +
+
+
+ ) +} + +function SearchResultItem({ searchResult: { document, type, page, tokens, metadata } }: { searchResult: any }) { + const isTitle = type === SearchDocumentType.Title + const isKeywords = type === SearchDocumentType.Keywords + const isDescription = type === SearchDocumentType.Description + const isDescriptionOrKeywords = isDescription || isKeywords + const isTitleRelated = isTitle || isDescriptionOrKeywords + const isContent = type === SearchDocumentType.Content + + const pathItems = (isTitle ? document.b : page.b).slice() + const articleTitle = isContent || isDescriptionOrKeywords ? document.s : document.t + if (!isTitleRelated) { + pathItems.push(page.t) + } + + let search = "" + if (Mark && tokens.length > 0) { + const params = new window.URLSearchParams() + for (const token of tokens) { + params.append("_highlight", token) + } + search = `?${params.toString()}` + } + + return ( +
+

+ +

+ + {pathItems.length > 0 &&

{concatDocumentPath(pathItems)}

} + + {(isContent || isDescription) && ( +

+ )} +

+ ) +} diff --git a/apps/docs/src/types/react-inert.d.ts b/apps/docs/src/types/react-inert.d.ts new file mode 100644 index 0000000000..1054211da3 --- /dev/null +++ b/apps/docs/src/types/react-inert.d.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import "react" + +declare module "react" { + interface HTMLAttributes { + inert?: boolean + } +} + +declare global { + namespace React { + interface HTMLAttributes { + inert?: boolean + } + } +} diff --git a/apps/docs/static/CNAME b/apps/docs/static/CNAME new file mode 100644 index 0000000000..e0a35f8b97 --- /dev/null +++ b/apps/docs/static/CNAME @@ -0,0 +1 @@ +roocode.github.io diff --git a/apps/docs/static/downloads/boomerang-tasks/roomodes.json b/apps/docs/static/downloads/boomerang-tasks/roomodes.json new file mode 100644 index 0000000000..c4aa8f39b2 --- /dev/null +++ b/apps/docs/static/downloads/boomerang-tasks/roomodes.json @@ -0,0 +1,12 @@ +{ + "customModes": [ + { + "slug": "boomerang-mode", + "name": "Boomerang Mode", + "roleDefinition": "You are Roo, a strategic workflow orchestrator who coordinates complex tasks by delegating them to appropriate specialized modes. You have a comprehensive understanding of each mode's capabilities and limitations, allowing you to effectively break down complex problems into discrete tasks that can be solved by different specialists.", + "customInstructions": "Your role is to coordinate complex workflows by delegating tasks to specialized modes. As an orchestrator, you should:\n\n1. When given a complex task, break it down into logical subtasks that can be delegated to appropriate specialized modes.\n\n2. For each subtask, use the `new_task` tool to delegate. Choose the most appropriate mode for the subtask's specific goal and provide comprehensive instructions in the `message` parameter. These instructions must include:\n * All necessary context from the parent task or previous subtasks required to complete the work.\n * A clearly defined scope, specifying exactly what the subtask should accomplish.\n * An explicit statement that the subtask should *only* perform the work outlined in these instructions and not deviate.\n * An instruction for the subtask to signal completion by using the `attempt_completion` tool, providing a concise yet thorough summary of the outcome in the `result` parameter, keeping in mind that this summary will be the source of truth used to keep track of what was completed on this project. \n * A statement that these specific instructions supersede any conflicting general instructions the subtask's mode might have.\n\n3. Track and manage the progress of all subtasks. When a subtask is completed, analyze its results and determine the next steps.\n\n4. Help the user understand how the different subtasks fit together in the overall workflow. Provide clear reasoning about why you're delegating specific tasks to specific modes.\n\n5. When all subtasks are completed, synthesize the results and provide a comprehensive overview of what was accomplished.\n\n6. Ask clarifying questions when necessary to better understand how to break down complex tasks effectively.\n\n7. Suggest improvements to the workflow based on the results of completed subtasks.\n\nUse subtasks to maintain clarity. If a request significantly shifts focus or requires a different expertise (mode), consider creating a subtask rather than overloading the current one.", + "groups": [], + "source": "global" + } + ] +} diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-1.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-1.png new file mode 100644 index 0000000000..4896bbf890 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-1.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-10.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-10.png new file mode 100644 index 0000000000..57d8f98d16 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-10.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-11.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-11.png new file mode 100644 index 0000000000..246e2dc190 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-11.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-12.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-12.png new file mode 100644 index 0000000000..c5ef1e9760 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-12.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-2.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-2.png new file mode 100644 index 0000000000..b3183e7bed Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-2.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-3.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-3.png new file mode 100644 index 0000000000..50acf92cdc Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-3.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-4.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-4.png new file mode 100644 index 0000000000..a699ad914b Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-4.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-5.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-5.png new file mode 100644 index 0000000000..c55abe2094 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-5.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-6.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-6.png new file mode 100644 index 0000000000..188947a5ea Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-6.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-7.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-7.png new file mode 100644 index 0000000000..0ad8b083c4 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-7.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-8.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-8.png new file mode 100644 index 0000000000..a93f7db6ec Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-8.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-9.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-9.png new file mode 100644 index 0000000000..110e26978e Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles-9.png differ diff --git a/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles.png b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles.png new file mode 100644 index 0000000000..5afdef8025 Binary files /dev/null and b/apps/docs/static/img/api-configuration-profiles/api-configuration-profiles.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-1.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-1.png new file mode 100644 index 0000000000..a10c0fce50 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-1.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-10.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-10.png new file mode 100644 index 0000000000..75d8b2834e Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-10.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-11.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-11.png new file mode 100644 index 0000000000..0b4e3ad731 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-11.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-12.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-12.png new file mode 100644 index 0000000000..b00c8b442c Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-12.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-13.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-13.png new file mode 100644 index 0000000000..d7afb227ad Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-13.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-14.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-14.png new file mode 100644 index 0000000000..0659869938 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-14.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-15.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-15.png new file mode 100644 index 0000000000..1daca635bb Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-15.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-16.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-16.png new file mode 100644 index 0000000000..021888ca6a Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-16.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-18.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-18.png new file mode 100644 index 0000000000..5166138cda Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-18.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-19.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-19.png new file mode 100644 index 0000000000..096f37a7d5 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-19.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-2.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-2.png new file mode 100644 index 0000000000..7b0661762c Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-2.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-20.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-20.png new file mode 100644 index 0000000000..59d3b311f8 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-20.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-21.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-21.png new file mode 100644 index 0000000000..c2c02e65e3 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-21.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-3.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-3.png new file mode 100644 index 0000000000..00a9b9d8f3 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-3.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-4.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-4.png new file mode 100644 index 0000000000..795e6d4ec8 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-4.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-5.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-5.png new file mode 100644 index 0000000000..67a78e503e Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-5.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-6.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-6.png new file mode 100644 index 0000000000..428b396367 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-6.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-7.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-7.png new file mode 100644 index 0000000000..b99d3a1218 Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-7.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-8.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-8.png new file mode 100644 index 0000000000..d495e474ce Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-8.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions-9.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-9.png new file mode 100644 index 0000000000..f5650cff7a Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions-9.png differ diff --git a/apps/docs/static/img/auto-approving-actions/auto-approving-actions.png b/apps/docs/static/img/auto-approving-actions/auto-approving-actions.png new file mode 100644 index 0000000000..ea4a7a426a Binary files /dev/null and b/apps/docs/static/img/auto-approving-actions/auto-approving-actions.png differ diff --git a/apps/docs/static/img/available-tools/available-tools.png b/apps/docs/static/img/available-tools/available-tools.png new file mode 100644 index 0000000000..ef90e554a8 Binary files /dev/null and b/apps/docs/static/img/available-tools/available-tools.png differ diff --git a/apps/docs/static/img/background-editing/background-editing.png b/apps/docs/static/img/background-editing/background-editing.png new file mode 100644 index 0000000000..47cd38d723 Binary files /dev/null and b/apps/docs/static/img/background-editing/background-editing.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-1.png b/apps/docs/static/img/browser-use/browser-use-1.png new file mode 100644 index 0000000000..0bd708fa12 Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-1.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-2.png b/apps/docs/static/img/browser-use/browser-use-2.png new file mode 100644 index 0000000000..9c3971bf6c Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-2.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-3.png b/apps/docs/static/img/browser-use/browser-use-3.png new file mode 100644 index 0000000000..fca7d812d5 Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-3.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-4.png b/apps/docs/static/img/browser-use/browser-use-4.png new file mode 100644 index 0000000000..4fd485168c Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-4.png differ diff --git a/apps/docs/static/img/browser-use/browser-use-5.png b/apps/docs/static/img/browser-use/browser-use-5.png new file mode 100644 index 0000000000..2a41216803 Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use-5.png differ diff --git a/apps/docs/static/img/browser-use/browser-use.png b/apps/docs/static/img/browser-use/browser-use.png new file mode 100644 index 0000000000..2861a97a04 Binary files /dev/null and b/apps/docs/static/img/browser-use/browser-use.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-1.png b/apps/docs/static/img/checkpoints/checkpoints-1.png new file mode 100644 index 0000000000..4acd43178e Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-1.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-2.png b/apps/docs/static/img/checkpoints/checkpoints-2.png new file mode 100644 index 0000000000..63719a10f6 Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-2.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-3.png b/apps/docs/static/img/checkpoints/checkpoints-3.png new file mode 100644 index 0000000000..e212b6190a Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-3.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-4.png b/apps/docs/static/img/checkpoints/checkpoints-4.png new file mode 100644 index 0000000000..7c4fc3e78b Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-4.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-6.png b/apps/docs/static/img/checkpoints/checkpoints-6.png new file mode 100644 index 0000000000..3dd7ccb9c7 Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-6.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-7.png b/apps/docs/static/img/checkpoints/checkpoints-7.png new file mode 100644 index 0000000000..e9dc5ac45a Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-7.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints-9.png b/apps/docs/static/img/checkpoints/checkpoints-9.png new file mode 100644 index 0000000000..b53afd3375 Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints-9.png differ diff --git a/apps/docs/static/img/checkpoints/checkpoints.png b/apps/docs/static/img/checkpoints/checkpoints.png new file mode 100644 index 0000000000..c9b7d65e43 Binary files /dev/null and b/apps/docs/static/img/checkpoints/checkpoints.png differ diff --git a/apps/docs/static/img/codebase-indexing/codebase-indexing-1.png b/apps/docs/static/img/codebase-indexing/codebase-indexing-1.png new file mode 100644 index 0000000000..1b2fa6478e Binary files /dev/null and b/apps/docs/static/img/codebase-indexing/codebase-indexing-1.png differ diff --git a/apps/docs/static/img/codebase-indexing/codebase-indexing-2.png b/apps/docs/static/img/codebase-indexing/codebase-indexing-2.png new file mode 100644 index 0000000000..ba40a69ef0 Binary files /dev/null and b/apps/docs/static/img/codebase-indexing/codebase-indexing-2.png differ diff --git a/apps/docs/static/img/codebase-indexing/codebase-indexing-3.png b/apps/docs/static/img/codebase-indexing/codebase-indexing-3.png new file mode 100644 index 0000000000..85393915e6 Binary files /dev/null and b/apps/docs/static/img/codebase-indexing/codebase-indexing-3.png differ diff --git a/apps/docs/static/img/codebase-indexing/codebase-indexing.png b/apps/docs/static/img/codebase-indexing/codebase-indexing.png new file mode 100644 index 0000000000..9b18c982d8 Binary files /dev/null and b/apps/docs/static/img/codebase-indexing/codebase-indexing.png differ diff --git a/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits-1.png b/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits-1.png new file mode 100644 index 0000000000..46402323d2 Binary files /dev/null and b/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits-1.png differ diff --git a/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits.png b/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits.png new file mode 100644 index 0000000000..59c7a4dffb Binary files /dev/null and b/apps/docs/static/img/concurrent-file-edits/concurrent-file-edits.png differ diff --git a/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-1.png b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-1.png new file mode 100644 index 0000000000..4d0fbdfff4 Binary files /dev/null and b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-1.png differ diff --git a/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-2.png b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-2.png new file mode 100644 index 0000000000..3fcb07530e Binary files /dev/null and b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads-2.png differ diff --git a/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads.png b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads.png new file mode 100644 index 0000000000..19b08dd514 Binary files /dev/null and b/apps/docs/static/img/concurrent-file-reads/concurrent-file-reads.png differ diff --git a/apps/docs/static/img/connecting-api-provider/connecting-api-provider-4.png b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-4.png new file mode 100644 index 0000000000..a60622d16d Binary files /dev/null and b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-4.png differ diff --git a/apps/docs/static/img/connecting-api-provider/connecting-api-provider-5.png b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-5.png new file mode 100644 index 0000000000..09f170a862 Binary files /dev/null and b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-5.png differ diff --git a/apps/docs/static/img/connecting-api-provider/connecting-api-provider-6.png b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-6.png new file mode 100644 index 0000000000..8ac7e53032 Binary files /dev/null and b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-6.png differ diff --git a/apps/docs/static/img/connecting-api-provider/connecting-api-provider-7.png b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-7.png new file mode 100644 index 0000000000..62fdfbb849 Binary files /dev/null and b/apps/docs/static/img/connecting-api-provider/connecting-api-provider-7.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-1.png b/apps/docs/static/img/context-mentions/context-mentions-1.png new file mode 100644 index 0000000000..9f8bdc4beb Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-1.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-2.png b/apps/docs/static/img/context-mentions/context-mentions-2.png new file mode 100644 index 0000000000..90ecbda70b Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-2.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-3.png b/apps/docs/static/img/context-mentions/context-mentions-3.png new file mode 100644 index 0000000000..20b2bfea46 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-3.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-4.png b/apps/docs/static/img/context-mentions/context-mentions-4.png new file mode 100644 index 0000000000..321c750105 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-4.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-5.png b/apps/docs/static/img/context-mentions/context-mentions-5.png new file mode 100644 index 0000000000..bd0367d149 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-5.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions-6.png b/apps/docs/static/img/context-mentions/context-mentions-6.png new file mode 100644 index 0000000000..e933613b00 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions-6.png differ diff --git a/apps/docs/static/img/context-mentions/context-mentions.png b/apps/docs/static/img/context-mentions/context-mentions.png new file mode 100644 index 0000000000..30afb6ccb8 Binary files /dev/null and b/apps/docs/static/img/context-mentions/context-mentions.png differ diff --git a/apps/docs/static/img/custom-instructions/custom-instructions-2.png b/apps/docs/static/img/custom-instructions/custom-instructions-2.png new file mode 100644 index 0000000000..6c92f3dedd Binary files /dev/null and b/apps/docs/static/img/custom-instructions/custom-instructions-2.png differ diff --git a/apps/docs/static/img/custom-instructions/custom-instructions.png b/apps/docs/static/img/custom-instructions/custom-instructions.png new file mode 100644 index 0000000000..16feb1c328 Binary files /dev/null and b/apps/docs/static/img/custom-instructions/custom-instructions.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-1.png b/apps/docs/static/img/custom-modes/custom-modes-1.png new file mode 100644 index 0000000000..c9ad7a989a Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-1.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-2.png b/apps/docs/static/img/custom-modes/custom-modes-2.png new file mode 100644 index 0000000000..8563536acc Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-2.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-3.png b/apps/docs/static/img/custom-modes/custom-modes-3.png new file mode 100644 index 0000000000..03f7306160 Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-3.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-4.png b/apps/docs/static/img/custom-modes/custom-modes-4.png new file mode 100644 index 0000000000..b274e59d7a Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-4.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-5.png b/apps/docs/static/img/custom-modes/custom-modes-5.png new file mode 100644 index 0000000000..fcdb067b4f Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-5.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes-6.png b/apps/docs/static/img/custom-modes/custom-modes-6.png new file mode 100644 index 0000000000..69b4221c21 Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes-6.png differ diff --git a/apps/docs/static/img/custom-modes/custom-modes.png b/apps/docs/static/img/custom-modes/custom-modes.png new file mode 100644 index 0000000000..18ba84c356 Binary files /dev/null and b/apps/docs/static/img/custom-modes/custom-modes.png differ diff --git a/apps/docs/static/img/custom-tools/custom-tools.png b/apps/docs/static/img/custom-tools/custom-tools.png new file mode 100644 index 0000000000..d696c64b1b Binary files /dev/null and b/apps/docs/static/img/custom-tools/custom-tools.png differ diff --git a/apps/docs/static/img/experimental-features/experimental-features.png b/apps/docs/static/img/experimental-features/experimental-features.png new file mode 100644 index 0000000000..d6bf53d74c Binary files /dev/null and b/apps/docs/static/img/experimental-features/experimental-features.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-1.png b/apps/docs/static/img/fast-edits/fast-edits-1.png new file mode 100644 index 0000000000..dc98d35677 Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-1.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-2.png b/apps/docs/static/img/fast-edits/fast-edits-2.png new file mode 100644 index 0000000000..ec569038d9 Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-2.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-3.png b/apps/docs/static/img/fast-edits/fast-edits-3.png new file mode 100644 index 0000000000..ae5842a016 Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-3.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-4.png b/apps/docs/static/img/fast-edits/fast-edits-4.png new file mode 100644 index 0000000000..b6b26784f3 Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-4.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits-5.png b/apps/docs/static/img/fast-edits/fast-edits-5.png new file mode 100644 index 0000000000..c465fd6dbc Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits-5.png differ diff --git a/apps/docs/static/img/fast-edits/fast-edits.png b/apps/docs/static/img/fast-edits/fast-edits.png new file mode 100644 index 0000000000..036beb4d3b Binary files /dev/null and b/apps/docs/static/img/fast-edits/fast-edits.png differ diff --git a/apps/docs/static/img/favicon.ico b/apps/docs/static/img/favicon.ico new file mode 100644 index 0000000000..b739db10d5 Binary files /dev/null and b/apps/docs/static/img/favicon.ico differ diff --git a/apps/docs/static/img/footgun-prompting/footgun-prompting-1.png b/apps/docs/static/img/footgun-prompting/footgun-prompting-1.png new file mode 100644 index 0000000000..359c9b487d Binary files /dev/null and b/apps/docs/static/img/footgun-prompting/footgun-prompting-1.png differ diff --git a/apps/docs/static/img/footgun-prompting/footgun-prompting.png b/apps/docs/static/img/footgun-prompting/footgun-prompting.png new file mode 100644 index 0000000000..0f19fa5c26 Binary files /dev/null and b/apps/docs/static/img/footgun-prompting/footgun-prompting.png differ diff --git a/apps/docs/static/img/how-tools-work/how-tools-work.png b/apps/docs/static/img/how-tools-work/how-tools-work.png new file mode 100644 index 0000000000..4227e61da0 Binary files /dev/null and b/apps/docs/static/img/how-tools-work/how-tools-work.png differ diff --git a/apps/docs/static/img/installing/installing-1.png b/apps/docs/static/img/installing/installing-1.png new file mode 100644 index 0000000000..d2baafe79f Binary files /dev/null and b/apps/docs/static/img/installing/installing-1.png differ diff --git a/apps/docs/static/img/installing/installing-2.png b/apps/docs/static/img/installing/installing-2.png new file mode 100644 index 0000000000..47a2e7f1bb Binary files /dev/null and b/apps/docs/static/img/installing/installing-2.png differ diff --git a/apps/docs/static/img/installing/installing-3.png b/apps/docs/static/img/installing/installing-3.png new file mode 100644 index 0000000000..679fe7c137 Binary files /dev/null and b/apps/docs/static/img/installing/installing-3.png differ diff --git a/apps/docs/static/img/installing/installing-4.png b/apps/docs/static/img/installing/installing-4.png new file mode 100644 index 0000000000..f2870524db Binary files /dev/null and b/apps/docs/static/img/installing/installing-4.png differ diff --git a/apps/docs/static/img/installing/installing-5.png b/apps/docs/static/img/installing/installing-5.png new file mode 100644 index 0000000000..237e8392af Binary files /dev/null and b/apps/docs/static/img/installing/installing-5.png differ diff --git a/apps/docs/static/img/installing/installing.png b/apps/docs/static/img/installing/installing.png new file mode 100644 index 0000000000..5e604e1679 Binary files /dev/null and b/apps/docs/static/img/installing/installing.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-1.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-1.png new file mode 100644 index 0000000000..a03f4cd2d6 Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-1.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-2.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-2.png new file mode 100644 index 0000000000..b13d7f5d7b Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-2.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-3.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-3.png new file mode 100644 index 0000000000..01a88ec391 Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-3.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-4.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-4.png new file mode 100644 index 0000000000..47df6416a6 Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation-4.png differ diff --git a/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation.png b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation.png new file mode 100644 index 0000000000..f7213e8733 Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensation/intelligent-context-condensation.png differ diff --git a/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-1.png b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-1.png new file mode 100644 index 0000000000..40a40aebcb Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-1.png differ diff --git a/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-2.png b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-2.png new file mode 100644 index 0000000000..8fee828d8c Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing-2.png differ diff --git a/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing.png b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing.png new file mode 100644 index 0000000000..294730821e Binary files /dev/null and b/apps/docs/static/img/intelligent-context-condensing/intelligent-context-condensing.png differ diff --git a/apps/docs/static/img/litellm/litellm.png b/apps/docs/static/img/litellm/litellm.png new file mode 100644 index 0000000000..3605db1f62 Binary files /dev/null and b/apps/docs/static/img/litellm/litellm.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-1.png b/apps/docs/static/img/marketplace/marketplace-1.png new file mode 100644 index 0000000000..6c7e0095b8 Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-1.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-2.png b/apps/docs/static/img/marketplace/marketplace-2.png new file mode 100644 index 0000000000..715705534c Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-2.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-3.png b/apps/docs/static/img/marketplace/marketplace-3.png new file mode 100644 index 0000000000..af222cc4c8 Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-3.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-4.png b/apps/docs/static/img/marketplace/marketplace-4.png new file mode 100644 index 0000000000..938f1fdd5e Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-4.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-5.png b/apps/docs/static/img/marketplace/marketplace-5.png new file mode 100644 index 0000000000..d1cde75943 Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-5.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-6.png b/apps/docs/static/img/marketplace/marketplace-6.png new file mode 100644 index 0000000000..e31b6e2926 Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-6.png differ diff --git a/apps/docs/static/img/marketplace/marketplace-7.png b/apps/docs/static/img/marketplace/marketplace-7.png new file mode 100644 index 0000000000..00f1e0028f Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace-7.png differ diff --git a/apps/docs/static/img/marketplace/marketplace.png b/apps/docs/static/img/marketplace/marketplace.png new file mode 100644 index 0000000000..97dcb2c600 Binary files /dev/null and b/apps/docs/static/img/marketplace/marketplace.png differ diff --git a/apps/docs/static/img/message-queueing/message-queueing.png b/apps/docs/static/img/message-queueing/message-queueing.png new file mode 100644 index 0000000000..2e4087ff88 Binary files /dev/null and b/apps/docs/static/img/message-queueing/message-queueing.png differ diff --git a/apps/docs/static/img/model-temperature/model-temperature.gif b/apps/docs/static/img/model-temperature/model-temperature.gif new file mode 100644 index 0000000000..0e6b7a9b16 Binary files /dev/null and b/apps/docs/static/img/model-temperature/model-temperature.gif differ diff --git a/apps/docs/static/img/model-temperature/model-temperature.png b/apps/docs/static/img/model-temperature/model-temperature.png new file mode 100644 index 0000000000..8915226115 Binary files /dev/null and b/apps/docs/static/img/model-temperature/model-temperature.png differ diff --git a/apps/docs/static/img/modes/modes-1.png b/apps/docs/static/img/modes/modes-1.png new file mode 100644 index 0000000000..465c98b566 Binary files /dev/null and b/apps/docs/static/img/modes/modes-1.png differ diff --git a/apps/docs/static/img/modes/modes-2.png b/apps/docs/static/img/modes/modes-2.png new file mode 100644 index 0000000000..eb98c17432 Binary files /dev/null and b/apps/docs/static/img/modes/modes-2.png differ diff --git a/apps/docs/static/img/modes/modes.png b/apps/docs/static/img/modes/modes.png new file mode 100644 index 0000000000..cc0cd8358b Binary files /dev/null and b/apps/docs/static/img/modes/modes.png differ diff --git a/apps/docs/static/img/power-steering/power-steering.png b/apps/docs/static/img/power-steering/power-steering.png new file mode 100644 index 0000000000..f6ecaf514e Binary files /dev/null and b/apps/docs/static/img/power-steering/power-steering.png differ diff --git a/apps/docs/static/img/recommended-mcp-servers/context7-global-setup-fixed.png b/apps/docs/static/img/recommended-mcp-servers/context7-global-setup-fixed.png new file mode 100644 index 0000000000..785d17ca35 Binary files /dev/null and b/apps/docs/static/img/recommended-mcp-servers/context7-global-setup-fixed.png differ diff --git a/apps/docs/static/img/recommended-mcp-servers/context7-project-setup-fixed.png b/apps/docs/static/img/recommended-mcp-servers/context7-project-setup-fixed.png new file mode 100644 index 0000000000..bb8b602350 Binary files /dev/null and b/apps/docs/static/img/recommended-mcp-servers/context7-project-setup-fixed.png differ diff --git a/apps/docs/static/img/recommended-mcp-servers/context7-running-fixed.png b/apps/docs/static/img/recommended-mcp-servers/context7-running-fixed.png new file mode 100644 index 0000000000..1e80a0f082 Binary files /dev/null and b/apps/docs/static/img/recommended-mcp-servers/context7-running-fixed.png differ diff --git a/apps/docs/static/img/reporting-errors/reporting-errors-1.png b/apps/docs/static/img/reporting-errors/reporting-errors-1.png new file mode 100644 index 0000000000..472b7e17d3 Binary files /dev/null and b/apps/docs/static/img/reporting-errors/reporting-errors-1.png differ diff --git a/apps/docs/static/img/reporting-errors/reporting-errors-2.png b/apps/docs/static/img/reporting-errors/reporting-errors-2.png new file mode 100644 index 0000000000..9f3838efb0 Binary files /dev/null and b/apps/docs/static/img/reporting-errors/reporting-errors-2.png differ diff --git a/apps/docs/static/img/right-column-roo.gif b/apps/docs/static/img/right-column-roo.gif new file mode 100644 index 0000000000..eaa058efcb Binary files /dev/null and b/apps/docs/static/img/right-column-roo.gif differ diff --git a/apps/docs/static/img/roo-code-logo-dark.svg b/apps/docs/static/img/roo-code-logo-dark.svg new file mode 100644 index 0000000000..d89abdb586 --- /dev/null +++ b/apps/docs/static/img/roo-code-logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/docs/static/img/roo-code-logo-white.svg b/apps/docs/static/img/roo-code-logo-white.svg new file mode 100644 index 0000000000..412c195605 --- /dev/null +++ b/apps/docs/static/img/roo-code-logo-white.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/docs/static/img/settings-management/settings-management.png b/apps/docs/static/img/settings-management/settings-management.png new file mode 100644 index 0000000000..e19dfbb1e8 Binary files /dev/null and b/apps/docs/static/img/settings-management/settings-management.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-1.png b/apps/docs/static/img/shell-integration/shell-integration-1.png new file mode 100644 index 0000000000..d089be39ed Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-1.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-10.png b/apps/docs/static/img/shell-integration/shell-integration-10.png new file mode 100644 index 0000000000..22347adcca Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-10.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-11.png b/apps/docs/static/img/shell-integration/shell-integration-11.png new file mode 100644 index 0000000000..4aa836e242 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-11.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-12.png b/apps/docs/static/img/shell-integration/shell-integration-12.png new file mode 100644 index 0000000000..a356a606a6 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-12.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-13.png b/apps/docs/static/img/shell-integration/shell-integration-13.png new file mode 100644 index 0000000000..f4255e8dee Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-13.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-14.png b/apps/docs/static/img/shell-integration/shell-integration-14.png new file mode 100644 index 0000000000..c2de52ec35 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-14.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-15.png b/apps/docs/static/img/shell-integration/shell-integration-15.png new file mode 100644 index 0000000000..7ca4a0ade0 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-15.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-16.png b/apps/docs/static/img/shell-integration/shell-integration-16.png new file mode 100644 index 0000000000..3a68472367 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-16.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-17.png b/apps/docs/static/img/shell-integration/shell-integration-17.png new file mode 100644 index 0000000000..56106648d8 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-17.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-18.png b/apps/docs/static/img/shell-integration/shell-integration-18.png new file mode 100644 index 0000000000..d13285aceb Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-18.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-19.png b/apps/docs/static/img/shell-integration/shell-integration-19.png new file mode 100644 index 0000000000..b69e726b68 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-19.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-2.png b/apps/docs/static/img/shell-integration/shell-integration-2.png new file mode 100644 index 0000000000..e55a511675 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-2.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-20.png b/apps/docs/static/img/shell-integration/shell-integration-20.png new file mode 100644 index 0000000000..dc5344d3cc Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-20.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-21.png b/apps/docs/static/img/shell-integration/shell-integration-21.png new file mode 100644 index 0000000000..3b1ef2f15d Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-21.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-22.png b/apps/docs/static/img/shell-integration/shell-integration-22.png new file mode 100644 index 0000000000..0e87d26932 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-22.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-23.png b/apps/docs/static/img/shell-integration/shell-integration-23.png new file mode 100644 index 0000000000..7ae3f7d690 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-23.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-24.png b/apps/docs/static/img/shell-integration/shell-integration-24.png new file mode 100644 index 0000000000..e0815d15bb Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-24.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-3.png b/apps/docs/static/img/shell-integration/shell-integration-3.png new file mode 100644 index 0000000000..24c8532a44 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-3.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-4.png b/apps/docs/static/img/shell-integration/shell-integration-4.png new file mode 100644 index 0000000000..6faa30e5b4 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-4.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-5.png b/apps/docs/static/img/shell-integration/shell-integration-5.png new file mode 100644 index 0000000000..f80ba0dcfb Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-5.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-6.png b/apps/docs/static/img/shell-integration/shell-integration-6.png new file mode 100644 index 0000000000..c9e34f0b3c Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-6.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-7.png b/apps/docs/static/img/shell-integration/shell-integration-7.png new file mode 100644 index 0000000000..3011004ec7 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-7.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-8.png b/apps/docs/static/img/shell-integration/shell-integration-8.png new file mode 100644 index 0000000000..6c5b793790 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-8.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration-9.png b/apps/docs/static/img/shell-integration/shell-integration-9.png new file mode 100644 index 0000000000..0b3a6355cf Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration-9.png differ diff --git a/apps/docs/static/img/shell-integration/shell-integration.png b/apps/docs/static/img/shell-integration/shell-integration.png new file mode 100644 index 0000000000..87749d1a09 Binary files /dev/null and b/apps/docs/static/img/shell-integration/shell-integration.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-1.png b/apps/docs/static/img/slash-commands/slash-commands-1.png new file mode 100644 index 0000000000..dd8881cec9 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-1.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-2.png b/apps/docs/static/img/slash-commands/slash-commands-2.png new file mode 100644 index 0000000000..50bc617885 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-2.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-3.png b/apps/docs/static/img/slash-commands/slash-commands-3.png new file mode 100644 index 0000000000..1949e87fa6 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-3.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-4.png b/apps/docs/static/img/slash-commands/slash-commands-4.png new file mode 100644 index 0000000000..821e3c8d53 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-4.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-5.png b/apps/docs/static/img/slash-commands/slash-commands-5.png new file mode 100644 index 0000000000..a97a016bc3 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-5.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands-6.png b/apps/docs/static/img/slash-commands/slash-commands-6.png new file mode 100644 index 0000000000..d70837e7d8 Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands-6.png differ diff --git a/apps/docs/static/img/slash-commands/slash-commands.png b/apps/docs/static/img/slash-commands/slash-commands.png new file mode 100644 index 0000000000..3f8570131c Binary files /dev/null and b/apps/docs/static/img/slash-commands/slash-commands.png differ diff --git a/apps/docs/static/img/social-share.png b/apps/docs/static/img/social-share.png new file mode 100644 index 0000000000..9b8709330c Binary files /dev/null and b/apps/docs/static/img/social-share.png differ diff --git a/apps/docs/static/img/suggested-responses/suggested-responses-1.png b/apps/docs/static/img/suggested-responses/suggested-responses-1.png new file mode 100644 index 0000000000..76fe29f536 Binary files /dev/null and b/apps/docs/static/img/suggested-responses/suggested-responses-1.png differ diff --git a/apps/docs/static/img/suggested-responses/suggested-responses.png b/apps/docs/static/img/suggested-responses/suggested-responses.png new file mode 100644 index 0000000000..e9e8def917 Binary files /dev/null and b/apps/docs/static/img/suggested-responses/suggested-responses.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-1.gif b/apps/docs/static/img/task-sharing/task-sharing-1.gif new file mode 100644 index 0000000000..762504b391 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-1.gif differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-1.png b/apps/docs/static/img/task-sharing/task-sharing-1.png new file mode 100644 index 0000000000..0caf7f50ba Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-1.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-10.png b/apps/docs/static/img/task-sharing/task-sharing-10.png new file mode 100644 index 0000000000..e86d761398 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-10.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-11.png b/apps/docs/static/img/task-sharing/task-sharing-11.png new file mode 100644 index 0000000000..0a28e342b2 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-11.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-12.png b/apps/docs/static/img/task-sharing/task-sharing-12.png new file mode 100644 index 0000000000..ca66a64c8b Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-12.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-2.png b/apps/docs/static/img/task-sharing/task-sharing-2.png new file mode 100644 index 0000000000..f3ade40bee Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-2.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-3.png b/apps/docs/static/img/task-sharing/task-sharing-3.png new file mode 100644 index 0000000000..3030a3ec30 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-3.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-4.png b/apps/docs/static/img/task-sharing/task-sharing-4.png new file mode 100644 index 0000000000..8c33940805 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-4.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-5.png b/apps/docs/static/img/task-sharing/task-sharing-5.png new file mode 100644 index 0000000000..5908e2c5f2 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-5.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-6.png b/apps/docs/static/img/task-sharing/task-sharing-6.png new file mode 100644 index 0000000000..ea50f5dd5d Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-6.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-7.png b/apps/docs/static/img/task-sharing/task-sharing-7.png new file mode 100644 index 0000000000..3f31155bfd Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-7.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-8.png b/apps/docs/static/img/task-sharing/task-sharing-8.png new file mode 100644 index 0000000000..063f6505ff Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-8.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing-9.png b/apps/docs/static/img/task-sharing/task-sharing-9.png new file mode 100644 index 0000000000..d979fb0ae4 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing-9.png differ diff --git a/apps/docs/static/img/task-sharing/task-sharing.png b/apps/docs/static/img/task-sharing/task-sharing.png new file mode 100644 index 0000000000..8df379a875 Binary files /dev/null and b/apps/docs/static/img/task-sharing/task-sharing.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-1.png b/apps/docs/static/img/task-todo-list/task-todo-list-1.png new file mode 100644 index 0000000000..b37caa1cd9 Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-1.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-2.png b/apps/docs/static/img/task-todo-list/task-todo-list-2.png new file mode 100644 index 0000000000..42ae539762 Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-2.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-3.png b/apps/docs/static/img/task-todo-list/task-todo-list-3.png new file mode 100644 index 0000000000..79072b309a Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-3.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-4.png b/apps/docs/static/img/task-todo-list/task-todo-list-4.png new file mode 100644 index 0000000000..6378b5db8a Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-4.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-5.png b/apps/docs/static/img/task-todo-list/task-todo-list-5.png new file mode 100644 index 0000000000..14033a4e3c Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-5.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list-6.png b/apps/docs/static/img/task-todo-list/task-todo-list-6.png new file mode 100644 index 0000000000..3f96434656 Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list-6.png differ diff --git a/apps/docs/static/img/task-todo-list/task-todo-list.png b/apps/docs/static/img/task-todo-list/task-todo-list.png new file mode 100644 index 0000000000..6d3d57667f Binary files /dev/null and b/apps/docs/static/img/task-todo-list/task-todo-list.png differ diff --git a/apps/docs/static/img/the-chat-interface/the-chat-interface-1.png b/apps/docs/static/img/the-chat-interface/the-chat-interface-1.png new file mode 100644 index 0000000000..328d802d3b Binary files /dev/null and b/apps/docs/static/img/the-chat-interface/the-chat-interface-1.png differ diff --git a/apps/docs/static/img/typing-your-requests/naturally.gif b/apps/docs/static/img/typing-your-requests/naturally.gif new file mode 100644 index 0000000000..24449aa447 Binary files /dev/null and b/apps/docs/static/img/typing-your-requests/naturally.gif differ diff --git a/apps/docs/static/img/typing-your-requests/typing-your-requests.png b/apps/docs/static/img/typing-your-requests/typing-your-requests.png new file mode 100644 index 0000000000..14b8bda031 Binary files /dev/null and b/apps/docs/static/img/typing-your-requests/typing-your-requests.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-1.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-1.png new file mode 100644 index 0000000000..e602c103de Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-1.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-10.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-10.png new file mode 100644 index 0000000000..6a63955801 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-10.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-2.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-2.png new file mode 100644 index 0000000000..59f80a2a46 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-2.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-3.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-3.png new file mode 100644 index 0000000000..064c185e7b Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-3.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-4.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-4.png new file mode 100644 index 0000000000..dff3532ecd Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-4.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-5.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-5.png new file mode 100644 index 0000000000..a995f04cd1 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-5.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-6.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-6.png new file mode 100644 index 0000000000..5f643b267f Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-6.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-7.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-7.png new file mode 100644 index 0000000000..3b3a75a062 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-7.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-8.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-8.png new file mode 100644 index 0000000000..b25949e178 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-8.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-9.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-9.png new file mode 100644 index 0000000000..1e5ea319ec Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo-9.png differ diff --git a/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo.png b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo.png new file mode 100644 index 0000000000..f217b78638 Binary files /dev/null and b/apps/docs/static/img/using-mcp-in-roo/using-mcp-in-roo.png differ diff --git a/apps/docs/static/img/using-modes/using-modes-1.png b/apps/docs/static/img/using-modes/using-modes-1.png new file mode 100644 index 0000000000..d25686fb93 Binary files /dev/null and b/apps/docs/static/img/using-modes/using-modes-1.png differ diff --git a/apps/docs/static/img/using-modes/using-modes-2.png b/apps/docs/static/img/using-modes/using-modes-2.png new file mode 100644 index 0000000000..e3cb7c43bf Binary files /dev/null and b/apps/docs/static/img/using-modes/using-modes-2.png differ diff --git a/apps/docs/static/img/using-modes/using-modes.png b/apps/docs/static/img/using-modes/using-modes.png new file mode 100644 index 0000000000..b22f93a913 Binary files /dev/null and b/apps/docs/static/img/using-modes/using-modes.png differ diff --git a/apps/docs/static/img/v3.11.13/v3.11.13-1.png b/apps/docs/static/img/v3.11.13/v3.11.13-1.png new file mode 100644 index 0000000000..27bb8e4638 Binary files /dev/null and b/apps/docs/static/img/v3.11.13/v3.11.13-1.png differ diff --git a/apps/docs/static/img/v3.11.13/v3.11.13.png b/apps/docs/static/img/v3.11.13/v3.11.13.png new file mode 100644 index 0000000000..631a95d112 Binary files /dev/null and b/apps/docs/static/img/v3.11.13/v3.11.13.png differ diff --git a/apps/docs/static/img/v3.11/v3.11-1.png b/apps/docs/static/img/v3.11/v3.11-1.png new file mode 100644 index 0000000000..22a7c73f81 Binary files /dev/null and b/apps/docs/static/img/v3.11/v3.11-1.png differ diff --git a/apps/docs/static/img/v3.11/v3.11-2.png b/apps/docs/static/img/v3.11/v3.11-2.png new file mode 100644 index 0000000000..b51f1b9b04 Binary files /dev/null and b/apps/docs/static/img/v3.11/v3.11-2.png differ diff --git a/apps/docs/static/img/v3.11/v3.11.png b/apps/docs/static/img/v3.11/v3.11.png new file mode 100644 index 0000000000..6966f45af7 Binary files /dev/null and b/apps/docs/static/img/v3.11/v3.11.png differ diff --git a/apps/docs/static/img/v3.14.0/v3.14.0.png b/apps/docs/static/img/v3.14.0/v3.14.0.png new file mode 100644 index 0000000000..3204855bed Binary files /dev/null and b/apps/docs/static/img/v3.14.0/v3.14.0.png differ diff --git a/apps/docs/static/img/v3.14.2/v3.14.2.png b/apps/docs/static/img/v3.14.2/v3.14.2.png new file mode 100644 index 0000000000..754ae9568a Binary files /dev/null and b/apps/docs/static/img/v3.14.2/v3.14.2.png differ diff --git a/apps/docs/static/img/v3.14.3/v3.14.3-1.png b/apps/docs/static/img/v3.14.3/v3.14.3-1.png new file mode 100644 index 0000000000..d62608d67f Binary files /dev/null and b/apps/docs/static/img/v3.14.3/v3.14.3-1.png differ diff --git a/apps/docs/static/img/v3.14.3/v3.14.3.png b/apps/docs/static/img/v3.14.3/v3.14.3.png new file mode 100644 index 0000000000..066049465b Binary files /dev/null and b/apps/docs/static/img/v3.14.3/v3.14.3.png differ diff --git a/apps/docs/static/img/v3.15.0/v3.15.0-1.png b/apps/docs/static/img/v3.15.0/v3.15.0-1.png new file mode 100644 index 0000000000..8063006af5 Binary files /dev/null and b/apps/docs/static/img/v3.15.0/v3.15.0-1.png differ diff --git a/apps/docs/static/img/v3.15.0/v3.15.0-2.png b/apps/docs/static/img/v3.15.0/v3.15.0-2.png new file mode 100644 index 0000000000..80b70cc163 Binary files /dev/null and b/apps/docs/static/img/v3.15.0/v3.15.0-2.png differ diff --git a/apps/docs/static/img/v3.15.0/v3.15.0.png b/apps/docs/static/img/v3.15.0/v3.15.0.png new file mode 100644 index 0000000000..c7fa55b1b9 Binary files /dev/null and b/apps/docs/static/img/v3.15.0/v3.15.0.png differ diff --git a/apps/docs/static/img/v3.15.2/v3.15.2.png b/apps/docs/static/img/v3.15.2/v3.15.2.png new file mode 100644 index 0000000000..1f1d12955c Binary files /dev/null and b/apps/docs/static/img/v3.15.2/v3.15.2.png differ diff --git a/apps/docs/static/img/v3.15/v3.15-1.png b/apps/docs/static/img/v3.15/v3.15-1.png new file mode 100644 index 0000000000..37eb260d80 Binary files /dev/null and b/apps/docs/static/img/v3.15/v3.15-1.png differ diff --git a/apps/docs/static/img/v3.15/v3.15.png b/apps/docs/static/img/v3.15/v3.15.png new file mode 100644 index 0000000000..5a4ffd2fd4 Binary files /dev/null and b/apps/docs/static/img/v3.15/v3.15.png differ diff --git a/apps/docs/static/img/v3.16/v3.16-1.png b/apps/docs/static/img/v3.16/v3.16-1.png new file mode 100644 index 0000000000..ab3ac2f24b Binary files /dev/null and b/apps/docs/static/img/v3.16/v3.16-1.png differ diff --git a/apps/docs/static/img/v3.16/v3.16.gif b/apps/docs/static/img/v3.16/v3.16.gif new file mode 100644 index 0000000000..d9f4ba88f0 Binary files /dev/null and b/apps/docs/static/img/v3.16/v3.16.gif differ diff --git a/apps/docs/static/img/v3.16/v3.16.png b/apps/docs/static/img/v3.16/v3.16.png new file mode 100644 index 0000000000..ff433788ca Binary files /dev/null and b/apps/docs/static/img/v3.16/v3.16.png differ diff --git a/apps/docs/static/img/v3.17.0/v3.17.0.png b/apps/docs/static/img/v3.17.0/v3.17.0.png new file mode 100644 index 0000000000..f08f0e040f Binary files /dev/null and b/apps/docs/static/img/v3.17.0/v3.17.0.png differ diff --git a/apps/docs/static/img/v3.18.0/v3.18.0-1.png b/apps/docs/static/img/v3.18.0/v3.18.0-1.png new file mode 100644 index 0000000000..2ac54d5e91 Binary files /dev/null and b/apps/docs/static/img/v3.18.0/v3.18.0-1.png differ diff --git a/apps/docs/static/img/v3.18.0/v3.18.0.png b/apps/docs/static/img/v3.18.0/v3.18.0.png new file mode 100644 index 0000000000..b37da240c4 Binary files /dev/null and b/apps/docs/static/img/v3.18.0/v3.18.0.png differ diff --git a/apps/docs/static/img/v3.22.0/v3.22.0-1.png b/apps/docs/static/img/v3.22.0/v3.22.0-1.png new file mode 100644 index 0000000000..200fa276c1 Binary files /dev/null and b/apps/docs/static/img/v3.22.0/v3.22.0-1.png differ diff --git a/apps/docs/static/img/v3.22.0/v3.22.0.png b/apps/docs/static/img/v3.22.0/v3.22.0.png new file mode 100644 index 0000000000..1b0f5f1f1b Binary files /dev/null and b/apps/docs/static/img/v3.22.0/v3.22.0.png differ diff --git a/apps/docs/static/img/v3.23.0/v3.23.0-1.png b/apps/docs/static/img/v3.23.0/v3.23.0-1.png new file mode 100644 index 0000000000..aa75bd5e37 Binary files /dev/null and b/apps/docs/static/img/v3.23.0/v3.23.0-1.png differ diff --git a/apps/docs/static/img/v3.23.0/v3.23.0.png b/apps/docs/static/img/v3.23.0/v3.23.0.png new file mode 100644 index 0000000000..a97cd61c74 Binary files /dev/null and b/apps/docs/static/img/v3.23.0/v3.23.0.png differ diff --git a/apps/docs/static/img/v3.23/v3.23-1.png b/apps/docs/static/img/v3.23/v3.23-1.png new file mode 100644 index 0000000000..1b2fa6478e Binary files /dev/null and b/apps/docs/static/img/v3.23/v3.23-1.png differ diff --git a/apps/docs/static/img/v3.23/v3.23.png b/apps/docs/static/img/v3.23/v3.23.png new file mode 100644 index 0000000000..1b2fa6478e Binary files /dev/null and b/apps/docs/static/img/v3.23/v3.23.png differ diff --git a/apps/docs/static/img/v3.25.0/v3.25.0-1.png b/apps/docs/static/img/v3.25.0/v3.25.0-1.png new file mode 100644 index 0000000000..5159d21903 Binary files /dev/null and b/apps/docs/static/img/v3.25.0/v3.25.0-1.png differ diff --git a/apps/docs/static/img/v3.25.0/v3.25.0-2.png b/apps/docs/static/img/v3.25.0/v3.25.0-2.png new file mode 100644 index 0000000000..3243e4e34d Binary files /dev/null and b/apps/docs/static/img/v3.25.0/v3.25.0-2.png differ diff --git a/apps/docs/static/img/v3.25.0/v3.25.0.png b/apps/docs/static/img/v3.25.0/v3.25.0.png new file mode 100644 index 0000000000..64985a1beb Binary files /dev/null and b/apps/docs/static/img/v3.25.0/v3.25.0.png differ diff --git a/apps/docs/static/img/v3.25.21/v3.25.21.png b/apps/docs/static/img/v3.25.21/v3.25.21.png new file mode 100644 index 0000000000..8f4b43e881 Binary files /dev/null and b/apps/docs/static/img/v3.25.21/v3.25.21.png differ diff --git a/apps/docs/static/img/v3.25.5/v3.25.5-1.png b/apps/docs/static/img/v3.25.5/v3.25.5-1.png new file mode 100644 index 0000000000..11e2790517 Binary files /dev/null and b/apps/docs/static/img/v3.25.5/v3.25.5-1.png differ diff --git a/apps/docs/static/img/v3.25.5/v3.25.5.png b/apps/docs/static/img/v3.25.5/v3.25.5.png new file mode 100644 index 0000000000..b05bba1e60 Binary files /dev/null and b/apps/docs/static/img/v3.25.5/v3.25.5.png differ diff --git a/apps/docs/static/img/v3.26.0/v3.26.0.png b/apps/docs/static/img/v3.26.0/v3.26.0.png new file mode 100644 index 0000000000..393eaa94eb Binary files /dev/null and b/apps/docs/static/img/v3.26.0/v3.26.0.png differ diff --git a/apps/docs/static/img/v3.26.1/v3.26.1.png b/apps/docs/static/img/v3.26.1/v3.26.1.png new file mode 100644 index 0000000000..9eb76ec706 Binary files /dev/null and b/apps/docs/static/img/v3.26.1/v3.26.1.png differ diff --git a/apps/docs/static/img/v3.26.2/v3.26.2.png b/apps/docs/static/img/v3.26.2/v3.26.2.png new file mode 100644 index 0000000000..9b21da456e Binary files /dev/null and b/apps/docs/static/img/v3.26.2/v3.26.2.png differ diff --git a/apps/docs/static/img/v3.26.3/v3.26.3.png b/apps/docs/static/img/v3.26.3/v3.26.3.png new file mode 100644 index 0000000000..3d2b5cf729 Binary files /dev/null and b/apps/docs/static/img/v3.26.3/v3.26.3.png differ diff --git a/apps/docs/static/img/v3.26.4/v3.26.4.png b/apps/docs/static/img/v3.26.4/v3.26.4.png new file mode 100644 index 0000000000..76803b9f99 Binary files /dev/null and b/apps/docs/static/img/v3.26.4/v3.26.4.png differ diff --git a/apps/docs/static/img/v3.26.5/v3.26.5.png b/apps/docs/static/img/v3.26.5/v3.26.5.png new file mode 100644 index 0000000000..e986259ff5 Binary files /dev/null and b/apps/docs/static/img/v3.26.5/v3.26.5.png differ diff --git a/apps/docs/static/img/v3.26.6/v3.26.6.png b/apps/docs/static/img/v3.26.6/v3.26.6.png new file mode 100644 index 0000000000..96be69a4e3 Binary files /dev/null and b/apps/docs/static/img/v3.26.6/v3.26.6.png differ diff --git a/apps/docs/static/img/v3.26.7/v3.26.7.png b/apps/docs/static/img/v3.26.7/v3.26.7.png new file mode 100644 index 0000000000..44f2aa0735 Binary files /dev/null and b/apps/docs/static/img/v3.26.7/v3.26.7.png differ diff --git a/apps/docs/static/img/v3.27.0/v3.27.0.png b/apps/docs/static/img/v3.27.0/v3.27.0.png new file mode 100644 index 0000000000..fce290e357 Binary files /dev/null and b/apps/docs/static/img/v3.27.0/v3.27.0.png differ diff --git a/apps/docs/static/img/v3.28.0/v3.28.0.png b/apps/docs/static/img/v3.28.0/v3.28.0.png new file mode 100644 index 0000000000..14e2205961 Binary files /dev/null and b/apps/docs/static/img/v3.28.0/v3.28.0.png differ diff --git a/apps/docs/static/img/v3.28.1/v3.28.1.png b/apps/docs/static/img/v3.28.1/v3.28.1.png new file mode 100644 index 0000000000..d4c77ebcfe Binary files /dev/null and b/apps/docs/static/img/v3.28.1/v3.28.1.png differ diff --git a/apps/docs/static/img/v3.28.10/v3.28.10-1.png b/apps/docs/static/img/v3.28.10/v3.28.10-1.png new file mode 100644 index 0000000000..285d276b2e Binary files /dev/null and b/apps/docs/static/img/v3.28.10/v3.28.10-1.png differ diff --git a/apps/docs/static/img/v3.28.10/v3.28.10.png b/apps/docs/static/img/v3.28.10/v3.28.10.png new file mode 100644 index 0000000000..97f6cd429f Binary files /dev/null and b/apps/docs/static/img/v3.28.10/v3.28.10.png differ diff --git a/apps/docs/static/img/v3.28.14/v3.28.14.png b/apps/docs/static/img/v3.28.14/v3.28.14.png new file mode 100644 index 0000000000..4ef1acc702 Binary files /dev/null and b/apps/docs/static/img/v3.28.14/v3.28.14.png differ diff --git a/apps/docs/static/img/v3.28.15/v3.28.15.png b/apps/docs/static/img/v3.28.15/v3.28.15.png new file mode 100644 index 0000000000..fc6e235bef Binary files /dev/null and b/apps/docs/static/img/v3.28.15/v3.28.15.png differ diff --git a/apps/docs/static/img/v3.28.16/v3.28.16.png b/apps/docs/static/img/v3.28.16/v3.28.16.png new file mode 100644 index 0000000000..5c4c31ece2 Binary files /dev/null and b/apps/docs/static/img/v3.28.16/v3.28.16.png differ diff --git a/apps/docs/static/img/v3.28.2/v3.28.2.png b/apps/docs/static/img/v3.28.2/v3.28.2.png new file mode 100644 index 0000000000..917c0a6309 Binary files /dev/null and b/apps/docs/static/img/v3.28.2/v3.28.2.png differ diff --git a/apps/docs/static/img/v3.28.3/v3.28.3.png b/apps/docs/static/img/v3.28.3/v3.28.3.png new file mode 100644 index 0000000000..883ef80970 Binary files /dev/null and b/apps/docs/static/img/v3.28.3/v3.28.3.png differ diff --git a/apps/docs/static/img/v3.28.4/v3.28.4.png b/apps/docs/static/img/v3.28.4/v3.28.4.png new file mode 100644 index 0000000000..ea1e82a8dd Binary files /dev/null and b/apps/docs/static/img/v3.28.4/v3.28.4.png differ diff --git a/apps/docs/static/img/v3.28.5/v3.28.5.png b/apps/docs/static/img/v3.28.5/v3.28.5.png new file mode 100644 index 0000000000..0a22c25c40 Binary files /dev/null and b/apps/docs/static/img/v3.28.5/v3.28.5.png differ diff --git a/apps/docs/static/img/v3.28.6/v3.28.6.png b/apps/docs/static/img/v3.28.6/v3.28.6.png new file mode 100644 index 0000000000..3ce1883756 Binary files /dev/null and b/apps/docs/static/img/v3.28.6/v3.28.6.png differ diff --git a/apps/docs/static/img/v3.28.7/v3.28.7.png b/apps/docs/static/img/v3.28.7/v3.28.7.png new file mode 100644 index 0000000000..d4690f19c9 Binary files /dev/null and b/apps/docs/static/img/v3.28.7/v3.28.7.png differ diff --git a/apps/docs/static/img/v3.28.8/v3.28.8.png b/apps/docs/static/img/v3.28.8/v3.28.8.png new file mode 100644 index 0000000000..8fcfa22453 Binary files /dev/null and b/apps/docs/static/img/v3.28.8/v3.28.8.png differ diff --git a/apps/docs/static/img/v3.28.9/v3.28.9.png b/apps/docs/static/img/v3.28.9/v3.28.9.png new file mode 100644 index 0000000000..a227243003 Binary files /dev/null and b/apps/docs/static/img/v3.28.9/v3.28.9.png differ diff --git a/apps/docs/static/img/v3.29.0/v3.29.0.png b/apps/docs/static/img/v3.29.0/v3.29.0.png new file mode 100644 index 0000000000..8f9381fbae Binary files /dev/null and b/apps/docs/static/img/v3.29.0/v3.29.0.png differ diff --git a/apps/docs/static/img/v3.29.1/v3.29.1.png b/apps/docs/static/img/v3.29.1/v3.29.1.png new file mode 100644 index 0000000000..71dcd474b0 Binary files /dev/null and b/apps/docs/static/img/v3.29.1/v3.29.1.png differ diff --git a/apps/docs/static/img/v3.29.4/v3.29.4.png b/apps/docs/static/img/v3.29.4/v3.29.4.png new file mode 100644 index 0000000000..aa53e7bcdb Binary files /dev/null and b/apps/docs/static/img/v3.29.4/v3.29.4.png differ diff --git a/apps/docs/static/img/v3.30.0/v3.30.0.png b/apps/docs/static/img/v3.30.0/v3.30.0.png new file mode 100644 index 0000000000..8050aa49ce Binary files /dev/null and b/apps/docs/static/img/v3.30.0/v3.30.0.png differ diff --git a/apps/docs/static/img/v3.30.2/v3.30.2.png b/apps/docs/static/img/v3.30.2/v3.30.2.png new file mode 100644 index 0000000000..134bf2290d Binary files /dev/null and b/apps/docs/static/img/v3.30.2/v3.30.2.png differ diff --git a/apps/docs/static/img/v3.30.3/v3.30.3.png b/apps/docs/static/img/v3.30.3/v3.30.3.png new file mode 100644 index 0000000000..6fb3e84d78 Binary files /dev/null and b/apps/docs/static/img/v3.30.3/v3.30.3.png differ diff --git a/apps/docs/static/img/v3.31.0/v3.31.0.png b/apps/docs/static/img/v3.31.0/v3.31.0.png new file mode 100644 index 0000000000..0317753640 Binary files /dev/null and b/apps/docs/static/img/v3.31.0/v3.31.0.png differ diff --git a/apps/docs/static/img/v3.31.1/v3.31.1.png b/apps/docs/static/img/v3.31.1/v3.31.1.png new file mode 100644 index 0000000000..15499dd936 Binary files /dev/null and b/apps/docs/static/img/v3.31.1/v3.31.1.png differ diff --git a/apps/docs/static/img/v3.31.3/v3.31.3.png b/apps/docs/static/img/v3.31.3/v3.31.3.png new file mode 100644 index 0000000000..f3b88afa91 Binary files /dev/null and b/apps/docs/static/img/v3.31.3/v3.31.3.png differ diff --git a/apps/docs/static/img/v3.32.0/v3.32.0.png b/apps/docs/static/img/v3.32.0/v3.32.0.png new file mode 100644 index 0000000000..2c7744b9eb Binary files /dev/null and b/apps/docs/static/img/v3.32.0/v3.32.0.png differ diff --git a/apps/docs/static/img/v3.32.1/v3.32.1.png b/apps/docs/static/img/v3.32.1/v3.32.1.png new file mode 100644 index 0000000000..b5613c2bc7 Binary files /dev/null and b/apps/docs/static/img/v3.32.1/v3.32.1.png differ diff --git a/apps/docs/static/img/v3.33.0/v3.33.0.png b/apps/docs/static/img/v3.33.0/v3.33.0.png new file mode 100644 index 0000000000..12ba322426 Binary files /dev/null and b/apps/docs/static/img/v3.33.0/v3.33.0.png differ diff --git a/apps/docs/static/img/v3.33.1/v3.33.1.png b/apps/docs/static/img/v3.33.1/v3.33.1.png new file mode 100644 index 0000000000..fc27da29dd Binary files /dev/null and b/apps/docs/static/img/v3.33.1/v3.33.1.png differ diff --git a/apps/docs/static/img/v3.33.3/v3.33.3.png b/apps/docs/static/img/v3.33.3/v3.33.3.png new file mode 100644 index 0000000000..c71aab016e Binary files /dev/null and b/apps/docs/static/img/v3.33.3/v3.33.3.png differ diff --git a/apps/docs/static/img/v3.34.0/v3.34.0.png b/apps/docs/static/img/v3.34.0/v3.34.0.png new file mode 100644 index 0000000000..bd3696c1c8 Binary files /dev/null and b/apps/docs/static/img/v3.34.0/v3.34.0.png differ diff --git a/apps/docs/static/img/v3.34.2/v3.34.2.png b/apps/docs/static/img/v3.34.2/v3.34.2.png new file mode 100644 index 0000000000..d32526a336 Binary files /dev/null and b/apps/docs/static/img/v3.34.2/v3.34.2.png differ diff --git a/apps/docs/static/img/v3.34.3/v3.34.3.png b/apps/docs/static/img/v3.34.3/v3.34.3.png new file mode 100644 index 0000000000..a3785a3ec6 Binary files /dev/null and b/apps/docs/static/img/v3.34.3/v3.34.3.png differ diff --git a/apps/docs/static/img/v3.34.4/v3.34.4.png b/apps/docs/static/img/v3.34.4/v3.34.4.png new file mode 100644 index 0000000000..11103bf3e4 Binary files /dev/null and b/apps/docs/static/img/v3.34.4/v3.34.4.png differ diff --git a/apps/docs/static/img/v3.34.5/v3.34.5.png b/apps/docs/static/img/v3.34.5/v3.34.5.png new file mode 100644 index 0000000000..90ba9b06b4 Binary files /dev/null and b/apps/docs/static/img/v3.34.5/v3.34.5.png differ diff --git a/apps/docs/static/img/v3.34.6/v3.34.6.png b/apps/docs/static/img/v3.34.6/v3.34.6.png new file mode 100644 index 0000000000..cfba5aa110 Binary files /dev/null and b/apps/docs/static/img/v3.34.6/v3.34.6.png differ diff --git a/apps/docs/static/img/v3.34.7/v3.34.7.png b/apps/docs/static/img/v3.34.7/v3.34.7.png new file mode 100644 index 0000000000..b9cad77d8e Binary files /dev/null and b/apps/docs/static/img/v3.34.7/v3.34.7.png differ diff --git a/apps/docs/static/img/v3.34.8/v3.34.8.png b/apps/docs/static/img/v3.34.8/v3.34.8.png new file mode 100644 index 0000000000..aedbe48365 Binary files /dev/null and b/apps/docs/static/img/v3.34.8/v3.34.8.png differ diff --git a/apps/docs/static/img/v3.35.0/v3.35.0.png b/apps/docs/static/img/v3.35.0/v3.35.0.png new file mode 100644 index 0000000000..71857f1d11 Binary files /dev/null and b/apps/docs/static/img/v3.35.0/v3.35.0.png differ diff --git a/apps/docs/static/img/v3.35.2/v3.35.2.png b/apps/docs/static/img/v3.35.2/v3.35.2.png new file mode 100644 index 0000000000..085712cf97 Binary files /dev/null and b/apps/docs/static/img/v3.35.2/v3.35.2.png differ diff --git a/apps/docs/static/img/v3.36.0/v3.36.0.png b/apps/docs/static/img/v3.36.0/v3.36.0.png new file mode 100644 index 0000000000..79200d592a Binary files /dev/null and b/apps/docs/static/img/v3.36.0/v3.36.0.png differ diff --git a/apps/docs/static/img/v3.36.1/v3.36.1.png b/apps/docs/static/img/v3.36.1/v3.36.1.png new file mode 100644 index 0000000000..a3e8b11d0e Binary files /dev/null and b/apps/docs/static/img/v3.36.1/v3.36.1.png differ diff --git a/apps/docs/static/img/v3.36.10/v3.36.10.png b/apps/docs/static/img/v3.36.10/v3.36.10.png new file mode 100644 index 0000000000..76fb30b98e Binary files /dev/null and b/apps/docs/static/img/v3.36.10/v3.36.10.png differ diff --git a/apps/docs/static/img/v3.36.11/v3.36.11.png b/apps/docs/static/img/v3.36.11/v3.36.11.png new file mode 100644 index 0000000000..d4b0843cf8 Binary files /dev/null and b/apps/docs/static/img/v3.36.11/v3.36.11.png differ diff --git a/apps/docs/static/img/v3.36.12/v3.36.12.png b/apps/docs/static/img/v3.36.12/v3.36.12.png new file mode 100644 index 0000000000..9b48925f1d Binary files /dev/null and b/apps/docs/static/img/v3.36.12/v3.36.12.png differ diff --git a/apps/docs/static/img/v3.36.13/v3.36.13.png b/apps/docs/static/img/v3.36.13/v3.36.13.png new file mode 100644 index 0000000000..ab7f00a706 Binary files /dev/null and b/apps/docs/static/img/v3.36.13/v3.36.13.png differ diff --git a/apps/docs/static/img/v3.36.14/v3.36.14.png b/apps/docs/static/img/v3.36.14/v3.36.14.png new file mode 100644 index 0000000000..15009bb2f4 Binary files /dev/null and b/apps/docs/static/img/v3.36.14/v3.36.14.png differ diff --git a/apps/docs/static/img/v3.36.15/v3.36.15.png b/apps/docs/static/img/v3.36.15/v3.36.15.png new file mode 100644 index 0000000000..549f02bd5d Binary files /dev/null and b/apps/docs/static/img/v3.36.15/v3.36.15.png differ diff --git a/apps/docs/static/img/v3.36.2/v3.36.2.png b/apps/docs/static/img/v3.36.2/v3.36.2.png new file mode 100644 index 0000000000..bd30b350de Binary files /dev/null and b/apps/docs/static/img/v3.36.2/v3.36.2.png differ diff --git a/apps/docs/static/img/v3.36.3/v3.36.3.png b/apps/docs/static/img/v3.36.3/v3.36.3.png new file mode 100644 index 0000000000..279a2f0419 Binary files /dev/null and b/apps/docs/static/img/v3.36.3/v3.36.3.png differ diff --git a/apps/docs/static/img/v3.36.4/v3.36.4.png b/apps/docs/static/img/v3.36.4/v3.36.4.png new file mode 100644 index 0000000000..914765bfb8 Binary files /dev/null and b/apps/docs/static/img/v3.36.4/v3.36.4.png differ diff --git a/apps/docs/static/img/v3.36.5/v3.36.5.png b/apps/docs/static/img/v3.36.5/v3.36.5.png new file mode 100644 index 0000000000..058fc03105 Binary files /dev/null and b/apps/docs/static/img/v3.36.5/v3.36.5.png differ diff --git a/apps/docs/static/img/v3.36.6/v3.36.6.png b/apps/docs/static/img/v3.36.6/v3.36.6.png new file mode 100644 index 0000000000..0edb7428b9 Binary files /dev/null and b/apps/docs/static/img/v3.36.6/v3.36.6.png differ diff --git a/apps/docs/static/img/v3.36.9/v3.36.9.png b/apps/docs/static/img/v3.36.9/v3.36.9.png new file mode 100644 index 0000000000..b63ccb5f89 Binary files /dev/null and b/apps/docs/static/img/v3.36.9/v3.36.9.png differ diff --git a/apps/docs/static/img/v3.37.0/v3.37.0.png b/apps/docs/static/img/v3.37.0/v3.37.0.png new file mode 100644 index 0000000000..2bee909c5b Binary files /dev/null and b/apps/docs/static/img/v3.37.0/v3.37.0.png differ diff --git a/apps/docs/static/img/v3.37.1/v3.37.1.png b/apps/docs/static/img/v3.37.1/v3.37.1.png new file mode 100644 index 0000000000..586f4821db Binary files /dev/null and b/apps/docs/static/img/v3.37.1/v3.37.1.png differ diff --git a/apps/docs/static/img/v3.37/v3.37-1.png b/apps/docs/static/img/v3.37/v3.37-1.png new file mode 100644 index 0000000000..e00eba37d9 Binary files /dev/null and b/apps/docs/static/img/v3.37/v3.37-1.png differ diff --git a/apps/docs/static/img/v3.37/v3.37-2.png b/apps/docs/static/img/v3.37/v3.37-2.png new file mode 100644 index 0000000000..44ef150a6c Binary files /dev/null and b/apps/docs/static/img/v3.37/v3.37-2.png differ diff --git a/apps/docs/static/img/v3.37/v3.37.png b/apps/docs/static/img/v3.37/v3.37.png new file mode 100644 index 0000000000..469ff66ae6 Binary files /dev/null and b/apps/docs/static/img/v3.37/v3.37.png differ diff --git a/apps/docs/static/img/v3.38.0/v3.38.0.png b/apps/docs/static/img/v3.38.0/v3.38.0.png new file mode 100644 index 0000000000..46683a26df Binary files /dev/null and b/apps/docs/static/img/v3.38.0/v3.38.0.png differ diff --git a/apps/docs/static/img/v3.38.1/v3.38.1.png b/apps/docs/static/img/v3.38.1/v3.38.1.png new file mode 100644 index 0000000000..d77506be2c Binary files /dev/null and b/apps/docs/static/img/v3.38.1/v3.38.1.png differ diff --git a/apps/docs/static/img/v3.38.2/v3.38.2.png b/apps/docs/static/img/v3.38.2/v3.38.2.png new file mode 100644 index 0000000000..d1e8f06d2f Binary files /dev/null and b/apps/docs/static/img/v3.38.2/v3.38.2.png differ diff --git a/apps/docs/static/img/v3.39.0/v3.39.0.png b/apps/docs/static/img/v3.39.0/v3.39.0.png new file mode 100644 index 0000000000..4f71720928 Binary files /dev/null and b/apps/docs/static/img/v3.39.0/v3.39.0.png differ diff --git a/apps/docs/static/img/v3.39.3/v3.39.3.png b/apps/docs/static/img/v3.39.3/v3.39.3.png new file mode 100644 index 0000000000..f8dcd92b69 Binary files /dev/null and b/apps/docs/static/img/v3.39.3/v3.39.3.png differ diff --git a/apps/docs/static/img/v3.40.0/v3.40.0.png b/apps/docs/static/img/v3.40.0/v3.40.0.png new file mode 100644 index 0000000000..32f2e71711 Binary files /dev/null and b/apps/docs/static/img/v3.40.0/v3.40.0.png differ diff --git a/apps/docs/static/img/v3.41.0/v3.41.0.png b/apps/docs/static/img/v3.41.0/v3.41.0.png new file mode 100644 index 0000000000..069858f2dd Binary files /dev/null and b/apps/docs/static/img/v3.41.0/v3.41.0.png differ diff --git a/apps/docs/static/img/v3.41.1/v3.41.1.png b/apps/docs/static/img/v3.41.1/v3.41.1.png new file mode 100644 index 0000000000..c07c05aa6e Binary files /dev/null and b/apps/docs/static/img/v3.41.1/v3.41.1.png differ diff --git a/apps/docs/static/img/v3.42.0/v3.42.0.png b/apps/docs/static/img/v3.42.0/v3.42.0.png new file mode 100644 index 0000000000..80bb7ffa35 Binary files /dev/null and b/apps/docs/static/img/v3.42.0/v3.42.0.png differ diff --git a/apps/docs/static/img/v3.43.0/v3.43.0.png b/apps/docs/static/img/v3.43.0/v3.43.0.png new file mode 100644 index 0000000000..b38ad925cc Binary files /dev/null and b/apps/docs/static/img/v3.43.0/v3.43.0.png differ diff --git a/apps/docs/static/img/v3.44.0/v3.44.0.png b/apps/docs/static/img/v3.44.0/v3.44.0.png new file mode 100644 index 0000000000..ca92998b3c Binary files /dev/null and b/apps/docs/static/img/v3.44.0/v3.44.0.png differ diff --git a/apps/docs/static/img/v3.45.0/v3.45.0.png b/apps/docs/static/img/v3.45.0/v3.45.0.png new file mode 100644 index 0000000000..53e2016420 Binary files /dev/null and b/apps/docs/static/img/v3.45.0/v3.45.0.png differ diff --git a/apps/docs/static/img/v3.46.0/v3.46.0.png b/apps/docs/static/img/v3.46.0/v3.46.0.png new file mode 100644 index 0000000000..10aa0cf20c Binary files /dev/null and b/apps/docs/static/img/v3.46.0/v3.46.0.png differ diff --git a/apps/docs/static/img/v3.47.0/v3.47.0.png b/apps/docs/static/img/v3.47.0/v3.47.0.png new file mode 100644 index 0000000000..bc5460933e Binary files /dev/null and b/apps/docs/static/img/v3.47.0/v3.47.0.png differ diff --git a/apps/docs/static/img/your-first-task/your-first-task-6.png b/apps/docs/static/img/your-first-task/your-first-task-6.png new file mode 100644 index 0000000000..93b88a043c Binary files /dev/null and b/apps/docs/static/img/your-first-task/your-first-task-6.png differ diff --git a/apps/docs/static/img/your-first-task/your-first-task-7.png b/apps/docs/static/img/your-first-task/your-first-task-7.png new file mode 100644 index 0000000000..01291cd8db Binary files /dev/null and b/apps/docs/static/img/your-first-task/your-first-task-7.png differ diff --git a/apps/docs/static/img/your-first-task/your-first-task-8.png b/apps/docs/static/img/your-first-task/your-first-task-8.png new file mode 100644 index 0000000000..e7fc32506d Binary files /dev/null and b/apps/docs/static/img/your-first-task/your-first-task-8.png differ diff --git a/apps/docs/static/img/your-first-task/your-first-task.png b/apps/docs/static/img/your-first-task/your-first-task.png new file mode 100644 index 0000000000..5a3cd874dd Binary files /dev/null and b/apps/docs/static/img/your-first-task/your-first-task.png differ diff --git a/apps/docs/static/robots.txt b/apps/docs/static/robots.txt new file mode 100644 index 0000000000..e3828ec20c --- /dev/null +++ b/apps/docs/static/robots.txt @@ -0,0 +1,3 @@ +User-agent: * +Allow: / +Sitemap: https://roocode.github.io/sitemap.xml diff --git a/apps/docs/static/ui/arrow-left.svg b/apps/docs/static/ui/arrow-left.svg new file mode 100644 index 0000000000..d31609649c --- /dev/null +++ b/apps/docs/static/ui/arrow-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/static/ui/arrow-right.svg b/apps/docs/static/ui/arrow-right.svg new file mode 100644 index 0000000000..8405ae26f2 --- /dev/null +++ b/apps/docs/static/ui/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/static/ui/check.svg b/apps/docs/static/ui/check.svg new file mode 100644 index 0000000000..6e390aaca8 --- /dev/null +++ b/apps/docs/static/ui/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/static/ui/x.svg b/apps/docs/static/ui/x.svg new file mode 100644 index 0000000000..eb194fd2e6 --- /dev/null +++ b/apps/docs/static/ui/x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 0000000000..712d200a37 --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,8 @@ +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "@docusaurus/tsconfig", + "compilerOptions": { + "baseUrl": "." + }, + "exclude": [".docusaurus", "build"] +} diff --git a/apps/docs/turbo.json b/apps/docs/turbo.json new file mode 100644 index 0000000000..1aa4810cc9 --- /dev/null +++ b/apps/docs/turbo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "build": { + "outputs": ["build/**"], + "inputs": [ + "docs/**", + "src/**", + "static/**", + "docusaurus.config.ts", + "sidebars.ts", + "package.json", + "tsconfig.json" + ] + } + } +} diff --git a/apps/web-evals/.env b/apps/web-evals/.env deleted file mode 100644 index 1bb6dd6dac..0000000000 --- a/apps/web-evals/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL=postgres://postgres:password@localhost:5433/evals_development diff --git a/apps/web-evals/.gitignore b/apps/web-evals/.gitignore deleted file mode 100644 index 443f3159ed..0000000000 --- a/apps/web-evals/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# .env -!.env - -# next.js -.next - -# typescript -tsconfig.tsbuildinfo diff --git a/apps/web-evals/CHANGELOG.md b/apps/web-evals/CHANGELOG.md deleted file mode 100644 index b3531905ac..0000000000 --- a/apps/web-evals/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -# @roo-code/web-evals - -## 0.0.1 diff --git a/apps/web-evals/components.json b/apps/web-evals/components.json deleted file mode 100644 index 5bcedb3141..0000000000 --- a/apps/web-evals/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "src/app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} diff --git a/apps/web-evals/eslint.config.mjs b/apps/web-evals/eslint.config.mjs deleted file mode 100644 index 024d6157d4..0000000000 --- a/apps/web-evals/eslint.config.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import { nextJsConfig } from "@roo-code/config-eslint/next-js" - -/** @type {import("eslint").Linter.Config} */ -export default [ - ...nextJsConfig, - { - rules: { - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "error", - { - caughtErrorsIgnorePattern: "^_", - }, - ], - }, - }, -] diff --git a/apps/web-evals/next-env.d.ts b/apps/web-evals/next-env.d.ts deleted file mode 100644 index 7506fe6afb..0000000000 --- a/apps/web-evals/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -import "./.next/dev/types/routes.d.ts" - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web-evals/next.config.ts b/apps/web-evals/next.config.ts deleted file mode 100644 index b5f54a87be..0000000000 --- a/apps/web-evals/next.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { NextConfig } from "next" - -const nextConfig: NextConfig = { - turbopack: {}, -} - -export default nextConfig diff --git a/apps/web-evals/package.json b/apps/web-evals/package.json deleted file mode 100644 index 1723f57583..0000000000 --- a/apps/web-evals/package.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "@roo-code/web-evals", - "version": "0.0.1", - "type": "module", - "scripts": { - "lint": "eslint src --ext=ts,tsx --max-warnings=0", - "check-types": "tsc -b", - "dev": "scripts/check-services.sh && next dev -p 3446", - "format": "prettier --write src", - "build": "next build", - "start": "next start -p 3446", - "clean": "rimraf tsconfig.tsbuildinfo .next .turbo" - }, - "dependencies": { - "@hookform/resolvers": "^5.1.1", - "@radix-ui/react-alert-dialog": "^1.1.7", - "@radix-ui/react-checkbox": "^1.1.5", - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-dropdown-menu": "^2.1.7", - "@radix-ui/react-label": "^2.1.2", - "@radix-ui/react-popover": "^1.1.6", - "@radix-ui/react-scroll-area": "^1.2.3", - "@radix-ui/react-select": "^2.1.6", - "@radix-ui/react-separator": "^1.1.2", - "@radix-ui/react-slider": "^1.2.4", - "@radix-ui/react-slot": "^1.1.2", - "@radix-ui/react-tabs": "^1.1.3", - "@radix-ui/react-tooltip": "^1.2.8", - "@roo-code/evals": "workspace:^", - "@roo-code/types": "workspace:^", - "@tanstack/react-query": "^5.69.0", - "archiver": "^7.0.1", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.1.0", - "fuzzysort": "^3.1.0", - "lucide-react": "^0.518.0", - "next": "^16.1.6", - "next-themes": "^0.4.6", - "p-map": "^7.0.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-hook-form": "^7.57.0", - "react-use": "^17.6.0", - "redis": "^5.5.5", - "sonner": "^2.0.5", - "tailwind-merge": "^3.3.0", - "tailwindcss-animate": "^1.0.7", - "vaul": "^1.1.2", - "zod": "^3.25.61" - }, - "devDependencies": { - "@roo-code/config-eslint": "workspace:^", - "@roo-code/config-typescript": "workspace:^", - "@tailwindcss/postcss": "^4", - "@types/archiver": "^7.0.0", - "@types/ps-tree": "^1.1.6", - "@types/react": "^18.3.23", - "@types/react-dom": "^18.3.5", - "tailwindcss": "^4", - "vitest": "^3.2.3" - } -} diff --git a/apps/web-evals/postcss.config.mjs b/apps/web-evals/postcss.config.mjs deleted file mode 100644 index 78452aadce..0000000000 --- a/apps/web-evals/postcss.config.mjs +++ /dev/null @@ -1,5 +0,0 @@ -const config = { - plugins: ["@tailwindcss/postcss"], -} - -export default config diff --git a/apps/web-evals/public/.gitkeep b/apps/web-evals/public/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/apps/web-evals/scripts/check-services.sh b/apps/web-evals/scripts/check-services.sh deleted file mode 100755 index d72ffd54e8..0000000000 --- a/apps/web-evals/scripts/check-services.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -if ! docker info &> /dev/null; then - echo "❌ Docker is not running. Please start Docker Desktop and try again." - exit 1 -fi - -if ! nc -z postgres 5433 2>/dev/null; then - echo "❌ PostgreSQL is not running on port 5432" - echo "💡 Start it with: pnpm --filter @roo-code/evals db:up" - exit 1 -fi - -if ! nc -z redis 6380 2>/dev/null; then - echo "❌ Redis is not running on port 6379" - echo "💡 Start it with: pnpm --filter @roo-code/evals redis:up" - exit 1 -fi - -echo "✅ All required services are running" diff --git a/apps/web-evals/src/actions/__tests__/killRun.spec.ts b/apps/web-evals/src/actions/__tests__/killRun.spec.ts deleted file mode 100644 index 814d70d9fc..0000000000 --- a/apps/web-evals/src/actions/__tests__/killRun.spec.ts +++ /dev/null @@ -1,207 +0,0 @@ -// npx vitest run src/actions/__tests__/killRun.spec.ts - -import { execFileSync } from "child_process" - -// Mock child_process -vi.mock("child_process", () => ({ - execFileSync: vi.fn(), - spawn: vi.fn(), -})) - -// Mock next/cache -vi.mock("next/cache", () => ({ - revalidatePath: vi.fn(), -})) - -// Mock redis client -vi.mock("@/lib/server/redis", () => ({ - redisClient: vi.fn().mockResolvedValue({ - del: vi.fn().mockResolvedValue(1), - }), -})) - -// Mock @roo-code/evals -vi.mock("@roo-code/evals", () => ({ - createRun: vi.fn(), - deleteRun: vi.fn(), - createTask: vi.fn(), - exerciseLanguages: [], - getExercisesForLanguage: vi.fn().mockResolvedValue([]), -})) - -// Mock timers to speed up tests -vi.useFakeTimers() - -// Import after mocks -import { killRun } from "../runs" - -const mockExecFileSync = execFileSync as ReturnType - -describe("killRun", () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - afterEach(() => { - vi.clearAllTimers() - }) - - it("should kill controller first, wait, then kill task containers", async () => { - const runId = 123 - - // execFileSync is used for all docker commands - mockExecFileSync - .mockReturnValueOnce("") // docker kill controller - .mockReturnValueOnce("evals-task-123-456.0\nevals-task-123-789.1\n") // docker ps - .mockReturnValueOnce("") // docker kill evals-task-123-456.0 - .mockReturnValueOnce("") // docker kill evals-task-123-789.1 - - const resultPromise = killRun(runId) - - // Fast-forward past the 10 second sleep - await vi.advanceTimersByTimeAsync(10000) - - const result = await resultPromise - - expect(result.success).toBe(true) - expect(result.killedContainers).toContain("evals-controller-123") - expect(result.killedContainers).toContain("evals-task-123-456.0") - expect(result.killedContainers).toContain("evals-task-123-789.1") - expect(result.errors).toHaveLength(0) - - // Verify execFileSync was called for docker kill - expect(mockExecFileSync).toHaveBeenNthCalledWith( - 1, - "docker", - ["kill", "evals-controller-123"], - expect.any(Object), - ) - // Verify execFileSync was called for docker ps with run-specific filter - expect(mockExecFileSync).toHaveBeenNthCalledWith( - 2, - "docker", - ["ps", "--format", "{{.Names}}", "--filter", "name=evals-task-123-"], - expect.any(Object), - ) - }) - - it("should continue killing runners even if controller is not running", async () => { - const runId = 456 - - mockExecFileSync - .mockImplementationOnce(() => { - throw new Error("No such container") - }) // controller kill fails - .mockReturnValueOnce("evals-task-456-100.0\n") // docker ps - .mockReturnValueOnce("") // docker kill task - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - expect(result.success).toBe(true) - expect(result.killedContainers).toContain("evals-task-456-100.0") - // Controller not in list since it failed - expect(result.killedContainers).not.toContain("evals-controller-456") - }) - - it("should clear Redis state after killing containers", async () => { - const runId = 789 - - const mockDel = vi.fn().mockResolvedValue(1) - const { redisClient } = await import("@/lib/server/redis") - vi.mocked(redisClient).mockResolvedValue({ del: mockDel } as never) - - mockExecFileSync - .mockReturnValueOnce("") // controller kill - .mockReturnValueOnce("") // docker ps (no tasks) - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - await resultPromise - - expect(mockDel).toHaveBeenCalledWith("heartbeat:789") - expect(mockDel).toHaveBeenCalledWith("runners:789") - }) - - it("should handle docker ps failure gracefully", async () => { - const runId = 111 - - mockExecFileSync - .mockReturnValueOnce("") // controller kill succeeds - .mockImplementationOnce(() => { - throw new Error("Docker error") - }) // docker ps fails - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - // Should still be successful because controller was killed - expect(result.success).toBe(true) - expect(result.killedContainers).toContain("evals-controller-111") - expect(result.errors).toContain("Failed to list Docker task containers") - }) - - it("should handle individual task kill failures", async () => { - const runId = 222 - - mockExecFileSync - .mockReturnValueOnce("") // controller kill - .mockReturnValueOnce("evals-task-222-300.0\nevals-task-222-400.0\n") // docker ps - .mockImplementationOnce(() => { - throw new Error("Kill failed") - }) // first task kill fails - .mockReturnValueOnce("") // second task kill succeeds - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - expect(result.success).toBe(true) - expect(result.killedContainers).toContain("evals-controller-222") - expect(result.killedContainers).toContain("evals-task-222-400.0") - expect(result.errors.length).toBe(1) - expect(result.errors[0]).toContain("evals-task-222-300.0") - }) - - it("should return success with no containers when nothing is running", async () => { - const runId = 333 - - mockExecFileSync - .mockImplementationOnce(() => { - throw new Error("No such container") - }) // controller not running - .mockReturnValueOnce("") // no task containers - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - expect(result.success).toBe(true) - expect(result.killedContainers).toHaveLength(0) - expect(result.errors).toHaveLength(0) - }) - - it("should only kill containers belonging to the specific run", async () => { - const runId = 555 - - mockExecFileSync - .mockReturnValueOnce("") // controller kill - .mockReturnValueOnce("evals-task-555-100.0\n") // docker ps - .mockReturnValueOnce("") // docker kill task - - const resultPromise = killRun(runId) - await vi.advanceTimersByTimeAsync(10000) - const result = await resultPromise - - expect(result.success).toBe(true) - // Verify execFileSync was called for docker ps with run-specific filter - expect(mockExecFileSync).toHaveBeenNthCalledWith( - 2, - "docker", - ["ps", "--format", "{{.Names}}", "--filter", "name=evals-task-555-"], - expect.any(Object), - ) - }) -}) diff --git a/apps/web-evals/src/actions/exercises.ts b/apps/web-evals/src/actions/exercises.ts deleted file mode 100644 index 17eb1ff085..0000000000 --- a/apps/web-evals/src/actions/exercises.ts +++ /dev/null @@ -1,22 +0,0 @@ -"use server" - -import * as path from "path" -import { fileURLToPath } from "url" - -import { exerciseLanguages, listDirectories } from "@roo-code/evals" - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) // /apps/web-evals/src/actions - -const EVALS_REPO_PATH = path.resolve(__dirname, "../../../../../evals") - -export const getExercises = async () => { - const result = await Promise.all( - exerciseLanguages.map(async (language) => { - const languagePath = path.join(EVALS_REPO_PATH, language) - const exercises = await listDirectories(__dirname, languagePath) - return exercises.map((exercise) => `${language}/${exercise}`) - }), - ) - - return result.flat() -} diff --git a/apps/web-evals/src/actions/heartbeat.ts b/apps/web-evals/src/actions/heartbeat.ts deleted file mode 100644 index a74aa8ee64..0000000000 --- a/apps/web-evals/src/actions/heartbeat.ts +++ /dev/null @@ -1,8 +0,0 @@ -"use server" - -import { redisClient } from "@/lib/server/redis" - -export const getHeartbeat = async (runId: number) => { - const redis = await redisClient() - return redis.get(`heartbeat:${runId}`) -} diff --git a/apps/web-evals/src/actions/runners.ts b/apps/web-evals/src/actions/runners.ts deleted file mode 100644 index 8b7e86b0f3..0000000000 --- a/apps/web-evals/src/actions/runners.ts +++ /dev/null @@ -1,8 +0,0 @@ -"use server" - -import { redisClient } from "@/lib/server/redis" - -export const getRunners = async (runId: number) => { - const redis = await redisClient() - return redis.sMembers(`runners:${runId}`) -} diff --git a/apps/web-evals/src/actions/runs.ts b/apps/web-evals/src/actions/runs.ts deleted file mode 100644 index f0c1578aed..0000000000 --- a/apps/web-evals/src/actions/runs.ts +++ /dev/null @@ -1,377 +0,0 @@ -"use server" - -import * as path from "path" -import fs from "fs" -import { fileURLToPath } from "url" -import { spawn, execFileSync } from "child_process" - -import { revalidatePath } from "next/cache" -import pMap from "p-map" - -import { - type ExerciseLanguage, - exerciseLanguages, - createRun as _createRun, - deleteRun as _deleteRun, - updateRun as _updateRun, - getIncompleteRuns as _getIncompleteRuns, - deleteRunsByIds as _deleteRunsByIds, - createTask, - getExercisesForLanguage, -} from "@roo-code/evals" - -import { CreateRun } from "@/lib/schemas" -import { redisClient } from "@/lib/server/redis" - -// Storage base path for eval logs -const EVALS_STORAGE_PATH = "/tmp/evals/runs" - -const EVALS_REPO_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../../../evals") - -export async function createRun({ - suite, - exercises = [], - timeout, - iterations = 1, - executionMethod = "vscode", - ...values -}: CreateRun) { - const run = await _createRun({ - ...values, - timeout, - executionMethod, - socketPath: "", // TODO: Get rid of this. - }) - - if (suite === "partial") { - for (const path of exercises) { - const [language, exercise] = path.split("/") - - if (!language || !exercise) { - throw new Error("Invalid exercise path: " + path) - } - - // Create multiple tasks for each iteration - for (let iteration = 1; iteration <= iterations; iteration++) { - await createTask({ - ...values, - runId: run.id, - language: language as ExerciseLanguage, - exercise, - iteration, - }) - } - } - } else { - for (const language of exerciseLanguages) { - const languageExercises = await getExercisesForLanguage(EVALS_REPO_PATH, language) - - // Create tasks for all iterations of each exercise - const tasksToCreate: Array<{ language: ExerciseLanguage; exercise: string; iteration: number }> = [] - for (const exercise of languageExercises) { - for (let iteration = 1; iteration <= iterations; iteration++) { - tasksToCreate.push({ language, exercise, iteration }) - } - } - - await pMap( - tasksToCreate, - ({ language, exercise, iteration }) => createTask({ runId: run.id, language, exercise, iteration }), - { concurrency: 10 }, - ) - } - } - - revalidatePath("/runs") - - try { - const isRunningInDocker = fs.existsSync("/.dockerenv") - - const dockerArgs = [ - `--name evals-controller-${run.id}`, - "--rm", - "--network evals_default", - "-v /var/run/docker.sock:/var/run/docker.sock", - "-v /tmp/evals:/var/log/evals", - "-e HOST_EXECUTION_METHOD=docker", - ] - - const cliCommand = `pnpm --filter @roo-code/evals cli --runId ${run.id}` - - const command = isRunningInDocker - ? `docker run ${dockerArgs.join(" ")} evals-runner sh -c "${cliCommand}"` - : cliCommand - - console.log("spawn ->", command) - - const childProcess = spawn("sh", ["-c", command], { - detached: true, - stdio: ["ignore", "pipe", "pipe"], - }) - - const logStream = fs.createWriteStream("/tmp/roo-code-evals.log", { flags: "a" }) - - if (childProcess.stdout) { - childProcess.stdout.pipe(logStream) - } - - if (childProcess.stderr) { - childProcess.stderr.pipe(logStream) - } - - childProcess.unref() - } catch (error) { - console.error(error) - } - - return run -} - -export async function deleteRun(runId: number) { - await _deleteRun(runId) - revalidatePath("/runs") -} - -export type KillRunResult = { - success: boolean - killedContainers: string[] - errors: string[] -} - -const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) - -/** - * Kill all Docker containers associated with a run (controller and task runners). - * Kills the controller first, waits 10 seconds, then kills runners. - * Also clears Redis state for heartbeat and runners. - * - * Container naming conventions: - * - Controller: evals-controller-{runId} - * - Task runners: evals-task-{runId}-{taskId}.{attempt} - */ -export async function killRun(runId: number): Promise { - const killedContainers: string[] = [] - const errors: string[] = [] - const controllerPattern = `evals-controller-${runId}` - const taskPattern = `evals-task-${runId}-` - - try { - // Step 1: Kill the controller first - console.log(`Killing controller: ${controllerPattern}`) - try { - execFileSync("docker", ["kill", controllerPattern], { encoding: "utf-8", timeout: 10000 }) - killedContainers.push(controllerPattern) - console.log(`Killed controller container: ${controllerPattern}`) - } catch (_error) { - // Controller might not be running - that's ok, continue to kill runners - console.log(`Controller ${controllerPattern} not running or already stopped`) - } - - // Step 2: Wait 10 seconds before killing runners - console.log("Waiting 10 seconds before killing runners...") - await sleep(10000) - - // Step 3: Find and kill all task runner containers for THIS run only - let taskContainerNames: string[] = [] - - try { - const output = execFileSync("docker", ["ps", "--format", "{{.Names}}", "--filter", `name=${taskPattern}`], { - encoding: "utf-8", - timeout: 10000, - }) - taskContainerNames = output - .split("\n") - .map((name) => name.trim()) - .filter((name) => name.length > 0 && name.startsWith(taskPattern)) - } catch (error) { - console.error("Failed to list task containers:", error) - errors.push("Failed to list Docker task containers") - } - - // Kill each task runner container - for (const containerName of taskContainerNames) { - try { - execFileSync("docker", ["kill", containerName], { encoding: "utf-8", timeout: 10000 }) - killedContainers.push(containerName) - console.log(`Killed task container: ${containerName}`) - } catch (error) { - // Container might have already stopped - console.error(`Failed to kill container ${containerName}:`, error) - errors.push(`Failed to kill container: ${containerName}`) - } - } - - // Step 4: Clear Redis state - try { - const redis = await redisClient() - const heartbeatKey = `heartbeat:${runId}` - const runnersKey = `runners:${runId}` - - await redis.del(heartbeatKey) - await redis.del(runnersKey) - console.log(`Cleared Redis keys: ${heartbeatKey}, ${runnersKey}`) - } catch (error) { - console.error("Failed to clear Redis state:", error) - errors.push("Failed to clear Redis state") - } - } catch (error) { - console.error("Error in killRun:", error) - errors.push("Unexpected error while killing containers") - } - - revalidatePath(`/runs/${runId}`) - revalidatePath("/runs") - - return { - success: killedContainers.length > 0 || errors.length === 0, - killedContainers, - errors, - } -} - -export type DeleteIncompleteRunsResult = { - success: boolean - deletedCount: number - deletedRunIds: number[] - storageErrors: string[] -} - -/** - * Delete all incomplete runs (runs without a taskMetricsId/final score). - * Removes both database records and storage folders. - */ -export async function deleteIncompleteRuns(): Promise { - const storageErrors: string[] = [] - - // Get all incomplete runs - const incompleteRuns = await _getIncompleteRuns() - const runIds = incompleteRuns.map((run) => run.id) - - if (runIds.length === 0) { - return { - success: true, - deletedCount: 0, - deletedRunIds: [], - storageErrors: [], - } - } - - // Delete storage folders for each run - for (const runId of runIds) { - const storagePath = path.join(EVALS_STORAGE_PATH, String(runId)) - try { - if (fs.existsSync(storagePath)) { - fs.rmSync(storagePath, { recursive: true, force: true }) - console.log(`Deleted storage folder: ${storagePath}`) - } - } catch (error) { - console.error(`Failed to delete storage folder ${storagePath}:`, error) - storageErrors.push(`Failed to delete storage for run ${runId}`) - } - - // Also try to clear Redis state for any potentially running incomplete runs - try { - const redis = await redisClient() - await redis.del(`heartbeat:${runId}`) - await redis.del(`runners:${runId}`) - } catch (error) { - // Non-critical error, just log it - console.error(`Failed to clear Redis state for run ${runId}:`, error) - } - } - - // Delete from database - await _deleteRunsByIds(runIds) - - revalidatePath("/runs") - - return { - success: true, - deletedCount: runIds.length, - deletedRunIds: runIds, - storageErrors, - } -} - -/** - * Get count of incomplete runs (for UI display) - */ -export async function getIncompleteRunsCount(): Promise { - const incompleteRuns = await _getIncompleteRuns() - return incompleteRuns.length -} - -/** - * Delete all runs older than 30 days. - * Removes both database records and storage folders. - */ -export async function deleteOldRuns(): Promise { - const storageErrors: string[] = [] - - // Get all runs older than 30 days - const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) - const { getRuns } = await import("@roo-code/evals") - const allRuns = await getRuns() - const oldRuns = allRuns.filter((run) => run.createdAt < thirtyDaysAgo) - const runIds = oldRuns.map((run) => run.id) - - if (runIds.length === 0) { - return { - success: true, - deletedCount: 0, - deletedRunIds: [], - storageErrors: [], - } - } - - // Delete storage folders for each run - for (const runId of runIds) { - const storagePath = path.join(EVALS_STORAGE_PATH, String(runId)) - try { - if (fs.existsSync(storagePath)) { - fs.rmSync(storagePath, { recursive: true, force: true }) - console.log(`Deleted storage folder: ${storagePath}`) - } - } catch (error) { - console.error(`Failed to delete storage folder ${storagePath}:`, error) - storageErrors.push(`Failed to delete storage for run ${runId}`) - } - - // Also try to clear Redis state - try { - const redis = await redisClient() - await redis.del(`heartbeat:${runId}`) - await redis.del(`runners:${runId}`) - } catch (error) { - // Non-critical error, just log it - console.error(`Failed to clear Redis state for run ${runId}:`, error) - } - } - - // Delete from database - await _deleteRunsByIds(runIds) - - revalidatePath("/runs") - - return { - success: true, - deletedCount: runIds.length, - deletedRunIds: runIds, - storageErrors, - } -} - -/** - * Update the description of a run. - */ -export async function updateRunDescription(runId: number, description: string | null): Promise<{ success: boolean }> { - try { - await _updateRun(runId, { description }) - revalidatePath("/runs") - revalidatePath(`/runs/${runId}`) - return { success: true } - } catch (error) { - console.error("Failed to update run description:", error) - return { success: false } - } -} diff --git a/apps/web-evals/src/actions/tasks.ts b/apps/web-evals/src/actions/tasks.ts deleted file mode 100644 index 18b428b0ca..0000000000 --- a/apps/web-evals/src/actions/tasks.ts +++ /dev/null @@ -1,11 +0,0 @@ -"use server" - -import { revalidatePath } from "next/cache" - -import { getTasks as _getTasks } from "@roo-code/evals" - -export async function getTasks(runId: number) { - const tasks = await _getTasks(runId) - revalidatePath(`/runs/${runId}`) - return tasks -} diff --git a/apps/web-evals/src/app/api/runs/[id]/logs/[taskId]/route.ts b/apps/web-evals/src/app/api/runs/[id]/logs/[taskId]/route.ts deleted file mode 100644 index e5ec8751ab..0000000000 --- a/apps/web-evals/src/app/api/runs/[id]/logs/[taskId]/route.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { NextResponse } from "next/server" -import type { NextRequest } from "next/server" -import * as fs from "node:fs/promises" -import * as path from "node:path" - -import { findTask, findRun } from "@roo-code/evals" - -export const dynamic = "force-dynamic" - -const LOG_BASE_PATH = "/tmp/evals/runs" - -// Sanitize path components to prevent path traversal attacks -function sanitizePathComponent(component: string): string { - // Remove any path separators, null bytes, and other dangerous characters - return component.replace(/[/\\:\0*?"<>|]/g, "_") -} - -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string; taskId: string }> }) { - const { id, taskId } = await params - - try { - const runId = Number(id) - const taskIdNum = Number(taskId) - - if (isNaN(runId) || isNaN(taskIdNum)) { - return NextResponse.json({ error: "Invalid run ID or task ID" }, { status: 400 }) - } - - // Verify the run exists - await findRun(runId) - - // Get the task to find its language and exercise - const task = await findTask(taskIdNum) - - // Verify the task belongs to this run - if (task.runId !== runId) { - return NextResponse.json({ error: "Task does not belong to this run" }, { status: 404 }) - } - - // Sanitize language and exercise to prevent path traversal - const safeLanguage = sanitizePathComponent(task.language) - const safeExercise = sanitizePathComponent(task.exercise) - - // Construct the log file path - const logFileName = `${safeLanguage}-${safeExercise}.log` - const logFilePath = path.join(LOG_BASE_PATH, String(runId), logFileName) - - // Verify the resolved path is within the expected directory (defense in depth) - const resolvedPath = path.resolve(logFilePath) - const expectedBase = path.resolve(LOG_BASE_PATH) - if (!resolvedPath.startsWith(expectedBase)) { - return NextResponse.json({ error: "Invalid log path" }, { status: 400 }) - } - - // Check if the log file exists and read it (async) - try { - const logContent = await fs.readFile(logFilePath, "utf-8") - return NextResponse.json({ logContent }) - } catch (err) { - if ((err as NodeJS.ErrnoException).code === "ENOENT") { - return NextResponse.json({ error: "Log file not found", logContent: null }, { status: 200 }) - } - throw err - } - } catch (error) { - console.error("Error reading task log:", error) - - if (error instanceof Error && error.name === "RecordNotFoundError") { - return NextResponse.json({ error: "Task or run not found" }, { status: 404 }) - } - - return NextResponse.json({ error: "Failed to read log file" }, { status: 500 }) - } -} diff --git a/apps/web-evals/src/app/api/runs/[id]/logs/failed/route.ts b/apps/web-evals/src/app/api/runs/[id]/logs/failed/route.ts deleted file mode 100644 index 8b2760df98..0000000000 --- a/apps/web-evals/src/app/api/runs/[id]/logs/failed/route.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { NextResponse } from "next/server" -import type { NextRequest } from "next/server" -import * as fs from "node:fs" -import * as path from "node:path" -import archiver from "archiver" - -import { findRun, getTasks } from "@roo-code/evals" - -export const dynamic = "force-dynamic" - -const LOG_BASE_PATH = "/tmp/evals/runs" - -// Sanitize path components to prevent path traversal attacks -function sanitizePathComponent(component: string): string { - // Remove any path separators, null bytes, and other dangerous characters - return component.replace(/[/\\:\0*?"<>|]/g, "_") -} - -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const { id } = await params - - try { - const runId = Number(id) - - if (isNaN(runId)) { - return NextResponse.json({ error: "Invalid run ID" }, { status: 400 }) - } - - // Verify the run exists - await findRun(runId) - - // Get all tasks for this run - const tasks = await getTasks(runId) - - // Filter for failed tasks only - const failedTasks = tasks.filter((task) => task.passed === false) - - if (failedTasks.length === 0) { - return NextResponse.json({ error: "No failed tasks to export" }, { status: 400 }) - } - - // Create a zip archive - const archive = archiver("zip", { zlib: { level: 9 } }) - - // Collect chunks to build the response - const chunks: Buffer[] = [] - - archive.on("data", (chunk: Buffer) => { - chunks.push(chunk) - }) - - // Track archive errors - let archiveError: Error | null = null - archive.on("error", (err: Error) => { - archiveError = err - }) - - // Set up the end promise before finalizing (proper event listener ordering) - const archiveEndPromise = new Promise((resolve, reject) => { - archive.on("end", resolve) - archive.on("error", reject) - }) - - // Add each failed task's log file and history files to the archive - const logDir = path.join(LOG_BASE_PATH, String(runId)) - let filesAdded = 0 - - for (const task of failedTasks) { - // Sanitize language and exercise to prevent path traversal - const safeLanguage = sanitizePathComponent(task.language) - const safeExercise = sanitizePathComponent(task.exercise) - const expectedBase = path.resolve(LOG_BASE_PATH) - - // Add the log file - const logFileName = `${safeLanguage}-${safeExercise}.log` - const logFilePath = path.join(logDir, logFileName) - - // Verify the resolved path is within the expected directory (defense in depth) - const resolvedLogPath = path.resolve(logFilePath) - if (resolvedLogPath.startsWith(expectedBase) && fs.existsSync(logFilePath)) { - archive.file(logFilePath, { name: logFileName }) - filesAdded++ - } - - // Add the API conversation history file - // Format: {language}-{exercise}.{iteration}_api_conversation_history.json - const apiHistoryFileName = `${safeLanguage}-${safeExercise}.${task.iteration}_api_conversation_history.json` - const apiHistoryFilePath = path.join(logDir, apiHistoryFileName) - const resolvedApiHistoryPath = path.resolve(apiHistoryFilePath) - if (resolvedApiHistoryPath.startsWith(expectedBase) && fs.existsSync(apiHistoryFilePath)) { - archive.file(apiHistoryFilePath, { name: apiHistoryFileName }) - filesAdded++ - } - - // Add the UI messages file - // Format: {language}-{exercise}.{iteration}_ui_messages.json - const uiMessagesFileName = `${safeLanguage}-${safeExercise}.${task.iteration}_ui_messages.json` - const uiMessagesFilePath = path.join(logDir, uiMessagesFileName) - const resolvedUiMessagesPath = path.resolve(uiMessagesFilePath) - if (resolvedUiMessagesPath.startsWith(expectedBase) && fs.existsSync(uiMessagesFilePath)) { - archive.file(uiMessagesFilePath, { name: uiMessagesFileName }) - filesAdded++ - } - } - - // Check if any files were actually added - if (filesAdded === 0) { - archive.abort() - return NextResponse.json( - { error: "No log files found - they may have been cleared from disk" }, - { status: 404 }, - ) - } - - // Finalize the archive - await archive.finalize() - - // Wait for all data to be collected - await archiveEndPromise - - // Check for archive errors - if (archiveError) { - throw archiveError - } - - // Combine all chunks into a single buffer - const zipBuffer = Buffer.concat(chunks) - - // Return the zip file - return new NextResponse(zipBuffer, { - status: 200, - headers: { - "Content-Type": "application/zip", - "Content-Disposition": `attachment; filename="run-${runId}-failed-logs.zip"`, - "Content-Length": String(zipBuffer.length), - }, - }) - } catch (error) { - console.error("Error exporting failed logs:", error) - - if (error instanceof Error && error.name === "RecordNotFoundError") { - return NextResponse.json({ error: "Run not found" }, { status: 404 }) - } - - return NextResponse.json({ error: "Failed to export logs" }, { status: 500 }) - } -} diff --git a/apps/web-evals/src/app/api/runs/[id]/stream/route.ts b/apps/web-evals/src/app/api/runs/[id]/stream/route.ts deleted file mode 100644 index 3168974ecd..0000000000 --- a/apps/web-evals/src/app/api/runs/[id]/stream/route.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { NextRequest } from "next/server" - -import { taskEventSchema } from "@roo-code/types" -import { findRun } from "@roo-code/evals" - -import { SSEStream } from "@/lib/server/sse-stream" -import { redisClient } from "@/lib/server/redis" - -export const dynamic = "force-dynamic" - -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const requestId = crypto.randomUUID() - const stream = new SSEStream() - const run = await findRun(Number(id)) - const redis = await redisClient() - - let isStreamClosed = false - const channelName = `evals:${run.id}` - - const onMessage = async (data: string) => { - if (isStreamClosed || stream.isClosed) { - return - } - - try { - const taskEvent = taskEventSchema.parse(JSON.parse(data)) - // console.log(`[stream#${requestId}] task event -> ${taskEvent.eventName}`) - const writeSuccess = await stream.write(JSON.stringify(taskEvent)) - - if (!writeSuccess) { - await disconnect() - } - } catch (_error) { - console.error(`[stream#${requestId}] invalid task event:`, data) - } - } - - const disconnect = async () => { - if (isStreamClosed) { - return - } - - isStreamClosed = true - - try { - await redis.unsubscribe(channelName) - console.log(`[stream#${requestId}] unsubscribed from ${channelName}`) - } catch (error) { - console.error(`[stream#${requestId}] error unsubscribing:`, error) - } - - try { - await stream.close() - } catch (error) { - console.error(`[stream#${requestId}] error closing stream:`, error) - } - } - - await redis.subscribe(channelName, onMessage) - - request.signal.addEventListener("abort", () => { - console.log(`[stream#${requestId}] abort`) - - disconnect().catch((error) => { - console.error(`[stream#${requestId}] cleanup error:`, error) - }) - }) - - return stream.getResponse() -} diff --git a/apps/web-evals/src/app/favicon.ico b/apps/web-evals/src/app/favicon.ico deleted file mode 100644 index 718d6fea48..0000000000 Binary files a/apps/web-evals/src/app/favicon.ico and /dev/null differ diff --git a/apps/web-evals/src/app/globals.css b/apps/web-evals/src/app/globals.css deleted file mode 100644 index 8c12f0d1d2..0000000000 --- a/apps/web-evals/src/app/globals.css +++ /dev/null @@ -1,141 +0,0 @@ -@import "tailwindcss"; - -@plugin "tailwindcss-animate"; - -@custom-variant dark (&:is(.dark *)); - -:root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); -} - -.dark { - --background: oklch(23.66% 0.0198 271.79); - --foreground: oklch(75.15% 0.0477 278.41); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: var(--primary); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(29.33% 0.0295 276.18); - --primary-foreground: var(--accent); - --secondary: var(--primary); - --secondary-foreground: var(--foreground); - --muted: oklch(28.27% 0.0207 273.06); - --muted-foreground: oklch(75.15% 0.0477 278.41 / 75%); - --accent: oklch(70.21% 0.1813 328.71); - --accent-foreground: oklch(1 0 0 / 75%); - --destructive: oklch(72.14% 0.1616 15.49); - --border: var(--primary); - --input: var(--primary); - --ring: oklch(83.63% 0.1259 176.52); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-sidebar-ring: var(--sidebar-ring); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar: var(--sidebar); - --color-chart-5: var(--chart-5); - --color-chart-4: var(--chart-4); - --color-chart-3: var(--chart-3); - --color-chart-2: var(--chart-2); - --color-chart-1: var(--chart-1); - --color-ring: var(--ring); - --color-input: var(--input); - --color-border: var(--border); - --color-destructive: var(--destructive); - --color-accent-foreground: var(--accent-foreground); - --color-accent: var(--accent); - --color-muted-foreground: var(--muted-foreground); - --color-muted: var(--muted); - --color-secondary-foreground: var(--secondary-foreground); - --color-secondary: var(--secondary); - --color-primary-foreground: var(--primary-foreground); - --color-primary: var(--primary); - --color-popover-foreground: var(--popover-foreground); - --color-popover: var(--popover); - --color-card-foreground: var(--card-foreground); - --color-card: var(--card); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - - --animate-hop: hop 0.8s ease-in-out infinite; - - @keyframes hop { - 0%, - 100% { - transform: none; - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: translateY(-8px); - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } -} - -@layer base { - * { - @apply border-border outline-ring/50; - } - html, - body { - height: 100%; - } - body { - @apply bg-background text-foreground; - scrollbar-color: rgba(0, 0, 0, 0.2) transparent; /* Firefox */ - scrollbar-width: thin; - } -} diff --git a/apps/web-evals/src/app/layout.tsx b/apps/web-evals/src/app/layout.tsx deleted file mode 100644 index 3bb34f7dfb..0000000000 --- a/apps/web-evals/src/app/layout.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { Metadata } from "next" -import { Geist, Geist_Mono } from "next/font/google" - -import { ThemeProvider, ReactQueryProvider } from "@/components/providers" -import { Toaster } from "@/components/ui" -import { Header } from "@/components/layout/header" - -import "./globals.css" - -const fontSans = Geist({ variable: "--font-sans", subsets: ["latin"] }) -const fontMono = Geist_Mono({ variable: "--font-mono", subsets: ["latin"] }) - -export const metadata: Metadata = { - title: "Roo Code Evals", -} - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode -}>) { - return ( - - - - -
- {children} - - - - - - ) -} diff --git a/apps/web-evals/src/app/page.tsx b/apps/web-evals/src/app/page.tsx deleted file mode 100644 index 3dcb26aebf..0000000000 --- a/apps/web-evals/src/app/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { getRuns } from "@roo-code/evals" - -import { Runs } from "@/components/home/runs" - -export const dynamic = "force-dynamic" - -export default async function Page() { - const runs = await getRuns() - return -} diff --git a/apps/web-evals/src/app/runs/[id]/page.tsx b/apps/web-evals/src/app/runs/[id]/page.tsx deleted file mode 100644 index 8b993eec8a..0000000000 --- a/apps/web-evals/src/app/runs/[id]/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { findRun } from "@roo-code/evals" - -import { Run } from "./run" - -export default async function Page({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const run = await findRun(Number(id)) - - return ( -
- -
- ) -} diff --git a/apps/web-evals/src/app/runs/[id]/run-status.tsx b/apps/web-evals/src/app/runs/[id]/run-status.tsx deleted file mode 100644 index e05b1b51eb..0000000000 --- a/apps/web-evals/src/app/runs/[id]/run-status.tsx +++ /dev/null @@ -1,79 +0,0 @@ -"use client" - -import { Link2, Link2Off, CheckCircle2 } from "lucide-react" -import type { RunStatus as _RunStatus } from "@/hooks/use-run-status" -import { cn } from "@/lib/utils" -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui" - -function StreamIcon({ status }: { status: "connected" | "waiting" | "error" }) { - if (status === "connected") { - return - } - return -} - -export const RunStatus = ({ - runStatus: { sseStatus, heartbeat, runners = [] }, - isComplete = false, -}: { - runStatus: _RunStatus - isComplete?: boolean -}) => { - // For completed runs, show a simple "Complete" badge - if (isComplete) { - return ( - - -
- -
-
- - Run complete - -
- ) - } - - return ( - - -
- {/* Task Stream status icon */} - - - {/* Task Controller ID */} - {heartbeat ?? "-"} - - {/* Task Runners count */} - 0 ? "text-green-500" : "text-rose-500"}> - {runners.length > 0 ? `${runners.length}r` : "0r"} - -
-
- -
-
- - Task Stream: {sseStatus} -
-
- - Task Controller: {heartbeat ?? "dead"} -
-
- 0 ? "text-green-500" : "text-rose-500"}>● - Task Runners: {runners.length > 0 ? runners.length : "none"} -
- {runners.length > 0 && ( -
- {runners.map((runner) => ( -
{runner}
- ))} -
- )} -
-
-
- ) -} diff --git a/apps/web-evals/src/app/runs/[id]/run.tsx b/apps/web-evals/src/app/runs/[id]/run.tsx deleted file mode 100644 index badd77741e..0000000000 --- a/apps/web-evals/src/app/runs/[id]/run.tsx +++ /dev/null @@ -1,1058 +0,0 @@ -"use client" - -import { useMemo, useState, useCallback, useEffect, Fragment } from "react" -import { toast } from "sonner" -import { LoaderCircle, FileText, Copy, Check, StopCircle, List, Layers } from "lucide-react" - -import type { Run, TaskMetrics as _TaskMetrics, Task } from "@roo-code/evals" -import type { ToolName } from "@roo-code/types" - -import { formatCurrency, formatDuration, formatTokens, formatToolUsageSuccessRate } from "@/lib/formatters" -import { useRunStatus } from "@/hooks/use-run-status" -import { killRun } from "@/actions/runs" -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, - Tooltip, - TooltipContent, - TooltipTrigger, - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - ScrollArea, - Button, - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui" - -import { TaskStatus } from "./task-status" -import { RunStatus } from "./run-status" - -type TaskMetrics = Pick<_TaskMetrics, "tokensIn" | "tokensOut" | "tokensContext" | "duration" | "cost"> - -// Extended Task type with taskMetrics from useRunStatus -type TaskWithMetrics = Task & { taskMetrics: _TaskMetrics | null } - -type ToolUsageEntry = { attempts: number; failures: number } -type ToolUsage = Record - -// Generate abbreviation from tool name (e.g., "read_file" -> "RF", "list_code_definition_names" -> "LCDN") -function getToolAbbreviation(toolName: string): string { - return toolName - .split("_") - .map((word) => word[0]?.toUpperCase() ?? "") - .join("") -} - -// Pattern definitions for syntax highlighting -type HighlightPattern = { - pattern: RegExp - className: string - // If true, wraps the entire match; if a number, wraps that capture group - wrapGroup?: number -} - -const HIGHLIGHT_PATTERNS: HighlightPattern[] = [ - // Log levels - styled as badges - { pattern: /\|\s*(INFO)\s*\|/g, className: "text-green-400", wrapGroup: 1 }, - { pattern: /\|\s*(WARN|WARNING)\s*\|/g, className: "text-yellow-400", wrapGroup: 1 }, - { pattern: /\|\s*(ERROR)\s*\|/g, className: "text-red-400 font-semibold", wrapGroup: 1 }, - { pattern: /\|\s*(DEBUG)\s*\|/g, className: "text-gray-400", wrapGroup: 1 }, - // Task identifiers - important events - { - pattern: /(taskCreated|taskFocused|taskStarted|taskCompleted|taskAborted|taskResumable)/g, - className: "text-purple-400 font-medium", - }, - // Tool failures - highlight in red - { pattern: /(taskToolFailed)/g, className: "text-red-400 font-bold" }, - { pattern: /(Tool execution failed|tool.*failed|failed.*tool)/gi, className: "text-red-400" }, - { pattern: /(EvalPass)/g, className: "text-green-400 font-bold" }, - { pattern: /(EvalFail)/g, className: "text-red-400 font-bold" }, - // Message arrows - { pattern: /→/g, className: "text-cyan-400" }, - // Tool names in quotes - { pattern: /"(tool)":\s*"([^"]+)"/g, className: "text-orange-400" }, - // JSON keys - { pattern: /"([^"]+)":/g, className: "text-sky-300" }, - // Boolean values - { pattern: /:\s*(true|false)/g, className: "text-amber-400", wrapGroup: 1 }, - // Numbers - { pattern: /:\s*(-?\d+\.?\d*)/g, className: "text-emerald-400", wrapGroup: 1 }, -] - -// Extract timestamp from a log line and return elapsed time from baseline -function formatElapsedTime(timestamp: string, baselineMs: number): string { - const currentMs = new Date(timestamp).getTime() - const elapsedMs = currentMs - baselineMs - const totalSeconds = Math.floor(elapsedMs / 1000) - const minutes = Math.floor(totalSeconds / 60) - const seconds = totalSeconds % 60 - return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}` -} - -// Extract the first timestamp from the log to use as baseline -function extractFirstTimestamp(log: string): number | null { - // Match timestamp at start of line: [2025-11-28T09:35:23.187Z | ... or [2025-11-28T09:35:23.187Z] - const match = log.match(/\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)[\s|\]]/) - const isoString = match?.[1] - if (!isoString) return null - return new Date(isoString).getTime() -} - -// Simplify log line by removing redundant metadata -function simplifyLogLine(line: string, baselineMs: number | null): { timestamp: string; simplified: string } { - // Extract timestamp - matches [2025-11-28T09:35:23.187Z | ... format - const timestampMatch = line.match(/\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)[\s|\]]/) - const isoTimestamp = timestampMatch?.[1] - if (!isoTimestamp) { - return { timestamp: "", simplified: line } - } - - const timestamp = baselineMs !== null ? formatElapsedTime(isoTimestamp, baselineMs) : isoTimestamp.slice(11, 19) - - // Remove the timestamp from the line (handles both [timestamp] and [timestamp | formats) - let simplified = line.replace(/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\s*\|?\s*/, "") - - // Remove redundant metadata: pid, run, task IDs (they're same for entire log) - simplified = simplified.replace(/\|\s*pid:\d+\s*/g, "") - simplified = simplified.replace(/\|\s*run:\d+\s*/g, "") - simplified = simplified.replace(/\|\s*task:\d+\s*/g, "") - simplified = simplified.replace(/runTask\s*\|\s*/g, "") - - // Clean up extra pipes, spaces, and trailing brackets - simplified = simplified.replace(/\|\s*\|/g, "|") - simplified = simplified.replace(/^\s*\|\s*/, "") - simplified = simplified.replace(/\]\s*$/, "") // Remove trailing bracket if present - - return { timestamp, simplified } -} - -// Format a single line with syntax highlighting using React elements (XSS-safe) -function formatLine(line: string): React.ReactNode[] { - // Find all matches with their positions - type Match = { start: number; end: number; text: string; className: string } - const matches: Match[] = [] - - for (const { pattern, className, wrapGroup } of HIGHLIGHT_PATTERNS) { - // Reset regex state - pattern.lastIndex = 0 - let regexMatch - while ((regexMatch = pattern.exec(line)) !== null) { - const capturedText = wrapGroup !== undefined ? regexMatch[wrapGroup] : regexMatch[0] - // Skip if capture group didn't match - if (!capturedText) continue - const start = - wrapGroup !== undefined ? regexMatch.index + regexMatch[0].indexOf(capturedText) : regexMatch.index - matches.push({ - start, - end: start + capturedText.length, - text: capturedText, - className, - }) - } - } - - // Sort matches by position and filter overlapping ones - matches.sort((a, b) => a.start - b.start) - const filteredMatches: Match[] = [] - for (const m of matches) { - const lastMatch = filteredMatches[filteredMatches.length - 1] - if (!lastMatch || m.start >= lastMatch.end) { - filteredMatches.push(m) - } - } - - // Build result with highlighted spans - const result: React.ReactNode[] = [] - let currentPos = 0 - - for (const [i, m] of filteredMatches.entries()) { - // Add text before this match - if (m.start > currentPos) { - result.push(line.slice(currentPos, m.start)) - } - // Add highlighted match - result.push( - - {m.text} - , - ) - currentPos = m.end - } - - // Add remaining text - if (currentPos < line.length) { - result.push(line.slice(currentPos)) - } - - return result.length > 0 ? result : [line] -} - -// Determine the visual style for a log line based on its content -function getLineStyle(line: string): string { - if (line.includes("ERROR")) return "bg-red-950/30 border-l-2 border-red-500" - if (line.includes("WARN") || line.includes("WARNING")) return "bg-yellow-950/20 border-l-2 border-yellow-500" - if (line.includes("taskToolFailed")) return "bg-red-950/30 border-l-2 border-red-500" - if (line.includes("taskStarted") || line.includes("taskCreated")) return "bg-purple-950/20" - if (line.includes("EvalPass")) return "bg-green-950/30 border-l-2 border-green-500" - if (line.includes("EvalFail")) return "bg-red-950/30 border-l-2 border-red-500" - if (line.includes("taskCompleted") || line.includes("taskAborted")) return "bg-blue-950/20" - return "" -} - -// Format log content with basic highlighting (XSS-safe - no dangerouslySetInnerHTML) -function formatLogContent(log: string): React.ReactNode[] { - const lines = log.split("\n") - const baselineMs = extractFirstTimestamp(log) - - return lines.map((line, index) => { - if (!line.trim()) { - return ( -
- {" "} -
- ) - } - - const parsed = simplifyLogLine(line, baselineMs) - const lineStyle = getLineStyle(line) - - return ( -
- {/* Elapsed time */} - - {parsed.timestamp} - - {/* Log content - pl-12 ensures wrapped lines are indented under the timestamp */} - - {formatLine(parsed.simplified)} - -
- ) - }) -} - -export function Run({ run }: { run: Run }) { - const runStatus = useRunStatus(run) - const { tasks, tokenUsage, toolUsage, usageUpdatedAt, heartbeat, runners } = runStatus - - const [selectedTask, setSelectedTask] = useState(null) - const [taskLog, setTaskLog] = useState(null) - const [isLoadingLog, setIsLoadingLog] = useState(false) - const [copied, setCopied] = useState(false) - const [showKillDialog, setShowKillDialog] = useState(false) - const [isKilling, setIsKilling] = useState(false) - const [groupByStatus, setGroupByStatus] = useState(() => { - // Initialize from localStorage if available (client-side only) - if (typeof window !== "undefined") { - const stored = localStorage.getItem("evals-group-by-status") - return stored === "true" - } - return false - }) - - // Persist groupByStatus to localStorage - useEffect(() => { - localStorage.setItem("evals-group-by-status", String(groupByStatus)) - }, [groupByStatus]) - - // Determine if run is still active (has heartbeat or runners) - const isRunActive = !run.taskMetricsId && (!!heartbeat || (runners && runners.length > 0)) - - const onKillRun = useCallback(async () => { - setIsKilling(true) - try { - const result = await killRun(run.id) - if (result.killedContainers.length > 0) { - toast.success(`Killed ${result.killedContainers.length} container(s)`) - } else if (result.errors.length === 0) { - toast.info("No running containers found") - } else { - toast.error(result.errors.join(", ")) - } - } catch (error) { - console.error("Failed to kill run:", error) - toast.error("Failed to kill run") - } finally { - setIsKilling(false) - setShowKillDialog(false) - } - }, [run.id]) - - const onCopyLog = useCallback(async () => { - if (!taskLog) return - - try { - await navigator.clipboard.writeText(taskLog) - setCopied(true) - toast.success("Log copied to clipboard") - setTimeout(() => setCopied(false), 2000) - } catch (error) { - console.error("Failed to copy log:", error) - toast.error("Failed to copy log") - } - }, [taskLog]) - - // Handle ESC key to close the dialog - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === "Escape" && selectedTask) { - setSelectedTask(null) - } - } - - document.addEventListener("keydown", handleKeyDown) - return () => document.removeEventListener("keydown", handleKeyDown) - }, [selectedTask]) - - const taskMetrics: Record = useMemo(() => { - // Reference usageUpdatedAt to trigger recomputation when Map contents change - void usageUpdatedAt - const metrics: Record = {} - - // Helper to calculate duration from database timestamps when streaming duration - // is unavailable (e.g., page was loaded after TaskStarted event was published) - const calculateDurationFromTimestamps = (task: TaskWithMetrics): number => { - if (!task.startedAt) return 0 - const startTime = new Date(task.startedAt).getTime() - const endTime = task.finishedAt ? new Date(task.finishedAt).getTime() : Date.now() - return endTime - startTime - } - - tasks?.forEach((task) => { - const streamingUsage = tokenUsage.get(task.id) - const dbMetrics = task.taskMetrics - - // For finished tasks, prefer DB values but fall back to streaming values - // This handles race conditions during timeout where DB might not have latest data - if (task.finishedAt) { - // Check if DB metrics have meaningful values (not just default/empty) - const dbHasData = dbMetrics && (dbMetrics.tokensIn > 0 || dbMetrics.tokensOut > 0 || dbMetrics.cost > 0) - if (dbHasData) { - // If DB duration is 0 but we have timestamps, calculate from timestamps - const duration = dbMetrics.duration || calculateDurationFromTimestamps(task) - metrics[task.id] = { ...dbMetrics, duration } - } else if (streamingUsage) { - // Fall back to streaming values if DB is empty/stale - // Use streaming duration, or calculate from timestamps if not available - const duration = streamingUsage.duration || calculateDurationFromTimestamps(task) - metrics[task.id] = { - tokensIn: streamingUsage.totalTokensIn, - tokensOut: streamingUsage.totalTokensOut, - tokensContext: streamingUsage.contextTokens, - duration, - cost: streamingUsage.totalCost, - } - } else { - // Task finished but no DB metrics and no streaming data - // (e.g., page loaded after task completed, metrics not persisted) - // Still provide duration calculated from timestamps - metrics[task.id] = { - tokensIn: 0, - tokensOut: 0, - tokensContext: 0, - duration: calculateDurationFromTimestamps(task), - cost: 0, - } - } - } else if (streamingUsage) { - // For running tasks, use streaming values - // Use streaming duration, or calculate from task.startedAt if not available - // (happens when page loads after TaskStarted event was already published) - const duration = streamingUsage.duration || calculateDurationFromTimestamps(task) - metrics[task.id] = { - tokensIn: streamingUsage.totalTokensIn, - tokensOut: streamingUsage.totalTokensOut, - tokensContext: streamingUsage.contextTokens, - duration, - cost: streamingUsage.totalCost, - } - } else if (task.startedAt) { - // Task has started (has startedAt in DB) but no streaming data yet - // This can happen when page loads after TaskStarted but before TokenUsageUpdated - metrics[task.id] = { - tokensIn: 0, - tokensOut: 0, - tokensContext: 0, - duration: calculateDurationFromTimestamps(task), - cost: 0, - } - } - }) - - return metrics - }, [tasks, tokenUsage, usageUpdatedAt]) - - const onViewTaskLog = useCallback( - async (task: Task) => { - // Only allow viewing logs for tasks that have started. - // Note: we treat presence of derived metrics as evidence of a started task, - // since this page may be rendered without streaming `tokenUsage` populated. - const hasStarted = !!task.startedAt || !!tokenUsage.get(task.id) || !!taskMetrics[task.id] - if (!hasStarted) { - toast.error("Task has not started yet") - return - } - - setSelectedTask(task) - setIsLoadingLog(true) - setTaskLog(null) - - try { - const response = await fetch(`/api/runs/${run.id}/logs/${task.id}`) - - if (!response.ok) { - const error = await response.json() - toast.error(error.error || "Failed to load log") - setSelectedTask(null) - return - } - - const data = await response.json() - setTaskLog(data.logContent) - } catch (error) { - console.error("Error loading task log:", error) - toast.error("Failed to load log") - setSelectedTask(null) - } finally { - setIsLoadingLog(false) - } - }, - [run.id, tokenUsage, taskMetrics], - ) - - // Collect all unique tool names from all tasks and sort by total attempts - const toolColumns = useMemo(() => { - // Reference usageUpdatedAt to trigger recomputation when Map contents change - void usageUpdatedAt - if (!tasks) return [] - - const toolTotals = new Map() - - for (const task of tasks) { - // Get both DB and streaming values - const dbToolUsage = task.taskMetrics?.toolUsage - const streamingToolUsage = toolUsage.get(task.id) - - // For finished tasks, prefer DB values but fall back to streaming values - // For running tasks, use streaming values - // This handles race conditions during timeout where DB might not have latest data - const taskToolUsage = task.finishedAt - ? dbToolUsage && Object.keys(dbToolUsage).length > 0 - ? dbToolUsage - : streamingToolUsage - : streamingToolUsage - - if (taskToolUsage) { - for (const [toolName, usage] of Object.entries(taskToolUsage)) { - const tool = toolName as ToolName - const current = toolTotals.get(tool) ?? 0 - toolTotals.set(tool, current + usage.attempts) - } - } - } - - // Sort by total attempts descending - return Array.from(toolTotals.entries()) - .sort((a, b) => b[1] - a[1]) - .map(([name]): ToolName => name) - // toolUsage ref is stable; usageUpdatedAt triggers recomputation when Map contents change - }, [tasks, toolUsage, usageUpdatedAt]) - - // Compute aggregate stats - const stats = useMemo(() => { - // Reference usageUpdatedAt to trigger recomputation when Map contents change - void usageUpdatedAt - if (!tasks) return null - - const passed = tasks.filter((t) => t.passed === true).length - const failed = tasks.filter((t) => t.passed === false).length - const completed = passed + failed - - let totalTokensIn = 0 - let totalTokensOut = 0 - let totalCost = 0 - let totalDuration = 0 - - // Aggregate tool usage from all tasks (both finished and running) - const toolUsageAggregate: ToolUsage = {} - - for (const task of tasks) { - const metrics = taskMetrics[task.id] - if (metrics) { - totalTokensIn += metrics.tokensIn - totalTokensOut += metrics.tokensOut - totalCost += metrics.cost - totalDuration += metrics.duration - } - - // Aggregate tool usage: prefer DB values for finished tasks, fall back to streaming values - // This handles race conditions during timeout where DB might not have latest data - const dbToolUsage = task.taskMetrics?.toolUsage - const streamingToolUsage = toolUsage.get(task.id) - const taskToolUsage = task.finishedAt - ? dbToolUsage && Object.keys(dbToolUsage).length > 0 - ? dbToolUsage - : streamingToolUsage - : streamingToolUsage - - if (taskToolUsage) { - for (const [key, usage] of Object.entries(taskToolUsage)) { - const tool = key as keyof ToolUsage - if (!toolUsageAggregate[tool]) { - toolUsageAggregate[tool] = { attempts: 0, failures: 0 } - } - toolUsageAggregate[tool].attempts += usage.attempts - toolUsageAggregate[tool].failures += usage.failures - } - } - } - - const remaining = tasks.length - completed - - return { - passed, - failed, - completed, - remaining, - passRate: completed > 0 ? ((passed / completed) * 100).toFixed(1) : null, - totalTokensIn, - totalTokensOut, - totalCost, - totalDuration, - toolUsage: toolUsageAggregate, - } - // Map refs are stable; usageUpdatedAt triggers recomputation when Map contents change - }, [tasks, taskMetrics, toolUsage, usageUpdatedAt]) - - // Calculate elapsed time (wall-clock time from run creation to completion or now) - const elapsedTime = useMemo(() => { - // Reference usageUpdatedAt to trigger recomputation for live elapsed time updates - void usageUpdatedAt - if (!tasks || tasks.length === 0) return null - - const startTime = new Date(run.createdAt).getTime() - - // If run is complete, find the latest finishedAt from tasks - if (run.taskMetricsId) { - const latestFinish = tasks.reduce((latest, task) => { - if (task.finishedAt) { - const finishTime = new Date(task.finishedAt).getTime() - return finishTime > latest ? finishTime : latest - } - return latest - }, startTime) - return latestFinish - startTime - } - - // If still running, use current time - return Date.now() - startTime - }, [tasks, run.createdAt, run.taskMetricsId, usageUpdatedAt]) - - // Task status categories - type TaskStatusCategory = "failed" | "in_progress" | "passed" | "not_started" - - const getTaskStatusCategory = useCallback( - (task: TaskWithMetrics): TaskStatusCategory => { - if (task.passed === false) return "failed" - if (task.passed === true) return "passed" - // Check streaming data, DB metrics, or startedAt timestamp - const hasStarted = !!task.startedAt || !!tokenUsage.get(task.id) || !!taskMetrics[task.id] - if (hasStarted) return "in_progress" - return "not_started" - }, - [tokenUsage, taskMetrics], - ) - - // Group tasks by status while preserving original index - const groupedTasks = useMemo(() => { - if (!tasks || !groupByStatus) return null - - const groups: Record> = { - failed: [], - in_progress: [], - passed: [], - not_started: [], - } - - tasks.forEach((task, index) => { - const status = getTaskStatusCategory(task) - groups[status].push({ task, originalIndex: index }) - }) - - return groups - }, [tasks, groupByStatus, getTaskStatusCategory]) - - const statusLabels = useMemo( - (): Record => ({ - failed: { label: "Failed", className: "text-red-500", count: groupedTasks?.failed.length ?? 0 }, - in_progress: { - label: "In Progress", - className: "text-yellow-500", - count: groupedTasks?.in_progress.length ?? 0, - }, - passed: { label: "Passed", className: "text-green-500", count: groupedTasks?.passed.length ?? 0 }, - not_started: { - label: "Not Started", - className: "text-muted-foreground", - count: groupedTasks?.not_started.length ?? 0, - }, - }), - [groupedTasks], - ) - - const statusOrder: TaskStatusCategory[] = ["failed", "in_progress", "passed", "not_started"] - - // Helper to render a task row - const renderTaskRow = (task: TaskWithMetrics, originalIndex: number) => { - const hasStarted = !!task.startedAt || !!tokenUsage.get(task.id) || !!taskMetrics[task.id] - return ( - hasStarted && onViewTaskLog(task)}> - - {originalIndex + 1} - - -
- -
- - {task.language}/{task.exercise} - {task.iteration > 1 && ( - (#{task.iteration}) - )} - - {hasStarted && ( - - - - - Click to view log - - )} -
-
-
- {taskMetrics[task.id] ? ( - <> - -
-
{formatTokens(taskMetrics[task.id]!.tokensIn)}
/ -
{formatTokens(taskMetrics[task.id]!.tokensOut)}
-
-
- - {formatTokens(taskMetrics[task.id]!.tokensContext)} - - {toolColumns.map((toolName) => { - const dbUsage = task.taskMetrics?.toolUsage?.[toolName] - const streamingUsage = toolUsage.get(task.id)?.[toolName] - const usage = task.finishedAt ? (dbUsage ?? streamingUsage) : streamingUsage - - const successRate = - usage && usage.attempts > 0 - ? ((usage.attempts - usage.failures) / usage.attempts) * 100 - : 100 - const rateColor = - successRate === 100 - ? "text-muted-foreground" - : successRate >= 80 - ? "text-yellow-500" - : "text-red-500" - return ( - - {usage ? ( -
- {usage.attempts} - {formatToolUsageSuccessRate(usage)} -
- ) : ( - - - )} -
- ) - })} - - {taskMetrics[task.id]!.duration ? formatDuration(taskMetrics[task.id]!.duration) : "-"} - - - {formatCurrency(taskMetrics[task.id]!.cost)} - - - ) : ( - - )} -
- ) - } - - return ( - <> -
- {!tasks ? ( - - ) : ( - <> - {/* View Toggle */} -
- - - - - - {groupByStatus ? "Show tasks in run order" : "Group tasks by status"} - - -
- - - {stats && ( - - - {/* Provider, Model title and status */} -
- {run.settings?.apiProvider && ( - - {run.settings.apiProvider} - - )} -
{run.model}
- - {run.description && ( - - - {run.description} - - )} - {isRunActive && ( - - - - - - Stop all containers for this run - - - )} -
- {/* Main Stats Row */} -
- {/* Pass Rate / Fail Rate / Remaining % */} -
-
- - {stats.completed > 0 - ? `${((stats.passed / stats.completed) * 100).toFixed(1)}%` - : "-"} - - / - - {stats.completed > 0 - ? `${((stats.failed / stats.completed) * 100).toFixed(1)}%` - : "-"} - - / - - {tasks.length > 0 - ? `${((stats.remaining / tasks.length) * 100).toFixed(1)}%` - : "-"} - -
-
- {stats.passed} - {" / "} - {stats.failed} - {" / "} - {stats.remaining} - {" of "} - {tasks.length} -
-
- - {/* Tokens */} -
-
- {formatTokens(stats.totalTokensIn)} - / - {formatTokens(stats.totalTokensOut)} -
-
Tokens In / Out
-
- - {/* Cost */} -
-
- {formatCurrency(stats.totalCost)} -
-
Cost
-
- - {/* Duration */} -
-
- {stats.totalDuration > 0 - ? formatDuration(stats.totalDuration) - : "-"} -
-
Duration
-
- - {/* Elapsed Time */} -
-
- {elapsedTime !== null ? formatDuration(elapsedTime) : "-"} -
-
Elapsed
-
- - {/* Estimated Time Remaining - only show if run is active and we have data */} - {!run.taskMetricsId && - elapsedTime !== null && - stats.completed > 0 && - stats.remaining > 0 && ( -
-
- ~ - {formatDuration( - (elapsedTime / stats.completed) * stats.remaining, - )} -
-
- Est. Remaining -
-
- )} -
- - {/* Tool Usage Row */} - {Object.keys(stats.toolUsage).length > 0 && ( -
- {Object.entries(stats.toolUsage) - .sort(([, a], [, b]) => b.attempts - a.attempts) - .map(([toolName, usage]) => { - const abbr = getToolAbbreviation(toolName) - const successRate = - usage.attempts > 0 - ? ((usage.attempts - usage.failures) / - usage.attempts) * - 100 - : 100 - const rateColor = - successRate === 100 - ? "text-green-500" - : successRate >= 80 - ? "text-yellow-500" - : "text-red-500" - return ( - - -
- - {abbr} - - - {usage.attempts} - - - {formatToolUsageSuccessRate(usage)} - -
-
- - {toolName} - -
- ) - })} -
- )} -
-
- )} - - # - Exercise - Tokens In / Out - Context - {toolColumns.map((toolName) => ( - - - {getToolAbbreviation(toolName)} - {toolName} - - - ))} - Duration - Cost - -
- - {groupByStatus && groupedTasks - ? // Grouped view - statusOrder.map((status) => { - const group = groupedTasks[status] - if (group.length === 0) return null - const { label, className } = statusLabels[status] - return ( - - - - - {label} ({group.length}) - - - - {group.map(({ task, originalIndex }) => - renderTaskRow(task, originalIndex), - )} - - ) - }) - : // Default order view - tasks.map((task, index) => renderTaskRow(task, index))} - -
- - )} -
- - {/* Task Log Dialog - Full Screen */} - setSelectedTask(null)}> - - -
- - - {selectedTask?.language}/{selectedTask?.exercise} - {selectedTask?.iteration && selectedTask.iteration > 1 && ( - (#{selectedTask.iteration}) - )} - - ( - {selectedTask?.passed === true - ? "Passed" - : selectedTask?.passed === false - ? "Failed" - : "Running"} - ) - - - {taskLog && ( - - )} -
-
-
- {isLoadingLog ? ( -
- -
- ) : taskLog ? ( - -
- {formatLogContent(taskLog)} -
-
- ) : ( -
- Log file not available (may have been cleared) -
- )} -
-
-
- - {/* Kill Run Confirmation Dialog */} - - - - Kill Run? - - This will stop the controller and all task runner containers for this run. Any running tasks - will be terminated immediately. This action cannot be undone. - - - - Cancel - - {isKilling ? ( - <> - - Killing... - - ) : ( - "Kill Run" - )} - - - - - - ) -} diff --git a/apps/web-evals/src/app/runs/[id]/task-status.tsx b/apps/web-evals/src/app/runs/[id]/task-status.tsx deleted file mode 100644 index bae785131a..0000000000 --- a/apps/web-evals/src/app/runs/[id]/task-status.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { CircleCheck, CircleDashed, CircleSlash, LoaderCircle } from "lucide-react" - -import type { Task } from "@roo-code/evals" - -type TaskStatusProps = { - task: Task - running: boolean -} - -export const TaskStatus = ({ task, running }: TaskStatusProps) => { - return task.passed === false ? ( - - ) : task.passed === true ? ( - - ) : running ? ( - - ) : ( - - ) -} diff --git a/apps/web-evals/src/app/runs/new/new-run.tsx b/apps/web-evals/src/app/runs/new/new-run.tsx deleted file mode 100644 index 8d44ef38e7..0000000000 --- a/apps/web-evals/src/app/runs/new/new-run.tsx +++ /dev/null @@ -1,1083 +0,0 @@ -"use client" - -import { useCallback, useEffect, useMemo, useRef, useState } from "react" -import { useRouter } from "next/navigation" -import { z } from "zod" -import { useQuery } from "@tanstack/react-query" -import { useForm, FormProvider } from "react-hook-form" -import { zodResolver } from "@hookform/resolvers/zod" -import { toast } from "sonner" -import { - X, - Rocket, - Check, - ChevronsUpDown, - SlidersHorizontal, - Info, - Plus, - Minus, - Terminal, - MonitorPlay, -} from "lucide-react" - -import { - type ProviderSettings, - type GlobalSettings, - globalSettingsSchema, - providerSettingsSchema, - getModelId, - EVALS_SETTINGS, -} from "@roo-code/types" - -import { createRun } from "@/actions/runs" -import { getExercises } from "@/actions/exercises" - -import { - type CreateRun, - type ExecutionMethod, - createRunSchema, - CONCURRENCY_MIN, - CONCURRENCY_MAX, - CONCURRENCY_DEFAULT, - TIMEOUT_MIN, - TIMEOUT_MAX, - TIMEOUT_DEFAULT, - ITERATIONS_MIN, - ITERATIONS_MAX, - ITERATIONS_DEFAULT, -} from "@/lib/schemas" -import { cn } from "@/lib/utils" - -import { loadRooLastModelSelection, saveRooLastModelSelection } from "@/lib/roo-last-model-selection" -import { normalizeCreateRunForSubmit } from "@/lib/normalize-create-run" - -import { useOpenRouterModels } from "@/hooks/use-open-router-models" -import { useRooCodeCloudModels } from "@/hooks/use-roo-code-cloud-models" - -import { - Button, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, - Input, - Textarea, - Tabs, - TabsList, - TabsTrigger, - MultiSelect, - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, - Popover, - PopoverContent, - PopoverTrigger, - Slider, - Label, - Tooltip, - TooltipContent, - TooltipTrigger, -} from "@/components/ui" - -import { SettingsDiff } from "./settings-diff" - -type ImportedSettings = { - apiConfigs: Record - globalSettings: GlobalSettings - currentApiConfigName: string -} - -type ModelSelection = { - id: string - model: string - popoverOpen: boolean -} - -type ConfigSelection = { - id: string - configName: string - popoverOpen: boolean -} - -export function NewRun() { - const router = useRouter() - const modelSelectionsByProviderRef = useRef>({}) - const modelValueByProviderRef = useRef>({}) - - const [provider, setModelSource] = useState<"roo" | "openrouter" | "other">("other") - const [executionMethod, setExecutionMethod] = useState("vscode") - const [commandExecutionTimeout, setCommandExecutionTimeout] = useState(20) - const [terminalShellIntegrationTimeout, setTerminalShellIntegrationTimeout] = useState(30) // seconds - - const [modelSelections, setModelSelections] = useState([ - { id: crypto.randomUUID(), model: "", popoverOpen: false }, - ]) - - const [importedSettings, setImportedSettings] = useState(null) - const [configSelections, setConfigSelections] = useState([ - { id: crypto.randomUUID(), configName: "", popoverOpen: false }, - ]) - - const openRouter = useOpenRouterModels() - const rooCodeCloud = useRooCodeCloudModels() - const models = provider === "openrouter" ? openRouter.data : rooCodeCloud.data - const searchValue = provider === "openrouter" ? openRouter.searchValue : rooCodeCloud.searchValue - const setSearchValue = provider === "openrouter" ? openRouter.setSearchValue : rooCodeCloud.setSearchValue - const onFilter = provider === "openrouter" ? openRouter.onFilter : rooCodeCloud.onFilter - - const exercises = useQuery({ queryKey: ["getExercises"], queryFn: () => getExercises() }) - - const [selectedExercises, setSelectedExercises] = useState([]) - - const form = useForm({ - resolver: zodResolver(createRunSchema), - defaultValues: { - model: "", - description: "", - suite: "full", - exercises: [], - settings: undefined, - concurrency: CONCURRENCY_DEFAULT, - timeout: TIMEOUT_DEFAULT, - iterations: ITERATIONS_DEFAULT, - jobToken: "", - executionMethod: "vscode", - }, - }) - - const { - register, - setValue, - clearErrors, - watch, - getValues, - formState: { isSubmitting }, - } = form - - const [suite, settings] = watch(["suite", "settings", "concurrency"]) - - const selectedModelIds = useMemo( - () => modelSelections.map((s) => s.model).filter((m) => m.length > 0), - [modelSelections], - ) - - const applyModelIds = useCallback( - (modelIds: string[]) => { - const unique = Array.from(new Set(modelIds.map((m) => m.trim()).filter((m) => m.length > 0))) - - if (unique.length === 0) { - setModelSelections([{ id: crypto.randomUUID(), model: "", popoverOpen: false }]) - setValue("model", "") - return - } - - setModelSelections(unique.map((model) => ({ id: crypto.randomUUID(), model, popoverOpen: false }))) - setValue("model", unique[0] ?? "") - }, - [setValue], - ) - - // Ensure the `exercises` field is registered so RHF always includes it in submit values. - useEffect(() => { - register("exercises") - }, [register]) - - // Load settings from localStorage on mount - useEffect(() => { - const savedConcurrency = localStorage.getItem("evals-concurrency") - - if (savedConcurrency) { - const parsed = parseInt(savedConcurrency, 10) - - if (!isNaN(parsed) && parsed >= CONCURRENCY_MIN && parsed <= CONCURRENCY_MAX) { - setValue("concurrency", parsed) - } - } - - const savedTimeout = localStorage.getItem("evals-timeout") - - if (savedTimeout) { - const parsed = parseInt(savedTimeout, 10) - - if (!isNaN(parsed) && parsed >= TIMEOUT_MIN && parsed <= TIMEOUT_MAX) { - setValue("timeout", parsed) - } - } - - const savedCommandTimeout = localStorage.getItem("evals-command-execution-timeout") - - if (savedCommandTimeout) { - const parsed = parseInt(savedCommandTimeout, 10) - - if (!isNaN(parsed) && parsed >= 20 && parsed <= 60) { - setCommandExecutionTimeout(parsed) - } - } - - const savedShellTimeout = localStorage.getItem("evals-shell-integration-timeout") - - if (savedShellTimeout) { - const parsed = parseInt(savedShellTimeout, 10) - - if (!isNaN(parsed) && parsed >= 30 && parsed <= 60) { - setTerminalShellIntegrationTimeout(parsed) - } - } - - const savedSuite = localStorage.getItem("evals-suite") - - if (savedSuite === "partial") { - setValue("suite", "partial") - const savedExercises = localStorage.getItem("evals-exercises") - if (savedExercises) { - try { - const parsed = JSON.parse(savedExercises) as string[] - if (Array.isArray(parsed)) { - setSelectedExercises(parsed) - setValue("exercises", parsed) - } - } catch { - // Invalid JSON, ignore. - } - } - } - }, [setValue]) - - // Track previous provider to detect switches - const [prevProvider, setPrevProvider] = useState(provider) - - // Preserve selections per provider; avoids cross-contamination while keeping UX stable. - useEffect(() => { - if (provider === prevProvider) return - - modelSelectionsByProviderRef.current[prevProvider] = modelSelections - modelValueByProviderRef.current[prevProvider] = getValues("model") - - const nextModelSelections = - modelSelectionsByProviderRef.current[provider] ?? - ([{ id: crypto.randomUUID(), model: "", popoverOpen: false }] satisfies ModelSelection[]) - - setModelSelections(nextModelSelections) - - const nextModelValue = - modelValueByProviderRef.current[provider] ?? - nextModelSelections.find((s) => s.model.trim().length > 0)?.model ?? - (provider === "other" && importedSettings && configSelections[0]?.configName - ? (getModelId(importedSettings.apiConfigs[configSelections[0].configName] ?? {}) ?? "") - : "") - - setValue("model", nextModelValue) - setPrevProvider(provider) - }, [provider, prevProvider, modelSelections, setValue, getValues, importedSettings, configSelections]) - - // When switching to Roo provider, restore last-used selection if current selection is empty - useEffect(() => { - if (provider !== "roo") return - if (selectedModelIds.length > 0) return - - const last = loadRooLastModelSelection() - if (last.length > 0) { - applyModelIds(last) - } - }, [applyModelIds, provider, selectedModelIds.length]) - - // Persist last-used Roo provider model selection - useEffect(() => { - if (provider !== "roo") return - saveRooLastModelSelection(selectedModelIds) - }, [provider, selectedModelIds]) - - // Extract unique languages from exercises - const languages = useMemo(() => { - if (!exercises.data) { - return [] - } - - const langs = new Set() - - for (const path of exercises.data) { - const lang = path.split("/")[0] - - if (lang) { - langs.add(lang) - } - } - - return Array.from(langs).sort() - }, [exercises.data]) - - const getExercisesForLanguage = useCallback( - (lang: string) => { - if (!exercises.data) { - return [] - } - - return exercises.data.filter((path) => path.startsWith(`${lang}/`)) - }, - [exercises.data], - ) - - const toggleLanguage = useCallback( - (lang: string) => { - const langExercises = getExercisesForLanguage(lang) - const allSelected = langExercises.every((ex) => selectedExercises.includes(ex)) - - let newSelected: string[] - - if (allSelected) { - newSelected = selectedExercises.filter((ex) => !ex.startsWith(`${lang}/`)) - } else { - const existing = new Set(selectedExercises) - - for (const ex of langExercises) { - existing.add(ex) - } - - newSelected = Array.from(existing) - } - - setSelectedExercises(newSelected) - setValue("exercises", newSelected) - localStorage.setItem("evals-exercises", JSON.stringify(newSelected)) - }, - [getExercisesForLanguage, selectedExercises, setValue], - ) - - const isLanguageSelected = useCallback( - (lang: string) => { - const langExercises = getExercisesForLanguage(lang) - return langExercises.length > 0 && langExercises.every((ex) => selectedExercises.includes(ex)) - }, - [getExercisesForLanguage, selectedExercises], - ) - - const isLanguagePartiallySelected = useCallback( - (lang: string) => { - const langExercises = getExercisesForLanguage(lang) - const selectedCount = langExercises.filter((ex) => selectedExercises.includes(ex)).length - return selectedCount > 0 && selectedCount < langExercises.length - }, - [getExercisesForLanguage, selectedExercises], - ) - - const addModelSelection = useCallback(() => { - setModelSelections((prev) => [...prev, { id: crypto.randomUUID(), model: "", popoverOpen: false }]) - }, []) - - const removeModelSelection = useCallback((id: string) => { - setModelSelections((prev) => prev.filter((s) => s.id !== id)) - }, []) - - const updateModelSelection = useCallback( - (id: string, model: string) => { - setModelSelections((prev) => prev.map((s) => (s.id === id ? { ...s, model, popoverOpen: false } : s))) - // Also set the form model field for validation (use first non-empty model). - setValue("model", model) - }, - [setValue], - ) - - const toggleModelPopover = useCallback((id: string, open: boolean) => { - setModelSelections((prev) => prev.map((s) => (s.id === id ? { ...s, popoverOpen: open } : s))) - }, []) - - const addConfigSelection = useCallback(() => { - setConfigSelections((prev) => [...prev, { id: crypto.randomUUID(), configName: "", popoverOpen: false }]) - }, []) - - const removeConfigSelection = useCallback((id: string) => { - setConfigSelections((prev) => prev.filter((s) => s.id !== id)) - }, []) - - const updateConfigSelection = useCallback( - (id: string, configName: string) => { - setConfigSelections((prev) => prev.map((s) => (s.id === id ? { ...s, configName, popoverOpen: false } : s))) - - // Also update the form settings for the first config (for validation). - if (importedSettings) { - const providerSettings = importedSettings.apiConfigs[configName] ?? {} - setValue("model", getModelId(providerSettings) ?? "") - setValue("settings", { ...EVALS_SETTINGS, ...providerSettings, ...importedSettings.globalSettings }) - } - }, - [importedSettings, setValue], - ) - - const toggleConfigPopover = useCallback((id: string, open: boolean) => { - setConfigSelections((prev) => prev.map((s) => (s.id === id ? { ...s, popoverOpen: open } : s))) - }, []) - - const onSubmit = useCallback( - async (values: CreateRun) => { - try { - const baseValues = normalizeCreateRunForSubmit(values, selectedExercises, suite) - - // Validate jobToken for Roo Code Cloud provider - if (provider === "roo" && !baseValues.jobToken?.trim()) { - toast.error("Roo Code Cloud Token is required") - return - } - - const selectionsToLaunch: { model: string; configName?: string }[] = [] - - if (provider === "other") { - for (const config of configSelections) { - if (config.configName) { - selectionsToLaunch.push({ model: "", configName: config.configName }) - } - } - } else { - for (const selection of modelSelections) { - if (selection.model) { - selectionsToLaunch.push({ model: selection.model }) - } - } - } - - if (selectionsToLaunch.length === 0) { - toast.error("Please select at least one model or config") - return - } - - const totalRuns = selectionsToLaunch.length - toast.info(totalRuns > 1 ? `Launching ${totalRuns} runs (every 20 seconds)...` : "Launching run...") - - for (let i = 0; i < selectionsToLaunch.length; i++) { - const selection = selectionsToLaunch[i]! - - // Wait 20 seconds between runs (except for the first one). - if (i > 0) { - await new Promise((resolve) => setTimeout(resolve, 20_000)) - } - - const runValues = { ...baseValues } - - if (provider === "openrouter") { - runValues.model = selection.model - runValues.settings = { - ...(runValues.settings || {}), - apiProvider: "openrouter", - openRouterModelId: selection.model, - commandExecutionTimeout, - terminalShellIntegrationTimeout: terminalShellIntegrationTimeout * 1000, - } - } else if (provider === "roo") { - runValues.model = selection.model - runValues.settings = { - ...(runValues.settings || {}), - apiProvider: "roo", - apiModelId: selection.model, - commandExecutionTimeout, - terminalShellIntegrationTimeout: terminalShellIntegrationTimeout * 1000, - } - } else if (provider === "other" && selection.configName && importedSettings) { - const providerSettings = importedSettings.apiConfigs[selection.configName] ?? {} - runValues.model = getModelId(providerSettings) ?? "" - runValues.settings = { - ...EVALS_SETTINGS, - ...providerSettings, - ...importedSettings.globalSettings, - commandExecutionTimeout, - terminalShellIntegrationTimeout: terminalShellIntegrationTimeout * 1000, - } - } - - try { - await createRun(runValues) - toast.success(`Run ${i + 1}/${totalRuns} launched`) - } catch (e) { - toast.error(`Run ${i + 1} failed: ${e instanceof Error ? e.message : "Unknown error"}`) - } - } - - router.push("/") - } catch (e) { - toast.error(e instanceof Error ? e.message : "An unknown error occurred.") - } - }, - [ - suite, - selectedExercises, - provider, - modelSelections, - configSelections, - importedSettings, - router, - commandExecutionTimeout, - terminalShellIntegrationTimeout, - ], - ) - - const onImportSettings = useCallback( - async (event: React.ChangeEvent) => { - const file = event.target.files?.[0] - - if (!file) { - return - } - - clearErrors("settings") - - try { - const { providerProfiles, globalSettings } = z - .object({ - providerProfiles: z.object({ - currentApiConfigName: z.string(), - apiConfigs: z.record(z.string(), providerSettingsSchema), - }), - globalSettings: globalSettingsSchema, - }) - .parse(JSON.parse(await file.text())) - - setImportedSettings({ - apiConfigs: providerProfiles.apiConfigs, - globalSettings, - currentApiConfigName: providerProfiles.currentApiConfigName, - }) - - const defaultConfigName = providerProfiles.currentApiConfigName - setConfigSelections([{ id: crypto.randomUUID(), configName: defaultConfigName, popoverOpen: false }]) - - const providerSettings = providerProfiles.apiConfigs[defaultConfigName] ?? {} - setValue("model", getModelId(providerSettings) ?? "") - setValue("settings", { ...EVALS_SETTINGS, ...providerSettings, ...globalSettings }) - - event.target.value = "" - } catch (e) { - console.error(e) - toast.error(e instanceof Error ? e.message : "An unknown error occurred.") - } - }, - [clearErrors, setValue], - ) - - return ( - <> - -
- ( - - setModelSource(value as "roo" | "openrouter" | "other")}> - - Import - Roo Code Cloud - OpenRouter - - - - {provider === "other" ? ( -
- - - - {importedSettings && Object.keys(importedSettings.apiConfigs).length > 0 && ( -
- - {configSelections.map((selection, index) => ( -
- - toggleConfigPopover(selection.id, open) - }> - - - - - - - - No config found. - - {Object.keys( - importedSettings.apiConfigs, - ).map((configName) => ( - - updateConfigSelection( - selection.id, - configName, - ) - }> - {configName} - {configName === - importedSettings.currentApiConfigName && ( - - (default) - - )} - - - ))} - - - - - - {index === configSelections.length - 1 ? ( - - ) : ( - - )} -
- ))} -
- )} - - {settings && ( - - )} -
- ) : ( - <> -
- {modelSelections.map((selection, index) => ( -
- toggleModelPopover(selection.id, open)}> - - - - - - - - No model found. - - {models?.map(({ id, name }) => ( - - updateModelSelection( - selection.id, - id, - ) - }> - {name} - - - ))} - - - - - - {index === modelSelections.length - 1 ? ( - - ) : ( - - )} -
- ))} -
- - )} - - -
- )} - /> - - {provider === "roo" && ( - ( - -
- Roo Code Cloud Token - - - - - -

- If you have access to the Roo Code Cloud repository and the - decryption key for the .env.* files, generate a token with: -

- - pnpm --filter @roo-code-cloud/auth production:create-auth-token - [email] [org] [ttl] - -
-
-
- - - - -
- )} - /> - )} - - ( - - Exercises -
- { - setValue("suite", value as "full" | "partial") - localStorage.setItem("evals-suite", value) - if (value === "full") { - setSelectedExercises([]) - setValue("exercises", []) - localStorage.removeItem("evals-exercises") - } - }}> - - All - Some - - - {suite === "partial" && languages.length > 0 && ( -
- {languages.map((lang) => ( - - ))} -
- )} -
- {suite === "partial" && ( - ({ value: path, label: path })) || []} - value={selectedExercises} - onValueChange={(value) => { - setSelectedExercises(value) - setValue("exercises", value) - localStorage.setItem("evals-exercises", JSON.stringify(value)) - }} - placeholder="Select" - variant="inverted" - maxCount={4} - /> - )} - -
- )} - /> - - {/* Concurrency, Timeout, and Iterations in a 3-column row */} -
- ( - - Concurrency - -
- { - field.onChange(value[0]) - localStorage.setItem("evals-concurrency", String(value[0])) - }} - /> -
{field.value}
-
-
- -
- )} - /> - - ( - - Timeout (Minutes) - -
- { - field.onChange(value[0]) - localStorage.setItem("evals-timeout", String(value[0])) - }} - /> -
{field.value}
-
-
- -
- )} - /> - - ( - - Iterations - -
- { - field.onChange(value[0]) - }} - /> -
{field.value}
-
-
- -
- )} - /> -
- - {/* Terminal timeouts in a 2-column row */} -
- -
- - - - - - -

- Maximum time in seconds to wait for terminal command execution to complete - before timing out. This applies to commands run via the execute_command - tool. -

-
-
-
-
- { - if (value !== undefined) { - setCommandExecutionTimeout(value) - localStorage.setItem("evals-command-execution-timeout", String(value)) - } - }} - /> -
{commandExecutionTimeout}
-
-
- - -
- - - - - - -

- Maximum time in seconds to wait for shell integration to initialize when - opening a new terminal. -

-
-
-
-
- { - if (value !== undefined) { - setTerminalShellIntegrationTimeout(value) - localStorage.setItem("evals-shell-integration-timeout", String(value)) - } - }} - /> -
{terminalShellIntegrationTimeout}
-
-
-
- - {/* Execution Method */} - ( - - Execution Method - { - const newExecutionMethod = value as ExecutionMethod - setExecutionMethod(newExecutionMethod) - setValue("executionMethod", newExecutionMethod) - }}> - - - - VSCode - - - - CLI - - - - - - )} - /> - - ( - - Description / Notes - -