Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3622982214 | |||
| d1a4ff87bc | |||
| 2e05247424 | |||
| fd5ad39fec |
@@ -1,223 +0,0 @@
|
||||
name: End-to-End Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
e2e-tests:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'end-to-end') || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
env:
|
||||
GITHUB_REPO_NAME: ${{ github.repository }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install poetry via pipx
|
||||
uses: abatilo/actions-poetry@v3
|
||||
with:
|
||||
poetry-version: 2.1.3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'poetry'
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-0 libnotify4 libnss3 libxss1 libxtst6 xauth xvfb libgbm1 libasound2t64 netcat-openbsd
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'frontend/package-lock.json'
|
||||
|
||||
- name: Setup environment for end-to-end tests
|
||||
run: |
|
||||
# Create test results directory
|
||||
mkdir -p test-results
|
||||
|
||||
# Create downloads directory for OpenHands (use a directory in the home folder)
|
||||
mkdir -p $HOME/downloads
|
||||
sudo chown -R $USER:$USER $HOME/downloads
|
||||
sudo chmod -R 755 $HOME/downloads
|
||||
|
||||
- name: Build OpenHands
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
LLM_MODEL: ${{ secrets.LLM_MODEL || 'gpt-4o' }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY || 'test-key' }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
INSTALL_DOCKER: 1
|
||||
RUNTIME: docker
|
||||
FRONTEND_PORT: 12000
|
||||
FRONTEND_HOST: 0.0.0.0
|
||||
BACKEND_HOST: 0.0.0.0
|
||||
BACKEND_PORT: 3000
|
||||
ENABLE_BROWSER: true
|
||||
INSTALL_PLAYWRIGHT: 1
|
||||
run: |
|
||||
# Fix poetry.lock file if needed
|
||||
echo "Fixing poetry.lock file if needed..."
|
||||
poetry lock
|
||||
|
||||
# Build OpenHands using make build
|
||||
echo "Running make build..."
|
||||
make build
|
||||
|
||||
# Install Chromium Headless Shell for Playwright (needed for pytest-playwright)
|
||||
echo "Installing Chromium Headless Shell for Playwright..."
|
||||
poetry run playwright install chromium-headless-shell
|
||||
|
||||
# Verify Playwright browsers are installed (for e2e tests only)
|
||||
echo "Verifying Playwright browsers installation for e2e tests..."
|
||||
BROWSER_CHECK=$(poetry run python tests/e2e/check_playwright.py 2>/dev/null)
|
||||
|
||||
if [ "$BROWSER_CHECK" != "chromium_found" ]; then
|
||||
echo "ERROR: Chromium browser not found or not working for e2e tests"
|
||||
echo "$BROWSER_CHECK"
|
||||
exit 1
|
||||
else
|
||||
echo "Playwright browsers are properly installed for e2e tests."
|
||||
fi
|
||||
|
||||
# Docker runtime will handle workspace directory creation
|
||||
|
||||
# Start the application using make run with custom parameters and reduced logging
|
||||
echo "Starting OpenHands using make run..."
|
||||
# Set environment variables to reduce logging verbosity
|
||||
export PYTHONUNBUFFERED=1
|
||||
export LOG_LEVEL=WARNING
|
||||
export UVICORN_LOG_LEVEL=warning
|
||||
export OPENHANDS_LOG_LEVEL=WARNING
|
||||
FRONTEND_PORT=12000 FRONTEND_HOST=0.0.0.0 BACKEND_HOST=0.0.0.0 make run > /tmp/openhands-e2e-test.log 2>&1 &
|
||||
|
||||
# Store the PID of the make run process
|
||||
MAKE_PID=$!
|
||||
echo "OpenHands started with PID: $MAKE_PID"
|
||||
|
||||
# Wait for the application to start
|
||||
echo "Waiting for OpenHands to start..."
|
||||
max_attempts=15
|
||||
attempt=1
|
||||
|
||||
while [ $attempt -le $max_attempts ]; do
|
||||
echo "Checking if OpenHands is running (attempt $attempt of $max_attempts)..."
|
||||
|
||||
# Check if the process is still running
|
||||
if ! ps -p $MAKE_PID > /dev/null; then
|
||||
echo "ERROR: OpenHands process has terminated unexpectedly"
|
||||
echo "Last 50 lines of the log:"
|
||||
tail -n 50 /tmp/openhands-e2e-test.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if frontend port is open
|
||||
if nc -z localhost 12000; then
|
||||
# Verify we can get HTML content
|
||||
if curl -s http://localhost:12000 | grep -q "<html"; then
|
||||
echo "SUCCESS: OpenHands is running and serving HTML content on port 12000"
|
||||
break
|
||||
else
|
||||
echo "Port 12000 is open but not serving HTML content yet"
|
||||
fi
|
||||
else
|
||||
echo "Frontend port 12000 is not open yet"
|
||||
fi
|
||||
|
||||
# Show log output on each attempt
|
||||
echo "Recent log output:"
|
||||
tail -n 20 /tmp/openhands-e2e-test.log
|
||||
|
||||
# Wait before next attempt
|
||||
echo "Waiting 10 seconds before next check..."
|
||||
sleep 10
|
||||
attempt=$((attempt + 1))
|
||||
|
||||
# Exit if we've reached the maximum number of attempts
|
||||
if [ $attempt -gt $max_attempts ]; then
|
||||
echo "ERROR: OpenHands failed to start after $max_attempts attempts"
|
||||
echo "Last 50 lines of the log:"
|
||||
tail -n 50 /tmp/openhands-e2e-test.log
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Final verification that the app is running
|
||||
if ! nc -z localhost 12000 || ! curl -s http://localhost:12000 | grep -q "<html"; then
|
||||
echo "ERROR: OpenHands is not running properly on port 12000"
|
||||
echo "Last 50 lines of the log:"
|
||||
tail -n 50 /tmp/openhands-e2e-test.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Print success message
|
||||
echo "OpenHands is running successfully on port 12000"
|
||||
|
||||
- name: Run end-to-end tests
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN }}
|
||||
LLM_MODEL: ${{ secrets.LLM_MODEL || 'gpt-4o' }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY || 'test-key' }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
run: |
|
||||
# Check if the application is running
|
||||
if ! nc -z localhost 12000; then
|
||||
echo "ERROR: OpenHands is not running on port 12000"
|
||||
echo "Last 50 lines of the log:"
|
||||
tail -n 50 /tmp/openhands-e2e-test.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run the tests with detailed output
|
||||
cd tests/e2e
|
||||
poetry run python -m pytest test_e2e_workflow.py::test_github_token_configuration test_e2e_workflow.py::test_conversation_start -v --no-header --capture=no --timeout=600
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: playwright-report
|
||||
path: tests/e2e/test-results/
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload OpenHands logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openhands-logs
|
||||
path: |
|
||||
/tmp/openhands-e2e-test.log
|
||||
/tmp/openhands-e2e-build.log
|
||||
/tmp/openhands-backend.log
|
||||
/tmp/openhands-frontend.log
|
||||
/tmp/backend-health-check.log
|
||||
/tmp/frontend-check.log
|
||||
/tmp/vite-config.log
|
||||
/tmp/makefile-contents.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
run: |
|
||||
# Stop OpenHands processes
|
||||
echo "Stopping OpenHands processes..."
|
||||
pkill -f "python -m openhands.server" || true
|
||||
pkill -f "npm run dev" || true
|
||||
pkill -f "make run" || true
|
||||
|
||||
# Print process status for debugging
|
||||
echo "Checking if any OpenHands processes are still running:"
|
||||
ps aux | grep -E "openhands|npm run dev" || true
|
||||
@@ -51,6 +51,8 @@ jobs:
|
||||
run: PYTHONPATH=".:$PYTHONPATH" poetry run pytest --forked -n auto -svv ./tests/unit
|
||||
- name: Run Runtime Tests with CLIRuntime
|
||||
run: PYTHONPATH=".:$PYTHONPATH" TEST_RUNTIME=cli poetry run pytest -svv tests/runtime/test_bash.py
|
||||
- name: Run E2E Tests
|
||||
run: PYTHONPATH=".:$PYTHONPATH" poetry run pytest -svv tests/e2e
|
||||
|
||||
# Run specific Windows python tests
|
||||
test-on-windows:
|
||||
|
||||
@@ -254,11 +254,3 @@ containers/runtime/Dockerfile
|
||||
containers/runtime/project.tar.gz
|
||||
containers/runtime/code
|
||||
**/node_modules/
|
||||
|
||||
# VSCode extension test files
|
||||
openhands/integrations/vscode/.vscode-test/
|
||||
openhands/integrations/vscode/out/
|
||||
openhands/integrations/vscode/node_modules/
|
||||
|
||||
# test results
|
||||
test-results
|
||||
|
||||
@@ -3,9 +3,4 @@
|
||||
"files.eol": "\n",
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.insertFinalNewline": true,
|
||||
"python.testing.pytestArgs": [
|
||||
"tests"
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ poetry run pytest ./tests/unit/test_*.py
|
||||
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker
|
||||
container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
|
||||
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.53-nikolaik`
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.52-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
|
||||
@@ -52,63 +52,37 @@ which comes with $20 in free credits for new users.
|
||||
|
||||
## 💻 Running OpenHands Locally
|
||||
|
||||
### Option 1: CLI Launcher (Recommended)
|
||||
OpenHands can also run on your local system using Docker.
|
||||
See the [Running OpenHands](https://docs.all-hands.dev/usage/installation) guide for
|
||||
system requirements and more information.
|
||||
|
||||
The easiest way to run OpenHands locally is using the CLI launcher with [uv](https://docs.astral.sh/uv/). This provides better isolation from your current project's virtual environment and is required for OpenHands' default MCP servers.
|
||||
> [!WARNING]
|
||||
> On a public network? See our [Hardened Docker Installation Guide](https://docs.all-hands.dev/usage/runtimes/docker#hardened-docker-installation)
|
||||
> to secure your deployment by restricting network binding and implementing additional security measures.
|
||||
|
||||
**Install uv** (if you haven't already):
|
||||
|
||||
See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) for the latest installation instructions for your platform.
|
||||
|
||||
**Launch OpenHands**:
|
||||
```bash
|
||||
# Launch the GUI server
|
||||
uvx --python 3.12 --from openhands-ai openhands serve
|
||||
|
||||
# Or launch the CLI
|
||||
uvx --python 3.12 --from openhands-ai openhands
|
||||
```
|
||||
|
||||
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000) (for GUI mode)!
|
||||
|
||||
### Option 2: Docker
|
||||
|
||||
<details>
|
||||
<summary>Click to expand Docker command</summary>
|
||||
|
||||
You can also run OpenHands directly with Docker:
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.53
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.52
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
> **Note**: If you used OpenHands before version 0.44, you may want to run `mv ~/.openhands-state ~/.openhands` to migrate your conversation history to the new location.
|
||||
|
||||
> [!WARNING]
|
||||
> On a public network? See our [Hardened Docker Installation Guide](https://docs.all-hands.dev/usage/runtimes/docker#hardened-docker-installation)
|
||||
> to secure your deployment by restricting network binding and implementing additional security measures.
|
||||
|
||||
### Getting Started
|
||||
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
|
||||
|
||||
When you open the application, you'll be asked to choose an LLM provider and add an API key.
|
||||
[Anthropic's Claude Sonnet 4](https://www.anthropic.com/api) (`anthropic/claude-sonnet-4-20250514`)
|
||||
works best, but you have [many options](https://docs.all-hands.dev/usage/llms).
|
||||
|
||||
See the [Running OpenHands](https://docs.all-hands.dev/usage/installation) guide for
|
||||
system requirements and more information.
|
||||
|
||||
## 💡 Other ways to run OpenHands
|
||||
|
||||
> [!WARNING]
|
||||
@@ -119,8 +93,8 @@ system requirements and more information.
|
||||
> [OpenHands Cloud Helm Chart](https://github.com/all-Hands-AI/OpenHands-cloud)
|
||||
|
||||
You can [connect OpenHands to your local filesystem](https://docs.all-hands.dev/usage/runtimes/docker#connecting-to-your-filesystem),
|
||||
interact with it via a [friendly CLI](https://docs.all-hands.dev/usage/how-to/cli-mode),
|
||||
run OpenHands in a scriptable [headless mode](https://docs.all-hands.dev/usage/how-to/headless-mode),
|
||||
interact with it via a [friendly CLI](https://docs.all-hands.dev/usage/how-to/cli-mode),
|
||||
or run it on tagged issues with [a github action](https://docs.all-hands.dev/usage/how-to/github-action).
|
||||
|
||||
Visit [Running OpenHands](https://docs.all-hands.dev/usage/installation) for more information and setup instructions.
|
||||
|
||||
@@ -51,17 +51,17 @@ OpenHands也可以使用Docker在本地系统上运行。
|
||||
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.53
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.52
|
||||
```
|
||||
|
||||
> **注意**: 如果您在0.44版本之前使用过OpenHands,您可能需要运行 `mv ~/.openhands-state ~/.openhands` 来将对话历史迁移到新位置。
|
||||
|
||||
@@ -42,17 +42,17 @@ OpenHandsはDockerを利用してローカル環境でも実行できます。
|
||||
> 公共ネットワークで実行していますか?[Hardened Docker Installation Guide](https://docs.all-hands.dev/usage/runtimes/docker#hardened-docker-installation)を参照して、ネットワークバインディングの制限や追加のセキュリティ対策を実施してください。
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.53
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.52
|
||||
```
|
||||
|
||||
**注**: バージョン0.44以前のOpenHandsを使用していた場合は、会話履歴を移行するために `mv ~/.openhands-state ~/.openhands` を実行してください。
|
||||
|
||||
@@ -93,7 +93,8 @@ def build_vscode_extension():
|
||||
|
||||
|
||||
def build(setup_kwargs):
|
||||
"""This function is called by Poetry during the build process.
|
||||
"""
|
||||
This function is called by Poetry during the build process.
|
||||
`setup_kwargs` is a dictionary that will be passed to `setuptools.setup()`.
|
||||
"""
|
||||
print('--- Running custom Poetry build script (build_vscode.py) ---')
|
||||
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
- DOCKER_HOST_ADDR=host.docker.internal
|
||||
#
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.53-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.52-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -7,7 +7,7 @@ services:
|
||||
image: openhands:latest
|
||||
container_name: openhands-app-${DATE:-}
|
||||
environment:
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik}
|
||||
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of ~/.openhands for this user
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 28 KiB |
@@ -78,14 +78,6 @@ description: Complete guide for setting up Jira Data Center integration with Ope
|
||||
- **Service Account API Key**: The personal access token from Step 2 above
|
||||
- Ensure **Active** toggle is enabled
|
||||
|
||||
<Note>
|
||||
Workspace name is the host name of your Jira Data Center instance.
|
||||
|
||||
Eg: http://jira.all-hands.dev/projects/OH/issues/OH-77
|
||||
|
||||
Here the workspace name is **jira.all-hands.dev**.
|
||||
</Note>
|
||||
|
||||
3. **Complete OAuth Flow**
|
||||
- You'll be redirected to Jira Data Center to complete OAuth verification
|
||||
- Grant the necessary permissions to verify your workspace access. If you have access to multiple workspaces, select the correct one that you initially provided
|
||||
@@ -109,18 +101,18 @@ Here the workspace name is **jira.all-hands.dev**.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Workspace link flow">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Workspace Configure flow">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Edit view as a user">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Edit view as the workspace creator">
|
||||

|
||||

|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -15,27 +15,28 @@ description: Complete guide for setting up Jira Cloud integration with OpenHands
|
||||
- Go to **Directory** > **Users**
|
||||
|
||||
2. **Create OpenHands Service Account**
|
||||
- Click **Service accounts**
|
||||
- Click **Create a service account**
|
||||
- Name: `OpenHands Agent`
|
||||
- Click **Next**
|
||||
- Select **User** role for Jira app
|
||||
- Click **Create**
|
||||
- Click **Add user**
|
||||
- Email: `openhands@yourcompany.com` (replace with your preferred service account email)
|
||||
- Display name: `OpenHands Agent`
|
||||
- Send invitation: **No** (you'll set password manually)
|
||||
- Click **Add user**
|
||||
|
||||
3. **Configure Account**
|
||||
- Locate the created user and click on it
|
||||
- Set a secure password
|
||||
- Add to relevant Jira projects with appropriate permissions
|
||||
|
||||
### Step 2: Generate API Token
|
||||
|
||||
1. **Access Service Account Configuration**
|
||||
- Locate the created service account from above step and click on it
|
||||
1. **Access API Token Management**
|
||||
- Log in as the OpenHands service account
|
||||
- Go to [API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens)
|
||||
|
||||
2. **Create API Token**
|
||||
- Click **Create API token**
|
||||
- Set the expiry to 365 days (maximum allowed value)
|
||||
- Click **Next**
|
||||
- In **Select token scopes** screen, filter by following values
|
||||
- App: Jira
|
||||
- Scope type: Classic
|
||||
- Scope actions: Write, Read
|
||||
- Select `read:jira-work` and `write:jira-work` scopes
|
||||
- Click **Next**
|
||||
- Review and create API token
|
||||
- Label: `OpenHands Cloud Integration`
|
||||
- Expiry: Set appropriate expiration (recommend 1 year)
|
||||
- Click **Create**
|
||||
- **Important**: Copy and securely store the token immediately
|
||||
|
||||
### Step 3: Configure Webhook
|
||||
@@ -82,14 +83,6 @@ description: Complete guide for setting up Jira Cloud integration with OpenHands
|
||||
- **Service Account API Key**: The API token from Step 2 above
|
||||
- Ensure **Active** toggle is enabled
|
||||
|
||||
<Note>
|
||||
Workspace name is the host name when accessing a resource in Jira Cloud.
|
||||
|
||||
Eg: https://all-hands.atlassian.net/browse/OH-55
|
||||
|
||||
Here the workspace name is **all-hands**.
|
||||
</Note>
|
||||
|
||||
3. **Complete OAuth Flow**
|
||||
- You'll be redirected to Jira Cloud to complete OAuth verification
|
||||
- Grant the necessary permissions to verify your workspace access.
|
||||
@@ -113,18 +106,18 @@ Here the workspace name is **all-hands**.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Workspace link flow">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Workspace Configure flow">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Edit view as a user">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Edit view as the workspace creator">
|
||||

|
||||

|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -28,7 +28,7 @@ description: Complete guide for setting up Linear integration with OpenHands Clo
|
||||
|
||||
1. **Access API Settings**
|
||||
- Log in as the service account
|
||||
- Go to **Settings** > **Security & access**
|
||||
- Go to **Settings** > **API**
|
||||
|
||||
2. **Create Personal API Key**
|
||||
- Click **Create new key**
|
||||
@@ -82,14 +82,6 @@ description: Complete guide for setting up Linear integration with OpenHands Clo
|
||||
- **Service Account API Key**: The API key from Step 2 above
|
||||
- Ensure **Active** toggle is enabled
|
||||
|
||||
<Note>
|
||||
Workspace name is the identifier after the host name when accessing a resource in Linear.
|
||||
|
||||
Eg: https://linear.app/allhands/issue/OH-37
|
||||
|
||||
Here the workspace name is **allhands**.
|
||||
</Note>
|
||||
|
||||
3. **Complete OAuth Flow**
|
||||
- You'll be redirected to Linear to complete OAuth verification
|
||||
- Grant the necessary permissions to verify your workspace access. If you have access to multiple workspaces, select the correct one that you initially provided
|
||||
@@ -113,15 +105,15 @@ Here the workspace name is **allhands**.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Workspace link flow">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Workspace Configure flow">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Edit view as a user">
|
||||

|
||||

|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Edit view as the workspace creator">
|
||||
|
||||
@@ -58,18 +58,17 @@ The OpenHands agent needs to identify which Git repository to work with when pro
|
||||
|
||||
### Platform Configuration Issues
|
||||
- **Webhook not triggering**: Verify the webhook URL is correct and the proper event types are selected (Comment, Issue updated)
|
||||
- **API authentication failing**: Check API key/token validity and ensure required scopes are granted. If your current API token is expired, make sure to update it in the respective integration settings
|
||||
- **API authentication failing**: Check API key/token validity and ensure required scopes are granted
|
||||
- **Permission errors**: Ensure the service account has access to relevant projects/teams and appropriate permissions
|
||||
|
||||
### Workspace Integration Issues
|
||||
- **Workspace linking requests credentials**: If there are no active workspace integrations for the workspace you specified, you need to configure it first. Contact your platform administrator that you want to integrate with (eg: Jira, Linear)
|
||||
- **OAuth flow fails**: Ensure you're signing in with the same Git provider account that contains the repositories you want OpenHands to work on
|
||||
- **Integration not found**: Verify the workspace name matches exactly and that platform configuration was completed first
|
||||
- **OAuth flow fails**: Make sure that you're authorizing with the correct account with proper workspace access
|
||||
|
||||
### General Issues
|
||||
- **Agent not responding**: Check webhook logs in your platform settings and verify service account status
|
||||
- **Authentication errors**: Verify Git provider permissions and OpenHands Cloud access
|
||||
- **Agent fails to identify git repo**: Ensure you're signing in with the same Git provider account that contains the repositories you want OpenHands to work on
|
||||
- **Partial functionality**: Ensure both platform configuration and workspace integration are properly completed
|
||||
|
||||
### Getting Help
|
||||
|
||||
@@ -20,42 +20,27 @@ for scripting.
|
||||
|
||||
### Running with Python
|
||||
|
||||
**Note** - OpenHands requires Python version 3.12 or higher (Python 3.14 is not currently supported) and `uv` for the default `fetch` MCP server (more details below).
|
||||
**Note** - OpenHands requires Python version 3.12 or higher (Python 3.14 is not currently supported) and `uvx` for the default `fetch` MCP server (more details below).
|
||||
|
||||
#### Recommended: Using uv
|
||||
1. Install OpenHands using pip:
|
||||
```bash
|
||||
pip install openhands-ai
|
||||
```
|
||||
|
||||
We recommend using [uv](https://docs.astral.sh/uv/) for the best OpenHands experience. uv provides better isolation from your current project's virtual environment and is required for OpenHands' default MCP servers.
|
||||
Or if you prefer not to manage your own Python environment, you can use `uvx`:
|
||||
|
||||
1. **Install uv** (if you haven't already):
|
||||
|
||||
See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) for the latest installation instructions for your platform.
|
||||
|
||||
2. **Launch OpenHands CLI**:
|
||||
```bash
|
||||
uvx --python 3.12 --from openhands-ai openhands
|
||||
```
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="Alternative: Traditional pip installation">
|
||||
|
||||
If you prefer to use pip:
|
||||
|
||||
```bash
|
||||
# Install OpenHands
|
||||
pip install openhands-ai
|
||||
```
|
||||
|
||||
Note that you'll still need `uv` installed for the default MCP servers to work properly.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Create shell aliases for easy access across environments">
|
||||
|
||||
Add the following to your shell configuration file (`.bashrc`, `.zshrc`, etc.):
|
||||
|
||||
```bash
|
||||
# Add OpenHands aliases (recommended)
|
||||
# Add OpenHands aliases
|
||||
alias openhands="uvx --python 3.12 --from openhands-ai openhands"
|
||||
alias oh="uvx --python 3.12 --from openhands-ai openhands"
|
||||
```
|
||||
@@ -87,10 +72,9 @@ source ~/.bashrc # or source ~/.zshrc
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
3. Launch an interactive OpenHands conversation from the command line:
|
||||
2. Launch an interactive OpenHands conversation from the command line:
|
||||
```bash
|
||||
# If using uvx (recommended)
|
||||
uvx --python 3.12 --from openhands-ai openhands
|
||||
openhands
|
||||
```
|
||||
|
||||
<Note>
|
||||
@@ -99,7 +83,7 @@ uvx --python 3.12 --from openhands-ai openhands
|
||||
poetry run openhands
|
||||
</Note>
|
||||
|
||||
4. Set your model, API key, and other preferences using the UI (or alternatively environment variables, below).
|
||||
3. Set your model, API key, and other preferences using the UI (or alternatively environment variables, below).
|
||||
|
||||
This command opens an interactive prompt where you can type tasks or commands and get responses from OpenHands.
|
||||
The first time you run the CLI, it will take you through configuring the required LLM
|
||||
@@ -119,7 +103,7 @@ The conversation history will be saved in `~/.openhands/sessions`.
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -128,7 +112,7 @@ docker run -it \
|
||||
-v ~/.openhands:/.openhands \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.53 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.52 \
|
||||
python -m openhands.cli.main --override-cli-mode true
|
||||
```
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export GITHUB_TOKEN="your-token" # Required for repository operations
|
||||
# Run OpenHands
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
@@ -73,7 +73,7 @@ docker run -it \
|
||||
-v ~/.openhands:/.openhands \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.53 \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.52 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi"
|
||||
```
|
||||
|
||||
|
||||
@@ -68,23 +68,23 @@ Download and install the LM Studio desktop app from [lmstudio.ai](https://lmstud
|
||||
1. Check [the installation guide](/usage/local-setup) and ensure all prerequisites are met before running OpenHands, then run:
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.53
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.52
|
||||
```
|
||||
|
||||
2. Wait until the server is running (see log below):
|
||||
```
|
||||
Digest: sha256:e72f9baecb458aedb9afc2cd5bc935118d1868719e55d50da73190d3a85c674f
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.53
|
||||
Status: Image is up to date for docker.all-hands.dev/all-hands-ai/openhands:0.52
|
||||
Starting OpenHands...
|
||||
Running OpenHands as root
|
||||
14:22:13 - openhands:INFO: server_config.py:50 - Using config class None
|
||||
|
||||
@@ -66,31 +66,9 @@ A system with a modern processor and a minimum of **4GB RAM** is recommended to
|
||||
|
||||
### Start the App
|
||||
|
||||
#### Option 1: Using the CLI Launcher with uv (Recommended)
|
||||
#### Option 1: Using the CLI Launcher (Recommended)
|
||||
|
||||
We recommend using [uv](https://docs.astral.sh/uv/) for the best OpenHands experience. uv provides better isolation from your current project's virtual environment and is required for OpenHands' default MCP servers (like the [fetch MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch)).
|
||||
|
||||
**Install uv** (if you haven't already):
|
||||
|
||||
See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) for the latest installation instructions for your platform.
|
||||
|
||||
**Launch OpenHands**:
|
||||
```bash
|
||||
# Launch the GUI server
|
||||
uvx --python 3.12 --from openhands-ai openhands serve
|
||||
|
||||
# Or with GPU support (requires nvidia-docker)
|
||||
uvx --python 3.12 --from openhands-ai openhands serve --gpu
|
||||
|
||||
# Or with current directory mounted
|
||||
uvx --python 3.12 --from openhands-ai openhands serve --mount-cwd
|
||||
```
|
||||
|
||||
This will automatically handle Docker requirements checking, image pulling, and launching the GUI server. The `--gpu` flag enables GPU support via nvidia-docker, and `--mount-cwd` mounts your current directory into the container.
|
||||
|
||||
<Accordion title="Alternative: Traditional pip installation">
|
||||
|
||||
If you prefer to use pip and have Python 3.12+ installed:
|
||||
If you have Python 3.12+ installed, you can use the CLI launcher for a simpler experience:
|
||||
|
||||
```bash
|
||||
# Install OpenHands
|
||||
@@ -98,32 +76,34 @@ pip install openhands-ai
|
||||
|
||||
# Launch the GUI server
|
||||
openhands serve
|
||||
|
||||
# Or with GPU support (requires nvidia-docker)
|
||||
openhands serve --gpu
|
||||
|
||||
# Or with current directory mounted
|
||||
openhands serve --mount-cwd
|
||||
```
|
||||
|
||||
Note that you'll still need `uv` installed for the default MCP servers to work properly.
|
||||
Or using `uvx --python 3.12 --from openhands-ai openhands serve` if you have [uv](https://docs.astral.sh/uv/) installed.
|
||||
|
||||
</Accordion>
|
||||
This will automatically handle Docker requirements checking, image pulling, and launching the GUI server. The `--gpu` flag enables GPU support via nvidia-docker, and `--mount-cwd` mounts your current directory into the container.
|
||||
|
||||
#### Option 2: Using Docker Directly
|
||||
|
||||
<Accordion title="Docker Command (Click to expand)">
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.53-nikolaik \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.52-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands:/.openhands \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.53
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.52
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
> **Note**: If you used OpenHands before version 0.44, you may want to run `mv ~/.openhands-state ~/.openhands` to migrate your conversation history to the new location.
|
||||
|
||||
You'll find OpenHands running at http://localhost:3000!
|
||||
|
||||
@@ -506,6 +506,7 @@ def commit0_setup(dataset: pd.DataFrame, repo_split: str) -> pd.DataFrame:
|
||||
Returns:
|
||||
Filtered dataset based on split type
|
||||
"""
|
||||
|
||||
filtered_dataset = pd.concat(
|
||||
[
|
||||
dataset[dataset['repo'].str.split('/').str[1] == repo]
|
||||
|
||||
@@ -89,7 +89,8 @@ def get_config(
|
||||
def get_dv_query_for_real(
|
||||
datasets, question, domain_knowledge=None, workflow_tags=None
|
||||
):
|
||||
"""Prepare a structured query for the agent to execute on the specified datasets.
|
||||
"""
|
||||
Prepare a structured query for the agent to execute on the specified datasets.
|
||||
|
||||
This function constructs a query by compiling metadata from the provided datasets, along with any relevant domain knowledge and workflow tags.
|
||||
|
||||
@@ -103,6 +104,7 @@ def get_dv_query_for_real(
|
||||
query_to_dv: Query to be run on the dataset
|
||||
dataset_meta: Metadata of the dataset
|
||||
"""
|
||||
|
||||
dataset_meta = ''
|
||||
for dataset_metadata in datasets:
|
||||
dataset_meta += 'Dataset name: ' + dataset_metadata['name']
|
||||
@@ -138,7 +140,8 @@ def get_dv_query_for_real(
|
||||
|
||||
|
||||
def initialize_runtime(runtime: Runtime, data_files: list[str]):
|
||||
"""Initialize the runtime for the agent.
|
||||
"""
|
||||
Initialize the runtime for the agent.
|
||||
|
||||
This function is called before the runtime is used to run the agent.
|
||||
"""
|
||||
@@ -228,7 +231,8 @@ def process_instance(
|
||||
metadata: EvalMetadata,
|
||||
reset_logger: bool = True,
|
||||
):
|
||||
"""Process and evaluate a single instance of the dataset.
|
||||
"""
|
||||
Process and evaluate a single instance of the dataset.
|
||||
|
||||
This function executes the OpenHands agent
|
||||
for a specific instance of the dataset. It retrieves
|
||||
@@ -243,6 +247,7 @@ def process_instance(
|
||||
Returns:
|
||||
output: EvalOutput object
|
||||
"""
|
||||
|
||||
config = get_config(metadata)
|
||||
|
||||
# Setup the logger properly, so you can run
|
||||
@@ -351,7 +356,8 @@ def list_csv_files(list_of_datasets):
|
||||
|
||||
|
||||
def create_dataset(repo_location: str, split: str = 'test'):
|
||||
"""Create a dataset from the discoverybench repository
|
||||
"""
|
||||
Create a dataset from the discoverybench repository
|
||||
by walking through the repository and extracting metadata
|
||||
from the metadata_{}.json files
|
||||
|
||||
@@ -362,6 +368,7 @@ def create_dataset(repo_location: str, split: str = 'test'):
|
||||
Returns:
|
||||
df: DataFrame containing the dataset instances
|
||||
"""
|
||||
|
||||
data_dict = {}
|
||||
|
||||
data_location = os.path.join(repo_location, 'discoverybench', 'real', split)
|
||||
|
||||
@@ -105,7 +105,8 @@ def process_instance(
|
||||
log_dir: str | None = None,
|
||||
runtime_failure_count: int = 0,
|
||||
) -> EvalOutput:
|
||||
"""Evaluate agent performance on a SWE-bench problem instance.
|
||||
"""
|
||||
Evaluate agent performance on a SWE-bench problem instance.
|
||||
|
||||
Note that this signature differs from the expected input to `run_evaluation`. Use
|
||||
`functools.partial` to provide optional arguments before passing to the evaluation harness.
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
"""Utilities for handling binary files and patch generation in SWE-bench evaluation."""
|
||||
"""
|
||||
Utilities for handling binary files and patch generation in SWE-bench evaluation.
|
||||
"""
|
||||
|
||||
|
||||
def remove_binary_diffs(patch_text):
|
||||
"""Remove binary file diffs from a git patch.
|
||||
"""
|
||||
Remove binary file diffs from a git patch.
|
||||
|
||||
Args:
|
||||
patch_text (str): The git patch text
|
||||
@@ -33,7 +36,8 @@ def remove_binary_diffs(patch_text):
|
||||
|
||||
|
||||
def remove_binary_files_from_git():
|
||||
"""Generate a bash command to remove binary files from git staging.
|
||||
"""
|
||||
Generate a bash command to remove binary files from git staging.
|
||||
|
||||
Returns:
|
||||
str: A bash command that removes binary files from git staging
|
||||
|
||||
@@ -111,7 +111,8 @@ def process_instance(
|
||||
runtime_failure_count: int = 0,
|
||||
conditional_imports: ConditionalImports | None = None,
|
||||
) -> EvalOutput:
|
||||
"""Evaluate agent performance on a SWE-bench problem instance.
|
||||
"""
|
||||
Evaluate agent performance on a SWE-bench problem instance.
|
||||
|
||||
Note that this signature differs from the expected input to `run_evaluation`. Use
|
||||
`functools.partial` to provide optional arguments before passing to the evaluation harness.
|
||||
|
||||
@@ -16,7 +16,8 @@ from openhands.core.logger import openhands_logger as logger
|
||||
|
||||
class LocEvaluator:
|
||||
def __init__(self, args):
|
||||
"""Localization evaluation.
|
||||
"""
|
||||
Localization evaluation.
|
||||
|
||||
Args:
|
||||
args: all main arguments
|
||||
@@ -75,7 +76,8 @@ class LocEvaluator:
|
||||
self.task_resolved = False
|
||||
|
||||
def _init_dir(self, directory_path):
|
||||
"""Check if a directory exists and create it if it doesn't.
|
||||
"""
|
||||
Check if a directory exists and create it if it doesn't.
|
||||
|
||||
Args:
|
||||
directory_path (str): Path to the directory to check/create
|
||||
@@ -205,7 +207,8 @@ class LocEvaluator:
|
||||
self._compute_avg_over_all()
|
||||
|
||||
def _write_to_json(self, data, file_name):
|
||||
"""Writes the current object data to a JSON file.
|
||||
"""
|
||||
Writes the current object data to a JSON file.
|
||||
|
||||
Returns:
|
||||
bool: True if writing was successful, False otherwise.
|
||||
@@ -222,7 +225,8 @@ class LocEvaluator:
|
||||
return False
|
||||
|
||||
def read_from_json(self, file_path):
|
||||
"""Reads data from a JSON file and loads it into the current object.
|
||||
"""
|
||||
Reads data from a JSON file and loads it into the current object.
|
||||
|
||||
Returns:
|
||||
dict: The loaded JSON data, or an empty dict if the file doesn't exist
|
||||
@@ -249,7 +253,8 @@ class LocEvaluator:
|
||||
return {}
|
||||
|
||||
def read_from_jsonl(self, file_path):
|
||||
"""Reads data from a JSON file and loads it into the current object.
|
||||
"""
|
||||
Reads data from a JSON file and loads it into the current object.
|
||||
|
||||
Returns:
|
||||
dict: The loaded JSON data, or an empty dict if the file doesn't exist
|
||||
@@ -289,7 +294,8 @@ class LocEvaluator:
|
||||
history_idx += 1
|
||||
|
||||
def _parse_string_to_dict(self, dict_string) -> dict:
|
||||
"""Convert a string representation of a dictionary to an actual dictionary.
|
||||
"""
|
||||
Convert a string representation of a dictionary to an actual dictionary.
|
||||
|
||||
Args:
|
||||
dict_string (str): String representation of a dictionary
|
||||
@@ -322,7 +328,8 @@ class LocEvaluator:
|
||||
return None
|
||||
|
||||
def _parse_value_from_args(self, argument_str: str, key: str) -> str:
|
||||
"""Parse a specific key's value from argument string.
|
||||
"""
|
||||
Parse a specific key's value from argument string.
|
||||
|
||||
Args:
|
||||
argument_str (str): The argument string containing key-value pairs
|
||||
@@ -400,7 +407,8 @@ class LocEvaluator:
|
||||
return ''
|
||||
|
||||
def _parse_path_from_args(self, argument_str: str) -> str:
|
||||
"""Parse path from argument string.
|
||||
"""
|
||||
Parse path from argument string.
|
||||
|
||||
Args:
|
||||
argument_str (str): The argument string containing path information
|
||||
@@ -411,7 +419,8 @@ class LocEvaluator:
|
||||
return self._parse_value_from_args(argument_str, 'path')
|
||||
|
||||
def _parse_func_names_from_str(self, code_patch) -> list:
|
||||
"""Parse function names from the new_str code patch.
|
||||
"""
|
||||
Parse function names from the new_str code patch.
|
||||
|
||||
Args:
|
||||
code_patch: Either a string (argument string) or already extracted new_str code
|
||||
@@ -792,7 +801,8 @@ class LocEvaluator:
|
||||
|
||||
|
||||
def swe_data_loader(args):
|
||||
"""Loading SWE-Bench data.
|
||||
"""
|
||||
Loading SWE-Bench data.
|
||||
|
||||
Args:
|
||||
args: Main arguments.
|
||||
@@ -824,7 +834,8 @@ def swe_data_loader(args):
|
||||
|
||||
|
||||
def infer_data_loader(args):
|
||||
"""Load instance IDs.
|
||||
"""
|
||||
Load instance IDs.
|
||||
|
||||
Args:
|
||||
args: Main arguments.
|
||||
@@ -857,7 +868,8 @@ def infer_data_loader(args):
|
||||
|
||||
|
||||
def infer_cost_calculator(args):
|
||||
"""Calculate total and average costs from metric JSON files with detailed output.
|
||||
"""
|
||||
Calculate total and average costs from metric JSON files with detailed output.
|
||||
|
||||
Args:
|
||||
args: Main arguments.
|
||||
|
||||
@@ -28,7 +28,8 @@ class LocalizationInfo:
|
||||
hunks_per_file: dict[str, int] # File -> number of hunks
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""Convert LocalizationInfo to a dictionary for JSON serialization.
|
||||
"""
|
||||
Convert LocalizationInfo to a dictionary for JSON serialization.
|
||||
|
||||
Returns:
|
||||
Dictionary representation of the localization information
|
||||
@@ -57,7 +58,8 @@ class LocalizationInfo:
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict[str, Any]) -> 'LocalizationInfo':
|
||||
"""Create LocalizationInfo from a dictionary (for loading from JSON).
|
||||
"""
|
||||
Create LocalizationInfo from a dictionary (for loading from JSON).
|
||||
|
||||
Args:
|
||||
data: Dictionary containing localization information
|
||||
@@ -89,7 +91,8 @@ class LocalizationInfo:
|
||||
|
||||
|
||||
class LocMeta:
|
||||
"""SWE-Bench dataset loader and ground-truth localization parser.
|
||||
"""
|
||||
SWE-Bench dataset loader and ground-truth localization parser.
|
||||
|
||||
This class handles loading SWE-Bench datasets and extracting ground-truth
|
||||
localization information from patches for code localization evaluation.
|
||||
@@ -101,7 +104,8 @@ class LocMeta:
|
||||
dataset_name: str = 'princeton-nlp/SWE-bench_Verified',
|
||||
split: str = 'test',
|
||||
):
|
||||
"""Initialize LocMeta with a SWE-Bench dataset.
|
||||
"""
|
||||
Initialize LocMeta with a SWE-Bench dataset.
|
||||
|
||||
Args:
|
||||
dataset_name: HuggingFace dataset name (e.g., "princeton-nlp/SWE-bench_Verified")
|
||||
@@ -120,7 +124,8 @@ class LocMeta:
|
||||
self._init_swe_dataset()
|
||||
|
||||
def _init_swe_dataset(self) -> None:
|
||||
"""Load and initialize the SWE-Bench dataset from HuggingFace.
|
||||
"""
|
||||
Load and initialize the SWE-Bench dataset from HuggingFace.
|
||||
Converts to pandas DataFrame for easy manipulation.
|
||||
"""
|
||||
try:
|
||||
@@ -145,7 +150,8 @@ class LocMeta:
|
||||
raise
|
||||
|
||||
def get_instance_by_id(self, instance_id: str) -> pd.Series:
|
||||
"""Retrieve a specific instance by its ID.
|
||||
"""
|
||||
Retrieve a specific instance by its ID.
|
||||
|
||||
Args:
|
||||
instance_id: The instance identifier
|
||||
@@ -163,7 +169,8 @@ class LocMeta:
|
||||
return self.df.iloc[idx]
|
||||
|
||||
def parse_instance_loc(self, instance: Union[pd.Series, str]) -> LocalizationInfo:
|
||||
"""Parse ground-truth localization information from a SWE-Bench instance.
|
||||
"""
|
||||
Parse ground-truth localization information from a SWE-Bench instance.
|
||||
|
||||
Args:
|
||||
instance: Either a pandas Series with instance data or an instance_id string
|
||||
@@ -211,7 +218,8 @@ class LocMeta:
|
||||
def _parse_file_patch_lines(
|
||||
self, file_patch: str
|
||||
) -> tuple[list[tuple[int, int]], int, int]:
|
||||
"""Parse line ranges and count changes from a single file patch.
|
||||
"""
|
||||
Parse line ranges and count changes from a single file patch.
|
||||
|
||||
Args:
|
||||
file_patch: Patch content for a single file
|
||||
@@ -245,7 +253,8 @@ class LocMeta:
|
||||
def _parse_code_structures_from_patch(
|
||||
self, file_patch: str, file_path: str
|
||||
) -> tuple[list[str], list[str]]:
|
||||
"""Extract function and class names from patch context (fallback method).
|
||||
"""
|
||||
Extract function and class names from patch context (fallback method).
|
||||
|
||||
Args:
|
||||
file_patch: Patch content for a single file
|
||||
@@ -302,7 +311,8 @@ class LocMeta:
|
||||
def _parse_patch_localization(
|
||||
self, patch_content: str, instance_id: str
|
||||
) -> LocalizationInfo:
|
||||
"""Parse localization information from a git patch (improved method).
|
||||
"""
|
||||
Parse localization information from a git patch (improved method).
|
||||
|
||||
Args:
|
||||
patch_content: The git patch content
|
||||
@@ -380,7 +390,8 @@ class LocMeta:
|
||||
def _extract_code_structures_from_patch(
|
||||
self, file_patch: str, file_path: str
|
||||
) -> tuple[list[str], list[str]]:
|
||||
"""Extract function and class names from patch context and content.
|
||||
"""
|
||||
Extract function and class names from patch context and content.
|
||||
|
||||
Args:
|
||||
file_patch: Patch content for a single file
|
||||
@@ -508,7 +519,8 @@ class LocMeta:
|
||||
def _parse_patch_localization_with_runtime(
|
||||
self, patch_content: str, instance_id: str, runtime: Runtime
|
||||
) -> LocalizationInfo:
|
||||
"""Parse localization information from a git patch using OpenHands runtime.
|
||||
"""
|
||||
Parse localization information from a git patch using OpenHands runtime.
|
||||
This is the superior method when runtime is available.
|
||||
|
||||
Args:
|
||||
@@ -584,7 +596,8 @@ class LocMeta:
|
||||
def parse_instance_loc_with_runtime(
|
||||
self, instance: Union[pd.Series, str], runtime: Runtime = None
|
||||
) -> LocalizationInfo:
|
||||
"""Parse ground-truth localization information using OpenHands runtime.
|
||||
"""
|
||||
Parse ground-truth localization information using OpenHands runtime.
|
||||
|
||||
Args:
|
||||
instance: Either a pandas Series with instance data or an instance_id string
|
||||
@@ -621,7 +634,8 @@ class LocMeta:
|
||||
def _analyze_source_code_with_runtime(
|
||||
self, runtime: Runtime, file_path: str, affected_lines: list[int]
|
||||
) -> tuple[list[str], list[str], dict[int, str], dict[int, str]]:
|
||||
"""Analyze source code using OpenHands runtime to find functions and classes.
|
||||
"""
|
||||
Analyze source code using OpenHands runtime to find functions and classes.
|
||||
|
||||
Args:
|
||||
runtime: OpenHands runtime object
|
||||
@@ -681,7 +695,8 @@ class LocMeta:
|
||||
def _parse_cython_content_with_line_mapping(
|
||||
self, content: str, affected_lines: list[int]
|
||||
) -> tuple[list[str], list[str], dict[int, str], dict[int, str]]:
|
||||
"""Parse Cython content to extract functions and classes with line mapping.
|
||||
"""
|
||||
Parse Cython content to extract functions and classes with line mapping.
|
||||
Since Cython files can't be parsed with Python's AST, we use regex-based parsing.
|
||||
|
||||
Args:
|
||||
@@ -813,7 +828,8 @@ class LocMeta:
|
||||
def _parse_python_content_with_line_mapping(
|
||||
self, content: str, affected_lines: list[int]
|
||||
) -> tuple[list[str], list[str], dict[int, str], dict[int, str]]:
|
||||
"""Parse Python content to extract functions and classes with accurate line mapping.
|
||||
"""
|
||||
Parse Python content to extract functions and classes with accurate line mapping.
|
||||
|
||||
Args:
|
||||
content: Python source code content
|
||||
@@ -898,7 +914,8 @@ class LocMeta:
|
||||
def _parse_python_content(
|
||||
self, content: str, affected_lines: list[int]
|
||||
) -> tuple[list[str], list[str], dict[int, str], dict[int, str]]:
|
||||
"""Parse Python content to extract functions and classes.
|
||||
"""
|
||||
Parse Python content to extract functions and classes.
|
||||
|
||||
Args:
|
||||
content: Python source code content
|
||||
@@ -972,7 +989,8 @@ class LocMeta:
|
||||
return [], [], {}, {}
|
||||
|
||||
def _split_patch_by_files(self, patch_content: str) -> dict[str, str]:
|
||||
"""Split a multi-file patch into individual file patches.
|
||||
"""
|
||||
Split a multi-file patch into individual file patches.
|
||||
|
||||
Args:
|
||||
patch_content: Complete patch content
|
||||
@@ -1031,7 +1049,8 @@ class LocMeta:
|
||||
def _empty_localization_info(
|
||||
self, instance_id: str = 'unknown'
|
||||
) -> LocalizationInfo:
|
||||
"""Return an empty LocalizationInfo object.
|
||||
"""
|
||||
Return an empty LocalizationInfo object.
|
||||
|
||||
Args:
|
||||
instance_id: Instance identifier
|
||||
@@ -1053,7 +1072,8 @@ class LocMeta:
|
||||
)
|
||||
|
||||
def get_dataset_statistics(self) -> dict[str, Any]:
|
||||
"""Get statistics about the loaded dataset.
|
||||
"""
|
||||
Get statistics about the loaded dataset.
|
||||
|
||||
Returns:
|
||||
Dictionary containing dataset statistics
|
||||
@@ -1075,7 +1095,8 @@ class LocMeta:
|
||||
return stats
|
||||
|
||||
def get_instances_by_repo(self, repo_name: str) -> pd.DataFrame:
|
||||
"""Get all instances for a specific repository.
|
||||
"""
|
||||
Get all instances for a specific repository.
|
||||
|
||||
Args:
|
||||
repo_name: Repository name (e.g., "django/django")
|
||||
|
||||
@@ -6,7 +6,8 @@ from openhands.core.logger import openhands_logger as logger
|
||||
|
||||
|
||||
def verify_instance_costs(row: pd.Series) -> float:
|
||||
"""Verifies that the accumulated_cost matches the sum of individual costs in metrics.
|
||||
"""
|
||||
Verifies that the accumulated_cost matches the sum of individual costs in metrics.
|
||||
Also checks for duplicate consecutive costs which might indicate buggy counting.
|
||||
If the consecutive costs are identical, the file is affected by this bug:
|
||||
https://github.com/All-Hands-AI/OpenHands/issues/5383
|
||||
|
||||
@@ -181,7 +181,9 @@ def distinct_methods_stats(tree, num_lines):
|
||||
|
||||
|
||||
def loops_stats(tree, num_lines):
|
||||
"""Calculate the average number of loops."""
|
||||
"""
|
||||
Calculate the average number of loops.
|
||||
"""
|
||||
total_loops = 0
|
||||
|
||||
def traverse(node):
|
||||
@@ -197,7 +199,9 @@ def loops_stats(tree, num_lines):
|
||||
|
||||
|
||||
def branches_stats(tree, num_lines):
|
||||
"""Calculate the average number of branches (conditional statements)."""
|
||||
"""
|
||||
Calculate the average number of branches (conditional statements).
|
||||
"""
|
||||
total_branches = 0
|
||||
|
||||
def traverse(node):
|
||||
|
||||
@@ -192,7 +192,8 @@ def run_mutation_testing(
|
||||
def grade_test_output(
|
||||
test_suite: str, instance: pd.Series, test_output: str, test_spec: TestSpec, runtime
|
||||
):
|
||||
"""Two-pass test grading with short-circuiting:
|
||||
"""
|
||||
Two-pass test grading with short-circuiting:
|
||||
1. Run all tests to identify passing/failing tests
|
||||
2. If no failing tests, evaluate coverage immediately
|
||||
3. Otherwise, run only passing tests for coverage analysis
|
||||
@@ -279,7 +280,8 @@ def process_instance(
|
||||
reset_logger: bool = True,
|
||||
log_dir: str | None = None,
|
||||
) -> EvalOutput:
|
||||
"""Evaluate agent performance on a TestGenEval problem instance.
|
||||
"""
|
||||
Evaluate agent performance on a TestGenEval problem instance.
|
||||
|
||||
Note that this signature differs from the expected input to `run_evaluation`. Use
|
||||
`functools.partial` to provide optional arguments before passing to the evaluation harness.
|
||||
@@ -451,7 +453,8 @@ def process_instance(
|
||||
|
||||
|
||||
def count_and_log_fields(evaluated_predictions, fields, key):
|
||||
"""Count and log the sum of specified fields in the evaluated predictions,
|
||||
"""
|
||||
Count and log the sum of specified fields in the evaluated predictions,
|
||||
ignoring fields with a value of -1. If all values for a field are -1,
|
||||
return -1.
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ from evaluation.benchmarks.testgeneval.constants import TestStatus
|
||||
|
||||
|
||||
def parse_log_pytest(log: str) -> dict[str, str]:
|
||||
"""Parser for test logs generated with PyTest framework
|
||||
"""
|
||||
Parser for test logs generated with PyTest framework
|
||||
|
||||
Args:
|
||||
log (str): log content
|
||||
@@ -25,7 +26,8 @@ def parse_log_pytest(log: str) -> dict[str, str]:
|
||||
|
||||
|
||||
def parse_log_pytest_options(log: str) -> dict[str, str]:
|
||||
"""Parser for test logs generated with PyTest framework with options
|
||||
"""
|
||||
Parser for test logs generated with PyTest framework with options
|
||||
|
||||
Args:
|
||||
log (str): log content
|
||||
@@ -59,7 +61,8 @@ def parse_log_pytest_options(log: str) -> dict[str, str]:
|
||||
|
||||
|
||||
def parse_log_django(log: str) -> dict[str, str]:
|
||||
"""Parser for test logs generated with Django tester framework
|
||||
"""
|
||||
Parser for test logs generated with Django tester framework
|
||||
|
||||
Args:
|
||||
log (str): log content
|
||||
@@ -138,7 +141,8 @@ def parse_log_django(log: str) -> dict[str, str]:
|
||||
|
||||
|
||||
def parse_log_pytest_v2(log: str) -> dict[str, str]:
|
||||
"""Parser for test logs generated with PyTest framework (Later Version)
|
||||
"""
|
||||
Parser for test logs generated with PyTest framework (Later Version)
|
||||
|
||||
Args:
|
||||
log (str): log content
|
||||
@@ -166,7 +170,8 @@ def parse_log_pytest_v2(log: str) -> dict[str, str]:
|
||||
|
||||
|
||||
def parse_log_seaborn(log: str) -> dict[str, str]:
|
||||
"""Parser for test logs generated with seaborn testing framework
|
||||
"""
|
||||
Parser for test logs generated with seaborn testing framework
|
||||
|
||||
Args:
|
||||
log (str): log content
|
||||
@@ -191,7 +196,8 @@ def parse_log_seaborn(log: str) -> dict[str, str]:
|
||||
|
||||
|
||||
def parse_log_sympy(log: str) -> dict[str, str]:
|
||||
"""Parser for test logs generated with Sympy framework
|
||||
"""
|
||||
Parser for test logs generated with Sympy framework
|
||||
|
||||
Args:
|
||||
log (str): log content
|
||||
@@ -223,7 +229,8 @@ def parse_log_sympy(log: str) -> dict[str, str]:
|
||||
|
||||
|
||||
def parse_log_matplotlib(log: str) -> dict[str, str]:
|
||||
"""Parser for test logs generated with PyTest framework
|
||||
"""
|
||||
Parser for test logs generated with PyTest framework
|
||||
|
||||
Args:
|
||||
log (str): log content
|
||||
|
||||
@@ -12,7 +12,8 @@ if sys.getrecursionlimit() < 10_000:
|
||||
|
||||
|
||||
def bleu(gold: list[str], pred: list[str]) -> float:
|
||||
"""Calculate BLEU score, using smoothing method 2 with auto reweighting, in the range of 0~100.
|
||||
"""
|
||||
Calculate BLEU score, using smoothing method 2 with auto reweighting, in the range of 0~100.
|
||||
|
||||
:param gold: list of gold tokens
|
||||
:param pred: list of predicted tokens
|
||||
@@ -29,7 +30,8 @@ def bleu(gold: list[str], pred: list[str]) -> float:
|
||||
|
||||
|
||||
def batch_bleu(golds: list[list[str]], preds: list[list[str]]) -> list[float]:
|
||||
"""Calculate BLEU score for a batch of sentences.
|
||||
"""
|
||||
Calculate BLEU score for a batch of sentences.
|
||||
|
||||
:param golds: list of gold sentences
|
||||
:param preds: list of predicted sentences
|
||||
@@ -41,7 +43,8 @@ def batch_bleu(golds: list[list[str]], preds: list[list[str]]) -> list[float]:
|
||||
|
||||
|
||||
def corpus_bleu(golds: list[list[str]], preds: list[list[str]]) -> float:
|
||||
"""Calculate corpus-level BLEU score for a batch of sentences.
|
||||
"""
|
||||
Calculate corpus-level BLEU score for a batch of sentences.
|
||||
|
||||
:param golds: list of gold sentences
|
||||
:param preds: list of predicted sentences
|
||||
@@ -60,7 +63,8 @@ def corpus_bleu(golds: list[list[str]], preds: list[list[str]]) -> float:
|
||||
def edit_sim(
|
||||
gold: Union[str, list[str]], pred: Union[str, list[str]], sep: str = ' '
|
||||
) -> float:
|
||||
"""Calculate char-level edit similarity, in the range of 0~100.
|
||||
"""
|
||||
Calculate char-level edit similarity, in the range of 0~100.
|
||||
|
||||
:param gold: gold sentence or list of gold tokens
|
||||
:param pred: predicted sentence or list of predicted tokens
|
||||
@@ -81,7 +85,8 @@ def batch_edit_sim(
|
||||
preds: list[Union[str, list[str]]],
|
||||
sep: str = ' ',
|
||||
) -> list[float]:
|
||||
"""Calculate char-level edit similarity for a batch of sentences.
|
||||
"""
|
||||
Calculate char-level edit similarity for a batch of sentences.
|
||||
|
||||
:param golds: list of gold sentences
|
||||
:param preds: list of predicted sentences
|
||||
@@ -97,7 +102,8 @@ T = TypeVar('T')
|
||||
|
||||
|
||||
def exact_match(gold: T, pred: T) -> float:
|
||||
"""Calculate exact match accuracy, in the range of {0, 100}.
|
||||
"""
|
||||
Calculate exact match accuracy, in the range of {0, 100}.
|
||||
|
||||
:param gold: gold sentence or list of gold tokens
|
||||
:param pred: predicted sentence or list of predicted tokens
|
||||
@@ -109,7 +115,8 @@ def exact_match(gold: T, pred: T) -> float:
|
||||
|
||||
|
||||
def batch_exact_match(golds: list[T], preds: list[T]) -> list[float]:
|
||||
"""Calculate exact match accuracy for a batch of sentences.
|
||||
"""
|
||||
Calculate exact match accuracy for a batch of sentences.
|
||||
|
||||
:param golds: list of gold sentences
|
||||
:param preds: list of predicted sentences
|
||||
@@ -123,7 +130,8 @@ def batch_exact_match(golds: list[T], preds: list[T]) -> list[float]:
|
||||
def rouge_l(
|
||||
gold: Union[str, list[str]], pred: Union[str, list[str]], sep: str = ' '
|
||||
) -> dict[str, float]:
|
||||
"""Calculate ROUGE-L F1, precision, and recall scores, in the range of 0~100.
|
||||
"""
|
||||
Calculate ROUGE-L F1, precision, and recall scores, in the range of 0~100.
|
||||
|
||||
:param gold: gold sentence or list of gold tokens
|
||||
:param pred: predicted sentence or list of predicted tokens
|
||||
@@ -148,7 +156,8 @@ def batch_rouge_l(
|
||||
preds: list[Union[str, list[str]]],
|
||||
sep: str = ' ',
|
||||
) -> dict[str, list[float]]:
|
||||
"""Calculate ROUGE-L F1, precision, and recall scores for a batch of sentences.
|
||||
"""
|
||||
Calculate ROUGE-L F1, precision, and recall scores for a batch of sentences.
|
||||
|
||||
:param golds: list of gold sentences
|
||||
:param preds: list of predicted sentences
|
||||
@@ -166,7 +175,8 @@ def accuracy(
|
||||
pred: list[str],
|
||||
ignore: Optional[Sequence[str]] = None,
|
||||
) -> float:
|
||||
"""Calculate token-level accuracy, in the range of 0~100.
|
||||
"""
|
||||
Calculate token-level accuracy, in the range of 0~100.
|
||||
If gold and pred are not the same length, the longer one would be truncated.
|
||||
|
||||
:param gold: list of gold tokens
|
||||
@@ -200,7 +210,8 @@ def batch_accuracy(
|
||||
preds: list[list[str]],
|
||||
ignore: Optional[Sequence[str]] = None,
|
||||
) -> list[float]:
|
||||
"""Calculate token-level accuracy for a batch of sentences.
|
||||
"""
|
||||
Calculate token-level accuracy for a batch of sentences.
|
||||
|
||||
:param golds: list of gold sentences
|
||||
:param preds: list of predicted sentences
|
||||
@@ -215,7 +226,8 @@ def batch_accuracy(
|
||||
def first_match_to_topk(
|
||||
first_match_list: list[int], k_values: list[int]
|
||||
) -> dict[int, list[float]]:
|
||||
"""Calculate top-k accuracy with the first match ranks (1-indexed).
|
||||
"""
|
||||
Calculate top-k accuracy with the first match ranks (1-indexed).
|
||||
|
||||
:param first_match: first match ranks (1-indexed)
|
||||
:param k_values: k values to consider
|
||||
@@ -225,7 +237,8 @@ def first_match_to_topk(
|
||||
|
||||
|
||||
def pass_at_k(n: int, c: int, k: int) -> float:
|
||||
"""Sample pass@k metric according to the Codex paper, but in the scale of 0~100.
|
||||
"""
|
||||
Sample pass@k metric according to the Codex paper, but in the scale of 0~100.
|
||||
:param n: total number of samples
|
||||
:param c: number of correct samples
|
||||
:param k: k in pass@$k$
|
||||
@@ -238,7 +251,8 @@ def pass_at_k(n: int, c: int, k: int) -> float:
|
||||
|
||||
|
||||
def self_bleu(samples: list[list[str]]) -> float:
|
||||
"""Calculate self-BLEU among the samples.
|
||||
"""
|
||||
Calculate self-BLEU among the samples.
|
||||
:param samples: the chosen m samples
|
||||
:return: self-BLEU
|
||||
"""
|
||||
@@ -260,7 +274,8 @@ def self_bleu(samples: list[list[str]]) -> float:
|
||||
|
||||
|
||||
def self_edit_distance(samples: list[Union[str, list[str]]], sep=' ') -> float:
|
||||
"""Calculate self-edit-distance among the samples.
|
||||
"""
|
||||
Calculate self-edit-distance among the samples.
|
||||
:param samples: the chosen m samples
|
||||
:param sep: the separator between tokens
|
||||
:return: self-edit-distance
|
||||
|
||||
@@ -30,7 +30,8 @@ def check_mutation(mutation_output):
|
||||
|
||||
|
||||
def count_methods(code_str):
|
||||
"""Counts the number of methods/functions in a given string of code.
|
||||
"""
|
||||
Counts the number of methods/functions in a given string of code.
|
||||
|
||||
Args:
|
||||
code_str (str): A string containing code.
|
||||
@@ -45,7 +46,8 @@ def count_methods(code_str):
|
||||
|
||||
|
||||
def get_lines_of_code(code_str):
|
||||
"""Extracts lines of code from a given string.
|
||||
"""
|
||||
Extracts lines of code from a given string.
|
||||
|
||||
Args:
|
||||
code_str (str): A string containing code.
|
||||
|
||||
@@ -7,7 +7,8 @@ import traceback
|
||||
|
||||
|
||||
def insert_line_in_string(input_string, new_str, insert_line):
|
||||
"""Inserts a new line into a string at the specified line number.
|
||||
"""
|
||||
Inserts a new line into a string at the specified line number.
|
||||
|
||||
:param input_string: The original string.
|
||||
:param new_str: The string to insert.
|
||||
@@ -28,7 +29,8 @@ def insert_line_in_string(input_string, new_str, insert_line):
|
||||
|
||||
|
||||
def print_string_diff(original, modified):
|
||||
"""Prints the differences between two strings line by line.
|
||||
"""
|
||||
Prints the differences between two strings line by line.
|
||||
|
||||
:param original: The original string.
|
||||
:param modified: The modified string.
|
||||
|
||||
@@ -37,7 +37,8 @@ def extract_preamble_classes_and_functions(code):
|
||||
current_position = 0
|
||||
|
||||
def extract_class_body(code: str, start_index: int) -> tuple[str, int]:
|
||||
"""Extracts the body of a class from the given code starting from the specified index.
|
||||
"""
|
||||
Extracts the body of a class from the given code starting from the specified index.
|
||||
Returns the class body and the end index of the class body.
|
||||
"""
|
||||
if not code or start_index < 0 or start_index >= len(code):
|
||||
@@ -167,8 +168,8 @@ def extract_preamble_classes_and_functions(code):
|
||||
def filter_passing_tests(
|
||||
test_content: str, test_output: str, repo: str
|
||||
) -> tuple[str, list[str], list[str]]:
|
||||
"""Filter tests based on their execution results.
|
||||
|
||||
"""
|
||||
Filter tests based on their execution results.
|
||||
Returns:
|
||||
Tuple containing:
|
||||
- Modified test content with only passing tests
|
||||
@@ -245,7 +246,8 @@ def filter_passing_tests(
|
||||
def filter_tests(
|
||||
test_content: str, test_output: str, repo: str
|
||||
) -> tuple[str, list[str], list[str]]:
|
||||
"""Filter tests using AST parsing to remove failing test functions from the test file.
|
||||
"""
|
||||
Filter tests using AST parsing to remove failing test functions from the test file.
|
||||
Non-test functions (e.g. setup or helper methods) and classes (even if all test methods are failing)
|
||||
are preserved.
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ DIFF_MODIFIED_FILE_REGEX = r'--- a/(.*)'
|
||||
|
||||
@dataclass
|
||||
class TestSpec:
|
||||
"""A dataclass that represents a test specification for a single instance of SWE-bench."""
|
||||
"""
|
||||
A dataclass that represents a test specification for a single instance of SWE-bench.
|
||||
"""
|
||||
|
||||
instance_id: str
|
||||
id: str
|
||||
@@ -84,7 +86,10 @@ def make_test_setup(specs, env_name, repo_directory, includes_tox=False):
|
||||
|
||||
|
||||
def make_test_script_list(test_cmd, specs, env_name, repo_directory):
|
||||
"""Runs the tests."""
|
||||
"""
|
||||
Runs the tests.
|
||||
"""
|
||||
|
||||
includes_tox = 'tox' in test_cmd
|
||||
eval_commands = make_test_setup(specs, env_name, repo_directory, includes_tox)
|
||||
eval_commands += [
|
||||
@@ -99,7 +104,10 @@ def make_test_script_list(test_cmd, specs, env_name, repo_directory):
|
||||
|
||||
|
||||
def make_mutation_script_list(specs, env_name, repo_directory, mutation_timeout):
|
||||
"""Runs the tests."""
|
||||
"""
|
||||
Runs the tests.
|
||||
"""
|
||||
|
||||
eval_commands = make_test_setup(specs, env_name, repo_directory)
|
||||
eval_commands += [
|
||||
'cosmic-ray init mutation.toml mutation.sqlite',
|
||||
|
||||
@@ -11,7 +11,8 @@ from evaluation.benchmarks.testgeneval.constants import (
|
||||
|
||||
|
||||
def get_test_directives(instance: TestGenEvalInstance) -> list:
|
||||
"""Get test directives from the test_patch of a task instance
|
||||
"""
|
||||
Get test directives from the test_patch of a task instance
|
||||
|
||||
Args:
|
||||
instance (dict): task instance
|
||||
@@ -42,7 +43,9 @@ def get_test_directives(instance: TestGenEvalInstance) -> list:
|
||||
def load_testgeneval_dataset(
|
||||
name='kjain14/testgeneval', split='test', ids=None
|
||||
) -> list[TestGenEvalInstance]:
|
||||
"""Load SWE-bench dataset from Hugging Face Datasets or local .json/.jsonl file"""
|
||||
"""
|
||||
Load SWE-bench dataset from Hugging Face Datasets or local .json/.jsonl file
|
||||
"""
|
||||
# check that all instance IDs are in the dataset
|
||||
if ids:
|
||||
ids = set(ids)
|
||||
|
||||
@@ -24,7 +24,9 @@ class ActionType(Enum):
|
||||
|
||||
@dataclass
|
||||
class Selector:
|
||||
"""Represents either a direct anchor ID or a descriptive selector"""
|
||||
"""
|
||||
Represents either a direct anchor ID or a descriptive selector
|
||||
"""
|
||||
|
||||
value: str
|
||||
is_anchor: bool = False
|
||||
@@ -147,7 +149,8 @@ def find_matching_anchor(content: str, selector: str) -> str | None:
|
||||
|
||||
|
||||
def resolve_action(action: BrowserAction, content: str) -> BrowserAction:
|
||||
"""Resolve any descriptive selectors in the action to anchor IDs based on the content.
|
||||
"""
|
||||
Resolve any descriptive selectors in the action to anchor IDs based on the content.
|
||||
Returns a new action with resolved selectors.
|
||||
"""
|
||||
if isinstance(action, (InputAction, ClickAction)):
|
||||
@@ -171,7 +174,8 @@ def pre_login(
|
||||
save_screenshots=True,
|
||||
screenshots_dir='screenshots',
|
||||
):
|
||||
"""Logs in to all the websites that are needed for the evaluation.
|
||||
"""
|
||||
Logs in to all the websites that are needed for the evaluation.
|
||||
Once logged in, the sessions would be cached in the browser, so OpenHands
|
||||
agent doesn't need to log in to these websites again.
|
||||
"""
|
||||
|
||||
@@ -68,7 +68,8 @@ def get_config(
|
||||
|
||||
|
||||
def load_dependencies(runtime: Runtime) -> list[str]:
|
||||
"""Every task has a dependencies.yml file, which lists all the services that the
|
||||
"""
|
||||
Every task has a dependencies.yml file, which lists all the services that the
|
||||
task depends on. This function loads the file and returns all dependent service names.
|
||||
"""
|
||||
command = 'cat /utils/dependencies.yml'
|
||||
|
||||
@@ -11,7 +11,9 @@ import sys
|
||||
|
||||
|
||||
def calculate_cost(model: str, prompt_tokens: int, completion_tokens: int) -> float:
|
||||
"""Calculate the cost of the model call."""
|
||||
"""
|
||||
Calculate the cost of the model call.
|
||||
"""
|
||||
if 'claude-3-5-sonnet' in model.lower():
|
||||
# https://www.anthropic.com/pricing#anthropic-api, accessed 12/11/2024
|
||||
return 0.000003 * prompt_tokens + 0.000015 * completion_tokens
|
||||
@@ -58,7 +60,8 @@ def calculate_cost(model: str, prompt_tokens: int, completion_tokens: int) -> fl
|
||||
|
||||
|
||||
def analyze_eval_json_file(filepath: str) -> tuple[int, int]:
|
||||
"""Analyze a single eval JSON file and extract the total and result from final_score.
|
||||
"""
|
||||
Analyze a single eval JSON file and extract the total and result from final_score.
|
||||
|
||||
Args:
|
||||
filepath: Path to the JSON file
|
||||
@@ -81,7 +84,8 @@ def analyze_eval_json_file(filepath: str) -> tuple[int, int]:
|
||||
|
||||
|
||||
def analyze_traj_json_file(filepath: str) -> tuple[int, float]:
|
||||
"""Analyze a single trajectory JSON file and extract the steps and tokens
|
||||
"""
|
||||
Analyze a single trajectory JSON file and extract the steps and tokens
|
||||
for each step. Then estimate the cost based on the tokens and the model type.
|
||||
Note: this is assuming there's no prompt caching at all.
|
||||
"""
|
||||
@@ -111,7 +115,8 @@ def analyze_traj_json_file(filepath: str) -> tuple[int, float]:
|
||||
def analyze_folder(
|
||||
folder_path: str,
|
||||
) -> tuple[dict[str, tuple[int, int]], dict[str, tuple[int, float]]]:
|
||||
"""Analyze all eval_*.json & traj_*.json files in the specified folder.
|
||||
"""
|
||||
Analyze all eval_*.json & traj_*.json files in the specified folder.
|
||||
|
||||
Args:
|
||||
folder_path: Path to the folder containing JSON files
|
||||
@@ -143,7 +148,9 @@ def analyze_folder(
|
||||
|
||||
|
||||
def get_task_nature_category(task_name: str) -> str:
|
||||
"""Get the nature category of the task."""
|
||||
"""
|
||||
Get the nature category of the task.
|
||||
"""
|
||||
task_nature = task_name.split('-')[0]
|
||||
if task_nature.lower() in ['sde', 'pm', 'ds', 'admin', 'hr', 'finance']:
|
||||
return task_nature
|
||||
@@ -152,7 +159,8 @@ def get_task_nature_category(task_name: str) -> str:
|
||||
|
||||
|
||||
def calculate_score(total: int, result: int) -> float:
|
||||
"""Calculate the score as a number between 0 and 1.
|
||||
"""
|
||||
Calculate the score as a number between 0 and 1.
|
||||
|
||||
Formula: score = (result / total) * 0.5 + (result // total) * 0.5
|
||||
Explanation:
|
||||
@@ -170,7 +178,8 @@ def calculate_score(total: int, result: int) -> float:
|
||||
|
||||
|
||||
def is_perfect_completion(total: int, result: int) -> bool:
|
||||
"""Check if the task achieved perfect completion.
|
||||
"""
|
||||
Check if the task achieved perfect completion.
|
||||
|
||||
Args:
|
||||
total: Total possible points
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""GPT performs line level generation prediction and truncates overly long tokens"""
|
||||
"""
|
||||
GPT performs line level generation prediction and truncates overly long tokens
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
@@ -54,7 +56,8 @@ def predict(content, model_name):
|
||||
|
||||
|
||||
def bulid_prompt(description, old_version, old_code, new_version) -> str:
|
||||
"""Build prompt
|
||||
"""
|
||||
build prompt
|
||||
:param version:
|
||||
:param description:
|
||||
:param masked_code:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""GPT performs line level generation prediction and truncates overly long tokens"""
|
||||
"""
|
||||
GPT performs line level generation prediction and truncates overly long tokens
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
@@ -54,7 +56,8 @@ def predict(content, model_name):
|
||||
|
||||
|
||||
def bulid_prompt(version, description) -> str:
|
||||
"""Build prompt
|
||||
"""
|
||||
build prompt
|
||||
:param version:
|
||||
:param description:
|
||||
:param masked_code:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""block completion"""
|
||||
"""
|
||||
block completion
|
||||
"""
|
||||
|
||||
import copy
|
||||
import gc
|
||||
@@ -77,7 +79,8 @@ def run_inference(model_name, origin_data_list):
|
||||
|
||||
|
||||
def bulid_prompt(version, description) -> str:
|
||||
"""Build prompt
|
||||
"""
|
||||
build prompt
|
||||
:param version:
|
||||
:param description:
|
||||
:param masked_code:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""code migration"""
|
||||
"""
|
||||
code migration
|
||||
"""
|
||||
|
||||
import copy
|
||||
import gc
|
||||
@@ -79,7 +81,8 @@ def run_inference(model_name, origin_data_list):
|
||||
|
||||
|
||||
def bulid_prompt(description, old_version, old_code, new_version) -> str:
|
||||
"""Build prompt
|
||||
"""
|
||||
build prompt
|
||||
:param version:
|
||||
:param description:
|
||||
:param masked_code:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""评测block的预测能力
|
||||
"""
|
||||
评测block的预测能力
|
||||
1、判断是否包含正确的函数名
|
||||
2、判断是否合法
|
||||
3、计算ISM,和PM
|
||||
@@ -21,7 +22,8 @@ def is_code_valid(code):
|
||||
|
||||
|
||||
def longest_common_prefix_between_lists_with_elements(list1, list2):
|
||||
"""计算两个字符串列表中元素的最长前缀匹配长度
|
||||
"""
|
||||
计算两个字符串列表中元素的最长前缀匹配长度
|
||||
:param list1:
|
||||
:param list2:
|
||||
:return:
|
||||
@@ -44,7 +46,8 @@ def longest_common_prefix_between_lists_with_elements(list1, list2):
|
||||
|
||||
|
||||
def get_token(ans_code: str, output_code: str):
|
||||
"""对代码进行词法分析,分解成标识符,返回两个标识符列表
|
||||
"""
|
||||
对代码进行词法分析,分解成标识符,返回两个标识符列表
|
||||
:param ans_code:
|
||||
:param output_code:
|
||||
:return:
|
||||
@@ -91,7 +94,8 @@ def get_token(ans_code: str, output_code: str):
|
||||
|
||||
|
||||
def get_token_per_line(code: str):
|
||||
"""对每一行代码进行词法分析,记录每一行的标识符
|
||||
"""
|
||||
对每一行代码进行词法分析,记录每一行的标识符
|
||||
:param code: 代码字符串
|
||||
:return: 每一行的标识符列表组成的列表
|
||||
"""
|
||||
@@ -113,7 +117,8 @@ def get_token_per_line(code: str):
|
||||
|
||||
|
||||
def get_ISM(answer_code: str, model_output_list: list, answer_name: str) -> list:
|
||||
"""计算ISM,返回一个有序的得分列表
|
||||
"""
|
||||
计算ISM,返回一个有序的得分列表
|
||||
:return:
|
||||
"""
|
||||
score_list = []
|
||||
@@ -152,7 +157,8 @@ def get_ISM(answer_code: str, model_output_list: list, answer_name: str) -> list
|
||||
def get_ISM_without_verification(
|
||||
answer_code: str, model_output_list: list, answer_name: str
|
||||
) -> list:
|
||||
"""计算ISM,返回一个有序的得分列表
|
||||
"""
|
||||
计算ISM,返回一个有序的得分列表
|
||||
:return:
|
||||
"""
|
||||
score_list = []
|
||||
@@ -184,7 +190,8 @@ def get_ISM_without_verification(
|
||||
|
||||
|
||||
def longest_common_prefix_with_lengths(list1, list2):
|
||||
"""计算两个二维列表中每个子列表的最长前缀匹配长度,并记录拥有最长前缀匹配长度的两个子列表的长度
|
||||
"""
|
||||
计算两个二维列表中每个子列表的最长前缀匹配长度,并记录拥有最长前缀匹配长度的两个子列表的长度
|
||||
:param list1: 第一个二维列表
|
||||
:param list2: 第二个二维列表
|
||||
:return: 最长前缀匹配长度以及拥有最长前缀匹配长度的两个子列表的长度
|
||||
@@ -209,7 +216,8 @@ def longest_common_prefix_with_lengths(list1, list2):
|
||||
|
||||
|
||||
def get_PM(answer_code: str, model_output_list: list, answer_name: str) -> list:
|
||||
"""计算PM,返回一个有序的得分列表
|
||||
"""
|
||||
计算PM,返回一个有序的得分列表
|
||||
:return:
|
||||
"""
|
||||
score_list = []
|
||||
@@ -246,7 +254,8 @@ def get_PM(answer_code: str, model_output_list: list, answer_name: str) -> list:
|
||||
|
||||
|
||||
def get_score(score_list: list, k):
|
||||
"""计算score@n,k
|
||||
"""
|
||||
计算score@n,k
|
||||
:param score_list:
|
||||
:param k:
|
||||
:return:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Calculate the cdc score for migration"""
|
||||
"""
|
||||
Calculate the cdc score for migration
|
||||
"""
|
||||
|
||||
import json
|
||||
import math
|
||||
@@ -9,7 +11,8 @@ import re
|
||||
|
||||
|
||||
def is_correct_parameter_count(function_name, correct_code, test_code):
|
||||
"""判断参数数量是否一致
|
||||
"""
|
||||
判断参数数量是否一致
|
||||
:param function_name:
|
||||
:param correct_code:
|
||||
:param test_code:
|
||||
@@ -40,7 +43,8 @@ def is_correct_parameter_count(function_name, correct_code, test_code):
|
||||
|
||||
|
||||
def check_keyword_parameters(function_name, correct_code, test_code):
|
||||
"""判断关键词参数赋值是否正确使用
|
||||
"""
|
||||
判断关键词参数赋值是否正确使用
|
||||
:param function_name:
|
||||
:param correct_code:
|
||||
:param test_code:
|
||||
@@ -78,7 +82,8 @@ def check_keyword_parameters(function_name, correct_code, test_code):
|
||||
|
||||
|
||||
def with_correct(answer_code: str, model_output: str) -> bool:
|
||||
"""当answer是with结构时,判断模型生成的是不是with结构
|
||||
"""
|
||||
当answer是with结构时,判断模型生成的是不是with结构
|
||||
:param answer_code:
|
||||
:param model_output:
|
||||
:return:
|
||||
@@ -100,7 +105,9 @@ def compute_block_score_k(
|
||||
core_line_in_core_block,
|
||||
core_line_in_output_clear,
|
||||
):
|
||||
"""cdc需要满足五个条件,em只需要满足第一个条件"""
|
||||
"""
|
||||
cdc需要满足五个条件,em只需要满足第一个条件
|
||||
"""
|
||||
c = 0
|
||||
n = len(model_output)
|
||||
for index, code in enumerate(model_output):
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Calculate the cdc score for line and block"""
|
||||
"""
|
||||
Calculate the cdc score for line and block
|
||||
"""
|
||||
|
||||
import json
|
||||
import math
|
||||
@@ -17,7 +19,8 @@ def is_code_valid(code):
|
||||
|
||||
|
||||
def is_correct_parameter_count(function_name, correct_code, test_code):
|
||||
"""判断参数数量是否一致
|
||||
"""
|
||||
判断参数数量是否一致
|
||||
:param function_name:
|
||||
:param correct_code:
|
||||
:param test_code:
|
||||
@@ -48,7 +51,8 @@ def is_correct_parameter_count(function_name, correct_code, test_code):
|
||||
|
||||
|
||||
def check_keyword_parameters(function_name, correct_code, test_code):
|
||||
"""判断关键词参数赋值是否正确使用
|
||||
"""
|
||||
判断关键词参数赋值是否正确使用
|
||||
:param function_name:
|
||||
:param correct_code:
|
||||
:param test_code:
|
||||
@@ -86,7 +90,8 @@ def check_keyword_parameters(function_name, correct_code, test_code):
|
||||
|
||||
|
||||
def with_correct(answer_code: str, model_output: str) -> bool:
|
||||
"""当answer是with结构时,判断模型生成的是不是with结构
|
||||
"""
|
||||
当answer是with结构时,判断模型生成的是不是with结构
|
||||
:param answer_code:
|
||||
:param model_output:
|
||||
:return:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Calculate the cdc score for line and block"""
|
||||
"""
|
||||
Calculate the cdc score for line and block
|
||||
"""
|
||||
|
||||
import json
|
||||
import math
|
||||
@@ -17,7 +19,8 @@ def is_code_valid(code):
|
||||
|
||||
|
||||
def is_correct_parameter_count(function_name, correct_code, test_code):
|
||||
"""判断参数数量是否一致
|
||||
"""
|
||||
判断参数数量是否一致
|
||||
:param function_name:
|
||||
:param correct_code:
|
||||
:param test_code:
|
||||
@@ -48,7 +51,8 @@ def is_correct_parameter_count(function_name, correct_code, test_code):
|
||||
|
||||
|
||||
def check_keyword_parameters(function_name, correct_code, test_code):
|
||||
"""判断关键词参数赋值是否正确使用
|
||||
"""
|
||||
判断关键词参数赋值是否正确使用
|
||||
:param function_name:
|
||||
:param correct_code:
|
||||
:param test_code:
|
||||
@@ -86,7 +90,8 @@ def check_keyword_parameters(function_name, correct_code, test_code):
|
||||
|
||||
|
||||
def with_correct(answer_code: str, model_output: str) -> bool:
|
||||
"""当answer是with结构时,判断模型生成的是不是with结构
|
||||
"""
|
||||
当answer是with结构时,判断模型生成的是不是with结构
|
||||
:param answer_code:
|
||||
:param model_output:
|
||||
:return:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Find the line of code generated by the model using the block in the version code"""
|
||||
"""
|
||||
Find the line of code generated by the model using the block in the version code
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Find the line of code generated by the model using the block in the version code"""
|
||||
"""
|
||||
Find the line of code generated by the model using the block in the version code
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Clear the<start>and<end>generated by the model in inference"""
|
||||
"""
|
||||
Clear the<start>and<end>generated by the model in inference
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
|
||||
@@ -622,7 +622,8 @@ def compatibility_for_eval_history_pairs(
|
||||
|
||||
|
||||
def is_fatal_evaluation_error(error: str | None) -> bool:
|
||||
"""The AgentController class overrides last error for certain exceptions
|
||||
"""
|
||||
The AgentController class overrides last error for certain exceptions
|
||||
We want to ensure those exeption do not overlap with fatal exceptions defined here
|
||||
This is because we do a comparisino against the stringified error
|
||||
"""
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { fireEvent, render, screen, within } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { act } from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { MaintenanceBanner } from "#/components/features/maintenance/maintenance-banner";
|
||||
|
||||
// Mock react-i18next
|
||||
vi.mock("react-i18next", async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import("react-i18next")>("react-i18next");
|
||||
const actual = await vi.importActual<typeof import("react-i18next")>("react-i18next");
|
||||
return {
|
||||
...actual,
|
||||
useTranslation: () => ({
|
||||
t: (key: string, options?: { time?: string }) => {
|
||||
const translations: Record<string, string> = {
|
||||
MAINTENANCE$SCHEDULED_MESSAGE: `Scheduled maintenance will begin at ${options?.time || "{{time}}"}`,
|
||||
"MAINTENANCE$SCHEDULED_MESSAGE": `Scheduled maintenance will begin at ${options?.time || "{{time}}"}`,
|
||||
};
|
||||
return translations[key] || key;
|
||||
},
|
||||
@@ -21,91 +19,34 @@ vi.mock("react-i18next", async () => {
|
||||
});
|
||||
|
||||
describe("MaintenanceBanner", () => {
|
||||
afterEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
it("renders maintenance banner with formatted time", () => {
|
||||
const startTime = "2024-01-15T10:00:00-05:00"; // EST timestamp
|
||||
|
||||
const { container } = render(<MaintenanceBanner startTime={startTime} />);
|
||||
|
||||
// Check if the banner is rendered
|
||||
const banner = screen.queryByTestId("maintenance-banner");
|
||||
expect(banner).toBeInTheDocument();
|
||||
expect(screen.getByText(/Scheduled maintenance will begin at/)).toBeInTheDocument();
|
||||
|
||||
// Check if the warning icon (SVG) is present
|
||||
const svgIcon = container.querySelector("svg");
|
||||
const svgIcon = container.querySelector('svg');
|
||||
expect(svgIcon).toBeInTheDocument();
|
||||
|
||||
// Check if the button to close is present
|
||||
const button = within(banner!).queryByTestId("dismiss-button");
|
||||
expect(button).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// maintenance-banner
|
||||
|
||||
it("handles invalid date gracefully", () => {
|
||||
const invalidTime = "invalid-date";
|
||||
|
||||
render(<MaintenanceBanner startTime={invalidTime} />);
|
||||
|
||||
// Check if the banner is rendered
|
||||
const banner = screen.queryByTestId("maintenance-banner");
|
||||
expect(banner).not.toBeInTheDocument();
|
||||
// Should still render the banner with the original string
|
||||
expect(screen.getByText(/Scheduled maintenance will begin at invalid-date/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("click on dismiss button removes banner", () => {
|
||||
const startTime = "2024-01-15T10:00:00-05:00"; // EST timestamp
|
||||
it("formats ISO date string correctly", () => {
|
||||
const isoTime = "2024-01-15T15:30:00.000Z";
|
||||
|
||||
render(<MaintenanceBanner startTime={startTime} />);
|
||||
render(<MaintenanceBanner startTime={isoTime} />);
|
||||
|
||||
// Check if the banner is rendered
|
||||
const banner = screen.queryByTestId("maintenance-banner");
|
||||
|
||||
const button = within(banner!).queryByTestId("dismiss-button");
|
||||
act(() => {
|
||||
fireEvent.click(button!);
|
||||
});
|
||||
|
||||
expect(banner).not.toBeInTheDocument();
|
||||
});
|
||||
it("banner reappears after dismissing on next maintenance event(future time)", () => {
|
||||
const startTime = "2024-01-15T10:00:00-05:00"; // EST timestamp
|
||||
const nextStartTime = "2025-01-15T10:00:00-05:00"; // EST timestamp
|
||||
|
||||
const { rerender } = render(<MaintenanceBanner startTime={startTime} />);
|
||||
|
||||
// Check if the banner is rendered
|
||||
const banner = screen.queryByTestId("maintenance-banner");
|
||||
const button = within(banner!).queryByTestId("dismiss-button");
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(button!);
|
||||
});
|
||||
|
||||
expect(banner).not.toBeInTheDocument();
|
||||
rerender(<MaintenanceBanner startTime={nextStartTime} />);
|
||||
|
||||
expect(screen.queryByTestId("maintenance-banner")).toBeInTheDocument();
|
||||
});
|
||||
it("banner doesn't reappear after dismissing on next maintenance event(past time)", () => {
|
||||
const startTime = "2024-01-15T10:00:00-05:00"; // EST timestamp
|
||||
const nextStartTime = "2023-01-15T10:00:00-05:00"; // EST timestamp
|
||||
|
||||
const { rerender } = render(<MaintenanceBanner startTime={startTime} />);
|
||||
|
||||
// Check if the banner is rendered
|
||||
const banner = screen.queryByTestId("maintenance-banner");
|
||||
const button = within(banner!).queryByTestId("dismiss-button");
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(button!);
|
||||
});
|
||||
|
||||
expect(banner).not.toBeInTheDocument();
|
||||
rerender(<MaintenanceBanner startTime={nextStartTime} />);
|
||||
|
||||
expect(screen.queryByTestId("maintenance-banner")).not.toBeInTheDocument();
|
||||
// Should render the banner (exact time format will depend on user's timezone)
|
||||
expect(screen.getByText(/Scheduled maintenance will begin at/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { renderHook } from "@testing-library/react";
|
||||
import { useDocumentTitleFromState } from "#/hooks/use-document-title-from-state";
|
||||
import { useActiveConversation } from "#/hooks/query/use-active-conversation";
|
||||
|
||||
// Mock the useActiveConversation hook
|
||||
vi.mock("#/hooks/query/use-active-conversation");
|
||||
|
||||
const mockUseActiveConversation = vi.mocked(useActiveConversation);
|
||||
|
||||
describe("useDocumentTitleFromState", () => {
|
||||
const originalTitle = document.title;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
document.title = "Test";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.title = originalTitle;
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should set document title to default suffix when no conversation", () => {
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: null,
|
||||
} as any);
|
||||
|
||||
renderHook(() => useDocumentTitleFromState());
|
||||
|
||||
expect(document.title).toBe("OpenHands");
|
||||
});
|
||||
|
||||
it("should set document title to custom suffix when no conversation", () => {
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: null,
|
||||
} as any);
|
||||
|
||||
renderHook(() => useDocumentTitleFromState("Custom App"));
|
||||
|
||||
expect(document.title).toBe("Custom App");
|
||||
});
|
||||
|
||||
it("should set document title with conversation title", () => {
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: {
|
||||
conversation_id: "123",
|
||||
title: "My Conversation",
|
||||
status: "RUNNING",
|
||||
},
|
||||
} as any);
|
||||
|
||||
renderHook(() => useDocumentTitleFromState());
|
||||
|
||||
expect(document.title).toBe("My Conversation | OpenHands");
|
||||
});
|
||||
|
||||
it("should update document title when conversation title changes", () => {
|
||||
// Initial state - no conversation
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: null,
|
||||
} as any);
|
||||
|
||||
const { rerender } = renderHook(() => useDocumentTitleFromState());
|
||||
expect(document.title).toBe("OpenHands");
|
||||
|
||||
// Conversation with initial title
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: {
|
||||
conversation_id: "123",
|
||||
title: "Conversation 65e29",
|
||||
status: "RUNNING",
|
||||
},
|
||||
} as any);
|
||||
rerender();
|
||||
expect(document.title).toBe("Conversation 65e29 | OpenHands");
|
||||
|
||||
// Conversation title updated to human-readable title
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: {
|
||||
conversation_id: "123",
|
||||
title: "Help me build a React app",
|
||||
status: "RUNNING",
|
||||
},
|
||||
} as any);
|
||||
rerender();
|
||||
expect(document.title).toBe("Help me build a React app | OpenHands");
|
||||
});
|
||||
|
||||
it("should handle conversation without title", () => {
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: {
|
||||
conversation_id: "123",
|
||||
title: undefined,
|
||||
status: "RUNNING",
|
||||
},
|
||||
} as any);
|
||||
|
||||
renderHook(() => useDocumentTitleFromState());
|
||||
|
||||
expect(document.title).toBe("OpenHands");
|
||||
});
|
||||
|
||||
it("should handle empty conversation title", () => {
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: {
|
||||
conversation_id: "123",
|
||||
title: "",
|
||||
status: "RUNNING",
|
||||
},
|
||||
} as any);
|
||||
|
||||
renderHook(() => useDocumentTitleFromState());
|
||||
|
||||
expect(document.title).toBe("OpenHands");
|
||||
});
|
||||
|
||||
it("should reset document title on cleanup", () => {
|
||||
mockUseActiveConversation.mockReturnValue({
|
||||
data: {
|
||||
conversation_id: "123",
|
||||
title: "My Conversation",
|
||||
status: "RUNNING",
|
||||
},
|
||||
} as any);
|
||||
|
||||
const { unmount } = renderHook(() => useDocumentTitleFromState());
|
||||
|
||||
expect(document.title).toBe("My Conversation | OpenHands");
|
||||
|
||||
unmount();
|
||||
|
||||
expect(document.title).toBe("OpenHands");
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import i18n from "../../src/i18n";
|
||||
import { AccountSettingsContextMenu } from "../../src/components/features/context-menu/account-settings-context-menu";
|
||||
import { renderWithProviders } from "../../test-utils";
|
||||
@@ -17,63 +17,4 @@ describe("Translations", () => {
|
||||
screen.getByTestId("account-settings-context-menu"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not attempt to load unsupported language codes", async () => {
|
||||
// Test that the configuration prevents 404 errors by not attempting to load
|
||||
// unsupported language codes like 'en-US@posix'
|
||||
const originalLanguage = i18n.language;
|
||||
|
||||
try {
|
||||
// With nonExplicitSupportedLngs: false, i18next will not attempt to load
|
||||
// unsupported language codes, preventing 404 errors
|
||||
|
||||
// Test with a language code that includes region but is not in supportedLngs
|
||||
await i18n.changeLanguage("en-US@posix");
|
||||
|
||||
// Since "en-US@posix" is not in supportedLngs and nonExplicitSupportedLngs is false,
|
||||
// i18next should fall back to the fallbackLng ("en")
|
||||
expect(i18n.language).toBe("en");
|
||||
|
||||
// Test another unsupported region code
|
||||
await i18n.changeLanguage("ja-JP");
|
||||
|
||||
// Even with nonExplicitSupportedLngs: false, i18next still falls back to base language
|
||||
// if it exists in supportedLngs, but importantly, it won't make a 404 request first
|
||||
expect(i18n.language).toBe("ja");
|
||||
|
||||
// Test that supported languages still work
|
||||
await i18n.changeLanguage("ja");
|
||||
expect(i18n.language).toBe("ja");
|
||||
|
||||
await i18n.changeLanguage("zh-CN");
|
||||
expect(i18n.language).toBe("zh-CN");
|
||||
|
||||
} finally {
|
||||
// Restore the original language
|
||||
await i18n.changeLanguage(originalLanguage);
|
||||
}
|
||||
});
|
||||
|
||||
it("should have proper i18n configuration", () => {
|
||||
// Test that the i18n instance has the expected configuration
|
||||
expect(i18n.options.supportedLngs).toBeDefined();
|
||||
|
||||
// nonExplicitSupportedLngs should be false to prevent 404 errors
|
||||
expect(i18n.options.nonExplicitSupportedLngs).toBe(false);
|
||||
|
||||
// fallbackLng can be a string or array, check if it includes "en"
|
||||
const fallbackLng = i18n.options.fallbackLng;
|
||||
if (Array.isArray(fallbackLng)) {
|
||||
expect(fallbackLng).toContain("en");
|
||||
} else {
|
||||
expect(fallbackLng).toBe("en");
|
||||
}
|
||||
|
||||
// Test that supported languages include both base and region-specific codes
|
||||
const supportedLngs = i18n.options.supportedLngs as string[];
|
||||
expect(supportedLngs).toContain("en");
|
||||
expect(supportedLngs).toContain("zh-CN");
|
||||
expect(supportedLngs).toContain("zh-TW");
|
||||
expect(supportedLngs).toContain("ko-KR");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.53.0",
|
||||
"version": "0.52.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.53.0",
|
||||
"version": "0.52.1",
|
||||
"dependencies": {
|
||||
"@heroui/react": "^2.8.2",
|
||||
"@heroui/use-infinite-scroll": "^2.2.10",
|
||||
@@ -21,13 +21,11 @@
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/react-query": "^5.84.2",
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.4.0",
|
||||
"axios": "^1.11.0",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"framer-motion": "^12.23.12",
|
||||
"i18next": "^25.3.2",
|
||||
@@ -3396,9 +3394,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||
"version": "0.3.12",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
|
||||
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
@@ -3415,15 +3413,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
|
||||
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.30",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
|
||||
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
|
||||
"version": "0.3.29",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
|
||||
"integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
@@ -4394,7 +4392,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.8.0.tgz",
|
||||
"integrity": "sha512-5NA9yLZComM+kCD3zNPL3rjrAFjzzODY8hjAJlpz/6jpyXoF28W8QTSo8rxc56XVNLONM75Y5nq1wzeEcWFFKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.27.7",
|
||||
"@babel/generator": "^7.27.5",
|
||||
@@ -4455,15 +4452,13 @@
|
||||
"version": "1.0.0-beta.27",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
||||
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@react-router/dev/node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
|
||||
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
||||
@@ -4484,7 +4479,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
||||
"integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -4502,33 +4496,10 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-router/express": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.8.0.tgz",
|
||||
"integrity": "sha512-lNUwux5IfMqczIL3gXZ/mauPUoVz65fSLPnUTkP7hkh/P7fcsPtYkmcixuaWb+882lY+Glf157OdoIMbcSMBaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-router/node": "7.8.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": "^4.17.1 || ^5",
|
||||
"react-router": "7.8.0",
|
||||
"typescript": "^5.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-router/node": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.8.0.tgz",
|
||||
"integrity": "sha512-/FFN9vqI2EHPwDCHTvsMInhrYvwJ5SlCeyUr1oWUxH47JyYkooVFks5++M4VkrTgj2ZBsMjPPKy0xRNTQdtBDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mjackson/node-fetch-server": "^0.2.0"
|
||||
},
|
||||
@@ -4549,7 +4520,6 @@
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/serve/-/serve-7.8.0.tgz",
|
||||
"integrity": "sha512-DokCv1GfOMt9KHu+k3WYY9sP5nOEzq7za+Vi3dWPHoY5oP0wgv8S4DnTPU08ASY8iFaF38NAzapbSFfu6Xfr0Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-router/express": "7.8.0",
|
||||
"@react-router/node": "7.8.0",
|
||||
@@ -4569,6 +4539,27 @@
|
||||
"react-router": "7.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-router/serve/node_modules/@react-router/express": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.8.0.tgz",
|
||||
"integrity": "sha512-lNUwux5IfMqczIL3gXZ/mauPUoVz65fSLPnUTkP7hkh/P7fcsPtYkmcixuaWb+882lY+Glf157OdoIMbcSMBaA==",
|
||||
"dependencies": {
|
||||
"@react-router/node": "7.8.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": "^4.17.1 || ^5",
|
||||
"react-router": "7.8.0",
|
||||
"typescript": "^5.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-stately/calendar": {
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.8.3.tgz",
|
||||
@@ -5270,8 +5261,7 @@
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.30",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.30.tgz",
|
||||
"integrity": "sha512-whXaSoNUFiyDAjkUF8OBpOm77Szdbk5lGNqFe6CbVbJFrhCCPinCbRA3NjawwlNHla1No7xvXXh+CpSxnPfUEw==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-whXaSoNUFiyDAjkUF8OBpOm77Szdbk5lGNqFe6CbVbJFrhCCPinCbRA3NjawwlNHla1No7xvXXh+CpSxnPfUEw=="
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.2.0",
|
||||
@@ -6271,10 +6261,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.85.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.85.0.tgz",
|
||||
"integrity": "sha512-t1HMfToVMGfwEJRya6GG7gbK0luZJd+9IySFNePL1BforU1F3LqQ3tBC2Rpvr88bOrlU6PXyMLgJD0Yzn4ztUw==",
|
||||
"license": "MIT",
|
||||
"version": "5.84.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.2.tgz",
|
||||
"integrity": "sha512-cZadySzROlD2+o8zIfbD978p0IphuQzRWiiH3I2ugnTmz4jbjc0+TdibpwqxlzynEen8OulgAg+rzdNF37s7XQ==",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.83.1"
|
||||
},
|
||||
@@ -6543,9 +6532,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
|
||||
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
|
||||
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6559,9 +6548,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz",
|
||||
"integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==",
|
||||
"version": "19.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz",
|
||||
"integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
@@ -6729,14 +6718,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz",
|
||||
"integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==",
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz",
|
||||
"integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.39.1",
|
||||
"@typescript-eslint/types": "^8.39.1",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.39.0",
|
||||
"@typescript-eslint/types": "^8.39.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -6751,9 +6740,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": {
|
||||
"version": "8.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz",
|
||||
"integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==",
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
|
||||
"integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -6783,9 +6772,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz",
|
||||
"integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==",
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz",
|
||||
"integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -6894,16 +6883,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz",
|
||||
"integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==",
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz",
|
||||
"integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.39.1",
|
||||
"@typescript-eslint/types": "8.39.1",
|
||||
"@typescript-eslint/typescript-estree": "8.39.1"
|
||||
"@typescript-eslint/scope-manager": "8.39.0",
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/typescript-estree": "8.39.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -6918,14 +6907,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz",
|
||||
"integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==",
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz",
|
||||
"integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.39.1",
|
||||
"@typescript-eslint/visitor-keys": "8.39.1"
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/visitor-keys": "8.39.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -6936,9 +6925,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
|
||||
"version": "8.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz",
|
||||
"integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==",
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
|
||||
"integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -6950,16 +6939,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz",
|
||||
"integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==",
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz",
|
||||
"integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.39.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.39.1",
|
||||
"@typescript-eslint/types": "8.39.1",
|
||||
"@typescript-eslint/visitor-keys": "8.39.1",
|
||||
"@typescript-eslint/project-service": "8.39.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.39.0",
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/visitor-keys": "8.39.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -6979,13 +6968,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz",
|
||||
"integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==",
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz",
|
||||
"integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.39.1",
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -7040,19 +7029,6 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@uidotdev/usehooks": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.4.1.tgz",
|
||||
"integrity": "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ungap/structured-clone": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
||||
@@ -7063,7 +7039,6 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.0.tgz",
|
||||
"integrity": "sha512-Jx9JfsTa05bYkS9xo0hkofp2dCmp1blrKjw9JONs5BTHOvJCgLbaPSuZLGSVJW6u2qe0tc4eevY0+gSNNi0YCw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
||||
@@ -7093,7 +7068,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-rsc/-/plugin-rsc-0.4.11.tgz",
|
||||
"integrity": "sha512-+4H4wLi+Y9yF58znBfKgGfX8zcqUGt8ngnmNgzrdGdF1SVz7EO0sg7WnhK5fFVHt6fUxsVEjmEabsCWHKPL1Tw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mjackson/node-fetch-server": "^0.7.0",
|
||||
"es-module-lexer": "^1.7.0",
|
||||
@@ -7113,8 +7087,7 @@
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.7.0.tgz",
|
||||
"integrity": "sha512-un8diyEBKU3BTVj3GzlTPA1kIjCkGdD+AMYQy31Gf9JCkfoZzwgJ79GUtHrF2BN3XPNMLpubbzPcxys+a3uZEw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "3.2.4",
|
||||
@@ -7298,7 +7271,6 @@
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
@@ -7311,7 +7283,6 @@
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -7452,8 +7423,7 @@
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/array-includes": {
|
||||
"version": "3.1.9",
|
||||
@@ -7818,7 +7788,6 @@
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
@@ -7842,7 +7811,6 @@
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@@ -7850,8 +7818,7 @@
|
||||
"node_modules/body-parser/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
@@ -7877,9 +7844,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.25.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz",
|
||||
"integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==",
|
||||
"version": "4.25.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
||||
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -7896,8 +7863,8 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001733",
|
||||
"electron-to-chromium": "^1.5.199",
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.3"
|
||||
},
|
||||
@@ -8004,9 +7971,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001734",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz",
|
||||
"integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==",
|
||||
"version": "1.0.30001731",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz",
|
||||
"integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -8477,7 +8444,6 @@
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
@@ -8489,7 +8455,6 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -8504,7 +8469,6 @@
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -8512,8 +8476,7 @@
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.45.0",
|
||||
@@ -8724,16 +8687,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
@@ -8878,7 +8831,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
@@ -8988,9 +8940,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.200",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.200.tgz",
|
||||
"integrity": "sha512-rFCxROw7aOe4uPTfIAx+rXv9cEcGx+buAF4npnhtTqCJk5KDFRnh3+KYj7rdVh6lsFt5/aPs+Irj9rZ33WMA7w==",
|
||||
"version": "1.5.197",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.197.tgz",
|
||||
"integrity": "sha512-m1xWB3g7vJ6asIFz+2pBUbq3uGmfmln1M9SSvBe4QIFWYrRHylP73zL/3nMjDmwz8V+1xAXQDfBd6+HPW0WvDQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
@@ -9004,7 +8956,6 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -9357,8 +9308,7 @@
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
@@ -10031,7 +9981,6 @@
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -10070,7 +10019,6 @@
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
@@ -10116,7 +10064,6 @@
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@@ -10124,8 +10071,7 @@
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
@@ -10250,7 +10196,6 @@
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~2.0.0",
|
||||
@@ -10268,7 +10213,6 @@
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@@ -10276,8 +10220,7 @@
|
||||
"node_modules/finalhandler/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/find-root": {
|
||||
"version": "1.1.0",
|
||||
@@ -10414,7 +10357,6 @@
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -10464,7 +10406,6 @@
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -11077,7 +11018,6 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
@@ -11134,9 +11074,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "25.3.4",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.4.tgz",
|
||||
"integrity": "sha512-AHklEYFLiRRxW1Cb6zE9lfnEtYvsydRC8nRS3RSKGX3zCqZ8nLZwMaUsrb80YuccPNv2RNokDL8LkTNnp+6mDw==",
|
||||
"version": "25.3.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.2.tgz",
|
||||
"integrity": "sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -11186,7 +11126,6 @@
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
@@ -11315,7 +11254,6 @@
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
@@ -11669,7 +11607,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
|
||||
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.6"
|
||||
}
|
||||
@@ -12379,13 +12316,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lint-staged": {
|
||||
"version": "16.1.5",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.5.tgz",
|
||||
"integrity": "sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A==",
|
||||
"version": "16.1.4",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.4.tgz",
|
||||
"integrity": "sha512-xy7rnzQrhTVGKMpv6+bmIA3C0yET31x8OhKBYfvGo0/byeZ6E0BjGARrir3Kg/RhhYHutpsi01+2J5IpfVoueA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.5.0",
|
||||
"chalk": "^5.4.1",
|
||||
"commander": "^14.0.0",
|
||||
"debug": "^4.4.1",
|
||||
"lilconfig": "^3.1.3",
|
||||
@@ -12394,7 +12331,7 @@
|
||||
"nano-spawn": "^1.0.2",
|
||||
"pidtree": "^0.6.0",
|
||||
"string-argv": "^0.3.2",
|
||||
"yaml": "^2.8.1"
|
||||
"yaml": "^2.8.0"
|
||||
},
|
||||
"bin": {
|
||||
"lint-staged": "bin/lint-staged.js"
|
||||
@@ -12785,7 +12722,6 @@
|
||||
"version": "0.539.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.539.0.tgz",
|
||||
"integrity": "sha512-VVISr+VF2krO91FeuCrm1rSOLACQUYVy7NQkzrOty52Y8TlTPcXcMdQFj9bYzBgXbWCiywlwSZ3Z8u6a+6bMlg==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
@@ -13156,7 +13092,6 @@
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -13171,7 +13106,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
@@ -13190,7 +13124,6 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -13776,7 +13709,6 @@
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
@@ -14381,7 +14313,6 @@
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
@@ -14584,7 +14515,6 @@
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -14652,8 +14582,7 @@
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
@@ -14686,7 +14615,6 @@
|
||||
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-4.0.2.tgz",
|
||||
"integrity": "sha512-sqpQDUy8vgB7ycLkendSKS6HnVz1Rneoc3Rc+ZBUCe2pbqlVuCC5vF52l0NJ1aiMg/r1qfYF9/myz8CZeI2rjA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"is-reference": "^3.0.2",
|
||||
@@ -14820,7 +14748,6 @@
|
||||
"version": "1.259.0",
|
||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.259.0.tgz",
|
||||
"integrity": "sha512-6usLnJshky8fQ82ask7PIJh4BSFOU0VkRbFg8Zanm/HIlYMG1VOdRWlToA63JXeO7Bzm9TuREq1wFm5U2VEVCg==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"core-js": "^3.38.1",
|
||||
"fflate": "^0.4.8",
|
||||
@@ -14994,7 +14921,6 @@
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
@@ -15079,7 +15005,6 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -15088,7 +15013,6 @@
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
@@ -15298,7 +15222,6 @@
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.0.tgz",
|
||||
"integrity": "sha512-r15M3+LHKgM4SOapNmsH3smAizWds1vJ0Z9C4mWaKnT9/wD7+d/0jYcj6LmOvonkrO4Rgdyp4KQ/29gWN2i1eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
"set-cookie-parser": "^2.6.0"
|
||||
@@ -16067,7 +15990,6 @@
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -16091,7 +16013,6 @@
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@@ -16099,14 +16020,12 @@
|
||||
"node_modules/send/node_modules/debug/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/send/node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -16115,7 +16034,6 @@
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
@@ -16184,8 +16102,7 @@
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||
"license": "ISC"
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
@@ -17223,7 +17140,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
@@ -17353,8 +17269,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-3.1.0.tgz",
|
||||
"integrity": "sha512-tVI25WEXl4fckNEmrq70xU1XumxUwEx/FZD5AgEcV8ri7Wvrg2o7GEq8U7htrNx3CajciGm+kDyhRf5JB6t7/A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
@@ -17386,7 +17301,6 @@
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
@@ -17614,7 +17528,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -17735,7 +17648,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
@@ -17814,10 +17726,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz",
|
||||
"integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
|
||||
"license": "MIT",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.1.tgz",
|
||||
"integrity": "sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.6",
|
||||
@@ -17997,7 +17908,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz",
|
||||
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"tests/deps/*",
|
||||
"tests/projects/*",
|
||||
@@ -18618,8 +18528,7 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
|
||||
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/zwitch": {
|
||||
"version": "2.0.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "0.53.0",
|
||||
"version": "0.52.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
@@ -20,13 +20,11 @@
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/react-query": "^5.84.2",
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.4.0",
|
||||
"axios": "^1.11.0",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"framer-motion": "^12.23.12",
|
||||
"i18next": "^25.3.2",
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Lock } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ContextMenu } from "./context-menu";
|
||||
import { ContextMenuListItem } from "./context-menu-list-item";
|
||||
import { useClickOutsideElement } from "#/hooks/use-click-outside-element";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { ContextMenuIconText } from "./context-menu-icon-text";
|
||||
|
||||
interface AccountSettingsContextMenuProps {
|
||||
onLogout: () => void;
|
||||
@@ -25,10 +23,7 @@ export function AccountSettingsContextMenu({
|
||||
className="absolute right-full md:left-full -top-1 z-10 w-fit"
|
||||
>
|
||||
<ContextMenuListItem onClick={onLogout} data-testid="logout-button">
|
||||
<ContextMenuIconText
|
||||
icon={Lock}
|
||||
text={t(I18nKey.ACCOUNT_SETTINGS$LOGOUT)}
|
||||
/>
|
||||
{t(I18nKey.ACCOUNT_SETTINGS$LOGOUT)}
|
||||
</ContextMenuListItem>
|
||||
</ContextMenu>
|
||||
);
|
||||
|
||||
@@ -13,7 +13,6 @@ export function ConnectToProviderMessage() {
|
||||
<Link
|
||||
data-testid="navigate-to-settings-button"
|
||||
to="/settings/integrations"
|
||||
className="self-start"
|
||||
>
|
||||
<BrandButton type="button" variant="primary" isDisabled={isLoading}>
|
||||
{!isLoading && t("SETTINGS$TITLE")}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMemo } from "react";
|
||||
import { isBefore } from "date-fns";
|
||||
import { useLocalStorage } from "@uidotdev/usehooks";
|
||||
import { FaTriangleExclamation } from "react-icons/fa6";
|
||||
import CloseIcon from "#/icons/close.svg?react";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
interface MaintenanceBannerProps {
|
||||
startTime: string;
|
||||
@@ -12,11 +7,6 @@ interface MaintenanceBannerProps {
|
||||
|
||||
export function MaintenanceBanner({ startTime }: MaintenanceBannerProps) {
|
||||
const { t } = useTranslation();
|
||||
const [dismissedAt, setDismissedAt] = useLocalStorage<string | null>(
|
||||
"maintenance_banner_dismissed_at",
|
||||
null,
|
||||
);
|
||||
|
||||
// Convert EST timestamp to user's local timezone
|
||||
const formatMaintenanceTime = (estTimeString: string): string => {
|
||||
try {
|
||||
@@ -62,28 +52,8 @@ export function MaintenanceBanner({ startTime }: MaintenanceBannerProps) {
|
||||
|
||||
const localTime = formatMaintenanceTime(startTime);
|
||||
|
||||
const isBannerVisible = useMemo(() => {
|
||||
const isValid = !Number.isNaN(new Date(startTime).getTime());
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
return !dismissedAt
|
||||
? true
|
||||
: isBefore(new Date(dismissedAt), new Date(startTime));
|
||||
}, [dismissedAt, startTime]);
|
||||
|
||||
if (!isBannerVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="maintenance-banner"
|
||||
className={cn(
|
||||
"bg-primary text-[#0D0F11] p-4 rounded",
|
||||
"flex flex-row items-center justify-between",
|
||||
)}
|
||||
>
|
||||
<div className="bg-primary text-[#0D0F11] p-4 rounded">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0">
|
||||
<FaTriangleExclamation className="text-white align-middle" />
|
||||
@@ -94,17 +64,6 @@ export function MaintenanceBanner({ startTime }: MaintenanceBannerProps) {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-testid="dismiss-button"
|
||||
onClick={() => setDismissedAt(localTime)}
|
||||
className={cn(
|
||||
"bg-[#0D0F11] rounded-full w-5 h-5 flex items-center justify-center cursor-pointer",
|
||||
)}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { I18nKey } from "#/i18n/declaration";
|
||||
import { SettingsInput } from "../settings-input";
|
||||
import { BitbucketTokenHelpAnchor } from "./bitbucket-token-help-anchor";
|
||||
import { KeyStatusIcon } from "../key-status-icon";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
interface BitbucketTokenInputProps {
|
||||
onChange: (value: string) => void;
|
||||
@@ -11,7 +10,6 @@ interface BitbucketTokenInputProps {
|
||||
isBitbucketTokenSet: boolean;
|
||||
name: string;
|
||||
bitbucketHostSet: string | null | undefined;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function BitbucketTokenInput({
|
||||
@@ -20,12 +18,11 @@ export function BitbucketTokenInput({
|
||||
isBitbucketTokenSet,
|
||||
name,
|
||||
bitbucketHostSet,
|
||||
className,
|
||||
}: BitbucketTokenInputProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)}>
|
||||
<div className="flex flex-col gap-6">
|
||||
<SettingsInput
|
||||
testId={name}
|
||||
name={name}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { I18nKey } from "#/i18n/declaration";
|
||||
import { SettingsInput } from "../settings-input";
|
||||
import { GitHubTokenHelpAnchor } from "./github-token-help-anchor";
|
||||
import { KeyStatusIcon } from "../key-status-icon";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
interface GitHubTokenInputProps {
|
||||
onChange: (value: string) => void;
|
||||
@@ -11,7 +10,6 @@ interface GitHubTokenInputProps {
|
||||
isGitHubTokenSet: boolean;
|
||||
name: string;
|
||||
githubHostSet: string | null | undefined;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function GitHubTokenInput({
|
||||
@@ -20,12 +18,11 @@ export function GitHubTokenInput({
|
||||
isGitHubTokenSet,
|
||||
name,
|
||||
githubHostSet,
|
||||
className,
|
||||
}: GitHubTokenInputProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)}>
|
||||
<div className="flex flex-col gap-6">
|
||||
<SettingsInput
|
||||
testId={name}
|
||||
name={name}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { I18nKey } from "#/i18n/declaration";
|
||||
import { SettingsInput } from "../settings-input";
|
||||
import { GitLabTokenHelpAnchor } from "./gitlab-token-help-anchor";
|
||||
import { KeyStatusIcon } from "../key-status-icon";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
interface GitLabTokenInputProps {
|
||||
onChange: (value: string) => void;
|
||||
@@ -11,7 +10,6 @@ interface GitLabTokenInputProps {
|
||||
isGitLabTokenSet: boolean;
|
||||
name: string;
|
||||
gitlabHostSet: string | null | undefined;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function GitLabTokenInput({
|
||||
@@ -20,12 +18,11 @@ export function GitLabTokenInput({
|
||||
isGitLabTokenSet,
|
||||
name,
|
||||
gitlabHostSet,
|
||||
className,
|
||||
}: GitLabTokenInputProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)}>
|
||||
<div className="flex flex-col gap-6">
|
||||
<SettingsInput
|
||||
testId={name}
|
||||
name={name}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { MCPConfig } from "#/types/settings";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
@@ -11,11 +11,6 @@ interface MCPJsonEditorProps {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const MCP_DEFAULT_CONFIG: MCPConfig = {
|
||||
sse_servers: [],
|
||||
stdio_servers: [],
|
||||
};
|
||||
|
||||
export function MCPJsonEditor({
|
||||
mcpConfig,
|
||||
onChange,
|
||||
@@ -25,17 +20,10 @@ export function MCPJsonEditor({
|
||||
const [configText, setConfigText] = useState(() =>
|
||||
mcpConfig
|
||||
? JSON.stringify(mcpConfig, null, 2)
|
||||
: JSON.stringify(MCP_DEFAULT_CONFIG, null, 2),
|
||||
: t(I18nKey.SETTINGS$MCP_DEFAULT_CONFIG),
|
||||
);
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
textareaRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setConfigText(e.target.value);
|
||||
};
|
||||
@@ -101,7 +89,6 @@ export function MCPJsonEditor({
|
||||
/>
|
||||
</p>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className={cn(
|
||||
"w-full h-64 resize-y p-2 rounded-sm text-sm font-mono",
|
||||
"bg-tertiary border border-[#717888]",
|
||||
@@ -131,7 +118,7 @@ export function MCPJsonEditor({
|
||||
{t(I18nKey.BUTTON$CANCEL)}
|
||||
</BrandButton>
|
||||
<BrandButton type="button" variant="primary" onClick={handleSave}>
|
||||
{t(I18nKey.SETTINGS$MCP_PREVIEW_CHANGES)}
|
||||
{t(I18nKey.SETTINGS$MCP_CONFIRM_CHANGES)}
|
||||
</BrandButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -100,17 +100,6 @@ export function ConfigureModal({
|
||||
}
|
||||
}, [isOpen, existingWorkspace, isWorkspaceEditable]);
|
||||
|
||||
// Helper function to get platform-specific placeholder
|
||||
const getWorkspacePlaceholder = () => {
|
||||
if (platform === "jira") {
|
||||
return I18nKey.PROJECT_MANAGEMENT$JIRA_WORKSPACE_NAME_PLACEHOLDER;
|
||||
}
|
||||
if (platform === "jira-dc") {
|
||||
return I18nKey.PROJECT_MANAGEMENT$JIRA_DC_WORKSPACE_NAME_PLACEHOLDER;
|
||||
}
|
||||
return I18nKey.PROJECT_MANAGEMENT$LINEAR_WORKSPACE_NAME_PLACEHOLDER;
|
||||
};
|
||||
|
||||
// Validation states
|
||||
const [workspaceError, setWorkspaceError] = useState<string | null>(null);
|
||||
const [webhookSecretError, setWebhookSecretError] = useState<string | null>(
|
||||
@@ -279,11 +268,8 @@ export function ConfigureModal({
|
||||
<BaseModalDescription>
|
||||
{showConfigurationFields ? (
|
||||
<Trans
|
||||
i18nKey={
|
||||
I18nKey.PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION_STAGE_2
|
||||
}
|
||||
i18nKey={I18nKey.PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION}
|
||||
components={{
|
||||
b: <b />,
|
||||
a: (
|
||||
<a
|
||||
href="https://docs.all-hands.dev/usage/cloud/openhands-cloud"
|
||||
@@ -294,41 +280,41 @@ export function ConfigureModal({
|
||||
Check the document for more information
|
||||
</a>
|
||||
),
|
||||
b: <b />,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Trans
|
||||
i18nKey={
|
||||
I18nKey.PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION_STAGE_1
|
||||
}
|
||||
components={{
|
||||
b: <b />,
|
||||
a: (
|
||||
<a
|
||||
href="https://docs.all-hands.dev/usage/cloud/openhands-cloud"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-500 underline"
|
||||
>
|
||||
Check the document for more information
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<p className="mt-4">
|
||||
<Trans
|
||||
i18nKey={
|
||||
I18nKey.PROJECT_MANAGEMENT$IMPORTANT_WORKSPACE_INTEGRATION
|
||||
}
|
||||
components={{
|
||||
b: <b />,
|
||||
a: (
|
||||
<a
|
||||
href="https://docs.all-hands.dev/usage/cloud/openhands-cloud"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-500 underline"
|
||||
>
|
||||
Check the document for more information
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
<p className="mt-4">
|
||||
{t(I18nKey.PROJECT_MANAGEMENT$WORKSPACE_NAME_HINT, {
|
||||
platform: platformName,
|
||||
})}
|
||||
</p>
|
||||
</BaseModalDescription>
|
||||
<div className="w-full flex flex-col gap-4 mt-1">
|
||||
<div className="w-full flex flex-col gap-4 mt-4">
|
||||
<div>
|
||||
<div className="flex gap-2 items-end">
|
||||
<div className="flex-1">
|
||||
<SettingsInput
|
||||
label={t(I18nKey.PROJECT_MANAGEMENT$WORKSPACE_NAME_LABEL)}
|
||||
placeholder={t(getWorkspacePlaceholder())}
|
||||
placeholder={t(
|
||||
I18nKey.PROJECT_MANAGEMENT$WORKSPACE_NAME_PLACEHOLDER,
|
||||
)}
|
||||
value={workspace}
|
||||
onChange={handleWorkspaceChange}
|
||||
className="w-full"
|
||||
@@ -432,7 +418,7 @@ export function ConfigureModal({
|
||||
>
|
||||
{(() => {
|
||||
if (existingWorkspace && showConfigurationFields) {
|
||||
return t(I18nKey.PROJECT_MANAGEMENT$UPDATE_BUTTON_LABEL);
|
||||
return t(I18nKey.PROJECT_MANAGEMENT$EDIT_BUTTON_LABEL);
|
||||
}
|
||||
return t(I18nKey.PROJECT_MANAGEMENT$CONNECT_BUTTON_LABEL);
|
||||
})()}
|
||||
|
||||
@@ -22,5 +22,5 @@ export function useDocumentTitleFromState(suffix = "OpenHands") {
|
||||
return () => {
|
||||
document.title = suffix;
|
||||
};
|
||||
}, [conversation?.title, suffix]);
|
||||
}, [conversation, suffix]);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export enum I18nKey {
|
||||
SETTINGS$NAV_MCP = "SETTINGS$NAV_MCP",
|
||||
SETTINGS$MCP_CONFIGURATION = "SETTINGS$MCP_CONFIGURATION",
|
||||
SETTINGS$MCP_EDIT_CONFIGURATION = "SETTINGS$MCP_EDIT_CONFIGURATION",
|
||||
SETTINGS$MCP_PREVIEW_CHANGES = "SETTINGS$MCP_PREVIEW_CHANGES",
|
||||
SETTINGS$MCP_CONFIRM_CHANGES = "SETTINGS$MCP_CONFIRM_CHANGES",
|
||||
SETTINGS$MCP_CONFIG_DESCRIPTION = "SETTINGS$MCP_CONFIG_DESCRIPTION",
|
||||
SETTINGS$MCP_CONFIG_ERROR = "SETTINGS$MCP_CONFIG_ERROR",
|
||||
SETTINGS$MCP_CONFIG_EXAMPLE = "SETTINGS$MCP_CONFIG_EXAMPLE",
|
||||
@@ -71,6 +71,7 @@ export enum I18nKey {
|
||||
SETTINGS$MCP_ERROR_SSE_URL = "SETTINGS$MCP_ERROR_SSE_URL",
|
||||
SETTINGS$MCP_ERROR_STDIO_PROPS = "SETTINGS$MCP_ERROR_STDIO_PROPS",
|
||||
SETTINGS$MCP_ERROR_INVALID_JSON = "SETTINGS$MCP_ERROR_INVALID_JSON",
|
||||
SETTINGS$MCP_DEFAULT_CONFIG = "SETTINGS$MCP_DEFAULT_CONFIG",
|
||||
HOME$CONNECT_PROVIDER_MESSAGE = "HOME$CONNECT_PROVIDER_MESSAGE",
|
||||
HOME$LETS_START_BUILDING = "HOME$LETS_START_BUILDING",
|
||||
HOME$OPENHANDS_DESCRIPTION = "HOME$OPENHANDS_DESCRIPTION",
|
||||
@@ -749,15 +750,8 @@ export enum I18nKey {
|
||||
PROJECT_MANAGEMENT$LINK_CONFIRMATION_TITLE = "PROJECT_MANAGEMENT$LINK_CONFIRMATION_TITLE",
|
||||
PROJECT_MANAGEMENT$CONFIGURE_BUTTON_LABEL = "PROJECT_MANAGEMENT$CONFIGURE_BUTTON_LABEL",
|
||||
PROJECT_MANAGEMENT$EDIT_BUTTON_LABEL = "PROJECT_MANAGEMENT$EDIT_BUTTON_LABEL",
|
||||
PROJECT_MANAGEMENT$UPDATE_BUTTON_LABEL = "PROJECT_MANAGEMENT$UPDATE_BUTTON_LABEL",
|
||||
PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE = "PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE",
|
||||
PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION_STAGE_1 = "PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION_STAGE_1",
|
||||
PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION_STAGE_2 = "PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION_STAGE_2",
|
||||
PROJECT_MANAGEMENT$WORKSPACE_NAME_HINT = "PROJECT_MANAGEMENT$WORKSPACE_NAME_HINT",
|
||||
PROJECT_MANAGEMENT$WORKSPACE_NAME_LABEL = "PROJECT_MANAGEMENT$WORKSPACE_NAME_LABEL",
|
||||
PROJECT_MANAGEMENT$JIRA_WORKSPACE_NAME_PLACEHOLDER = "PROJECT_MANAGEMENT$JIRA_WORKSPACE_NAME_PLACEHOLDER",
|
||||
PROJECT_MANAGEMENT$JIRA_DC_WORKSPACE_NAME_PLACEHOLDER = "PROJECT_MANAGEMENT$JIRA_DC_WORKSPACE_NAME_PLACEHOLDER",
|
||||
PROJECT_MANAGEMENT$LINEAR_WORKSPACE_NAME_PLACEHOLDER = "PROJECT_MANAGEMENT$LINEAR_WORKSPACE_NAME_PLACEHOLDER",
|
||||
PROJECT_MANAGEMENT$WORKSPACE_NAME_PLACEHOLDER = "PROJECT_MANAGEMENT$WORKSPACE_NAME_PLACEHOLDER",
|
||||
PROJECT_MANAGEMENT$WEBHOOK_SECRET_LABEL = "PROJECT_MANAGEMENT$WEBHOOK_SECRET_LABEL",
|
||||
PROJECT_MANAGEMENT$WEBHOOK_SECRET_PLACEHOLDER = "PROJECT_MANAGEMENT$WEBHOOK_SECRET_PLACEHOLDER",
|
||||
PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_LABEL = "PROJECT_MANAGEMENT$SERVICE_ACCOUNT_EMAIL_LABEL",
|
||||
@@ -766,6 +760,9 @@ export enum I18nKey {
|
||||
PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_PLACEHOLDER = "PROJECT_MANAGEMENT$SERVICE_ACCOUNT_API_PLACEHOLDER",
|
||||
PROJECT_MANAGEMENT$CONNECT_BUTTON_LABEL = "PROJECT_MANAGEMENT$CONNECT_BUTTON_LABEL",
|
||||
PROJECT_MANAGEMENT$ACTIVE_TOGGLE_LABEL = "PROJECT_MANAGEMENT$ACTIVE_TOGGLE_LABEL",
|
||||
PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION = "PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION",
|
||||
PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE = "PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE",
|
||||
PROJECT_MANAGEMENT$IMPORTANT_WORKSPACE_INTEGRATION = "PROJECT_MANAGEMENT$IMPORTANT_WORKSPACE_INTEGRATION",
|
||||
PROJECT_MANAGEMENT$WORKSPACE_NAME_VALIDATION_ERROR = "PROJECT_MANAGEMENT$WORKSPACE_NAME_VALIDATION_ERROR",
|
||||
PROJECT_MANAGEMENT$WEBHOOK_SECRET_NAME_VALIDATION_ERROR = "PROJECT_MANAGEMENT$WEBHOOK_SECRET_NAME_VALIDATION_ERROR",
|
||||
PROJECT_MANAGEMENT$SVC_ACC_EMAIL_VALIDATION_ERROR = "PROJECT_MANAGEMENT$SVC_ACC_EMAIL_VALIDATION_ERROR",
|
||||
|
||||
@@ -27,15 +27,7 @@ i18n
|
||||
.init({
|
||||
fallbackLng: "en",
|
||||
debug: import.meta.env.NODE_ENV === "development",
|
||||
|
||||
// Define supported languages explicitly to prevent 404 errors
|
||||
// According to i18next documentation, this is the recommended way to prevent
|
||||
// 404 requests for unsupported language codes like 'en-US@posix'
|
||||
supportedLngs: AvailableLanguages.map((lang) => lang.value),
|
||||
|
||||
// Do NOT set nonExplicitSupportedLngs: true as it causes 404 errors
|
||||
// for region-specific codes not in supportedLngs (per i18next developer)
|
||||
nonExplicitSupportedLngs: false,
|
||||
load: "currentOnly",
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -815,21 +815,21 @@
|
||||
"de": "Konfiguration bearbeiten",
|
||||
"uk": "Редагувати налаштування"
|
||||
},
|
||||
"SETTINGS$MCP_PREVIEW_CHANGES": {
|
||||
"en": "Preview Changes",
|
||||
"ja": "変更内容をプレビュー",
|
||||
"zh-CN": "预览更改",
|
||||
"zh-TW": "預覽變更",
|
||||
"ko-KR": "변경 사항 미리보기",
|
||||
"no": "Forhåndsvis endringer",
|
||||
"it": "Anteprima modifiche",
|
||||
"pt": "Visualizar alterações",
|
||||
"es": "Vista previa de los cambios",
|
||||
"ar": "معاينة التغييرات",
|
||||
"fr": "Aperçu des modifications",
|
||||
"tr": "Değişiklikleri Önizle",
|
||||
"de": "Änderungen anzeigen",
|
||||
"uk": "Переглянути зміни"
|
||||
"SETTINGS$MCP_CONFIRM_CHANGES": {
|
||||
"en": "Confirm Changes",
|
||||
"ja": "変更を確定",
|
||||
"zh-CN": "确认更改",
|
||||
"zh-TW": "確認變更",
|
||||
"ko-KR": "변경 사항 확인",
|
||||
"no": "Bekreft endringer",
|
||||
"it": "Conferma modifiche",
|
||||
"pt": "Confirmar alterações",
|
||||
"es": "Confirmar cambios",
|
||||
"ar": "تأكيد التغييرات",
|
||||
"fr": "Confirmer les modifications",
|
||||
"tr": "Değişiklikleri Onayla",
|
||||
"de": "Änderungen bestätigen",
|
||||
"uk": "Підтвердити зміни"
|
||||
},
|
||||
"SETTINGS$MCP_CONFIG_DESCRIPTION": {
|
||||
"en": "Edit the JSON configuration for MCP servers below. The configuration must include both sse_servers and stdio_servers arrays. For full configuration details and integration examples, see the <a>documentation</a>.",
|
||||
@@ -1135,6 +1135,22 @@
|
||||
"de": "Ungültiges JSON",
|
||||
"uk": "Невірний JSON"
|
||||
},
|
||||
"SETTINGS$MCP_DEFAULT_CONFIG": {
|
||||
"en": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"ja": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"zh-CN": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"zh-TW": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"ko-KR": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"no": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"it": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"pt": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"es": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"ar": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"fr": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"tr": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"de": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}",
|
||||
"uk": "{\n \"sse_servers\": [],\n \"stdio_servers\": []\n}"
|
||||
},
|
||||
"HOME$CONNECT_PROVIDER_MESSAGE": {
|
||||
"en": "To get started with suggested tasks, please connect your GitHub, GitLab, or Bitbucket account.",
|
||||
"ja": "提案されたタスクを始めるには、GitHub、GitLab、またはBitbucketアカウントを接続してください。",
|
||||
@@ -11983,86 +11999,6 @@
|
||||
"de": "Bearbeiten",
|
||||
"uk": "Редагувати"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$UPDATE_BUTTON_LABEL": {
|
||||
"en": "Update",
|
||||
"ja": "更新",
|
||||
"zh-CN": "更新",
|
||||
"zh-TW": "更新",
|
||||
"ko-KR": "업데이트",
|
||||
"no": "Oppdater",
|
||||
"it": "Aggiorna",
|
||||
"pt": "Atualizar",
|
||||
"es": "Actualizar",
|
||||
"ar": "تحديث",
|
||||
"fr": "Mettre à jour",
|
||||
"tr": "Güncelle",
|
||||
"de": "Aktualisieren",
|
||||
"uk": "Оновити"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE": {
|
||||
"en": "Configure {{platform}} Integration",
|
||||
"ja": "{{platform}}統合を設定",
|
||||
"zh-CN": "配置{{platform}}集成",
|
||||
"zh-TW": "配置{{platform}}集成",
|
||||
"ko-KR": "{{platform}} 통합 구성",
|
||||
"no": "Konfigurer {{platform}}-integrasjon",
|
||||
"it": "Configura integrazione {{platform}}",
|
||||
"pt": "Configurar integração {{platform}}",
|
||||
"es": "Configurar integración de {{platform}}",
|
||||
"ar": "تكوين تكامل {{platform}}",
|
||||
"fr": "Configurer l'intégration {{platform}}",
|
||||
"tr": "{{platform}} Entegrasyonunu Yapılandır",
|
||||
"de": "{{platform}}-Integration konfigurieren",
|
||||
"uk": "Налаштувати інтеграцію {{platform}}"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION_STAGE_1": {
|
||||
"en": "<b>Important: </b>Make sure the workspace integration for your target workspace is already configured. Check the <a>documentation</a> for more information.",
|
||||
"ja": "<b>重要: </b>対象のワークスペースのワークスペース統合がすでに設定されていることを確認してください。詳しくは<a>ドキュメント</a>をご覧ください。",
|
||||
"zh-CN": "<b>重要提示:</b>请确保目标工作区的工作区集成已配置完毕。查看<a>文档</a>了解更多信息。",
|
||||
"zh-TW": "<b>重要提示:</b>請確保目標工作區的工作區整合已設定完成。查看<a>文件</a>以了解更多資訊。",
|
||||
"ko-KR": "<b>중요:</b>대상 작업 공간에 대한 작업 공간 통합이 이미 구성되어 있는지 확인하세요. 자세한 내용은 <a>설명서</a>를 참조하세요.",
|
||||
"no": "<b>Viktig:</b>Sørg for at arbeidsområdeintegrasjonen for målarbeidsområdet ditt allerede er konfigurert. Se <a>dokumentasjonen</a> for mer informasjon.",
|
||||
"it": "<b>Importante: </b>Assicurati che l'integrazione dell'area di lavoro per l'area di lavoro di destinazione sia già configurata. Consulta la <a>documentazione</a> per ulteriori informazioni.",
|
||||
"pt": "<b>Importante:</b>Certifique-se de que a integração do workspace de destino já esteja configurada. Consulte a <a>documentação</a> para obter mais informações.",
|
||||
"es": "Importante: Asegúrate de que la integración del espacio de trabajo de destino ya esté configurada. Consulta la documentación para obtener más información.",
|
||||
"ar": "<b>هام: </b>تأكد من إعداد تكامل مساحة العمل لمساحة العمل المستهدفة. <a>راجع المستند لمزيد من المعلومات</a>.",
|
||||
"fr": "<b>Important :</b>Assurez-vous que l'intégration de l'espace de travail cible est déjà configurée. Consultez la <a>documentation</a> pour plus d'informations.",
|
||||
"tr": "<b>Önemli: </b>Hedef çalışma alanınız için çalışma alanı entegrasyonunun zaten yapılandırılmış olduğundan emin olun. Daha fazla bilgi için <a>belgelere</a> bakın.",
|
||||
"de": "<b>Wichtig:</b>Stellen Sie sicher, dass die Arbeitsbereichsintegration für Ihren Zielarbeitsbereich bereits konfiguriert ist. Weitere Informationen finden Sie in der <a>Dokumentation</a>.",
|
||||
"uk": "<b>Важливо: </b>Переконайтеся, що інтеграцію робочого простору для вашого цільового робочого простору вже налаштовано. Перегляньте <a>документацію</a> для отримання додаткової інформації."
|
||||
},
|
||||
"PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION_STAGE_2": {
|
||||
"en": "<b>Important:</b> Check the <a>documentation</a> for more information about configuring the workspace integration or updating an existing integration.",
|
||||
"ja": "<b>重要:</b> ワークスペース統合の設定や既存の統合の更新について詳しくは<a>ドキュメント</a>をご確認ください。",
|
||||
"zh-CN": "<b>重要提示:</b>有关配置工作区集成或更新现有集成的更多信息,请查看<a>文档</a>。",
|
||||
"zh-TW": "<b>重要提示:</b>有關配置工作區整合或更新現有整合的更多資訊,請查看<a>文件</a>。",
|
||||
"ko-KR": "<b>중요:</b> 작업공간 통합 구성이나 기존 통합 업데이트에 대한 자세한 내용은 <a>설명서</a>를 확인하세요.",
|
||||
"no": "<b>Viktig:</b> Se <a>dokumentasjonen</a> for mer informasjon om konfigurering av arbeidsområdeintegrasjon eller oppdatering av eksisterende integrasjon.",
|
||||
"it": "<b>Importante:</b> Consulta la <a>documentazione</a> per ulteriori informazioni sulla configurazione dell'integrazione dell'area di lavoro o sull'aggiornamento di un'integrazione esistente.",
|
||||
"pt": "<b>Importante:</b> Consulte a <a>documentação</a> para obter mais informações sobre como configurar a integração do workspace ou atualizar uma integração existente.",
|
||||
"es": "<b>Importante:</b> Consulte la <a>documentación</a> para obtener más información sobre la configuración de la integración del espacio de trabajo o la actualización de una integración existente.",
|
||||
"ar": "<b>هام:</b> راجع <a>الوثائق</a> لمزيد من المعلومات حول تكوين تكامل مساحة العمل أو تحديث تكامل موجود.",
|
||||
"fr": "<b>Important :</b> Consultez la <a>documentation</a> pour plus d'informations sur la configuration de l'intégration de l'espace de travail ou la mise à jour d'une intégration existante.",
|
||||
"tr": "<b>Önemli:</b> Çalışma alanı entegrasyonunu yapılandırma veya mevcut bir entegrasyonu güncelleme hakkında daha fazla bilgi için <a>belgelere</a> bakın.",
|
||||
"de": "<b>Wichtig:</b> Weitere Informationen zur Konfiguration der Arbeitsbereichsintegration oder zur Aktualisierung einer bestehenden Integration finden Sie in der <a>Dokumentation</a>.",
|
||||
"uk": "<b>Важливо:</b> Перегляньте <a>документацію</a> для отримання додаткової інформації про налаштування інтеграції робочого простору або оновлення існуючої інтеграції."
|
||||
},
|
||||
"PROJECT_MANAGEMENT$WORKSPACE_NAME_HINT": {
|
||||
"en": "Workspace name can be found in the browser URL when you're accessing a resource (eg: issue) in {{platform}}.",
|
||||
"ja": "ワークスペース名は、{{platform}}のリソース(例:イシュー)にアクセスする際のブラウザURLで確認できます。",
|
||||
"zh-CN": "工作区名称可以在访问{{platform}}资源(如:问题)时的浏览器URL中找到。",
|
||||
"zh-TW": "工作區名稱可以在存取{{platform}}資源(如:問題)時的瀏覽器URL中找到。",
|
||||
"ko-KR": "작업공간 이름은 {{platform}}의 리소스(예: 이슈)에 액세스할 때 브라우저 URL에서 찾을 수 있습니다.",
|
||||
"no": "Arbeidsområdenavn kan finnes i nettleser-URL-en når du får tilgang til en ressurs (f.eks: sak) i {{platform}}.",
|
||||
"it": "Il nome dell'area di lavoro può essere trovato nell'URL del browser quando stai accedendo a una risorsa (es: issue) in {{platform}}.",
|
||||
"pt": "O nome do workspace pode ser encontrado na URL do navegador quando você está acessando um recurso (ex: issue) em {{platform}}.",
|
||||
"es": "El nombre del espacio de trabajo se puede encontrar en la URL del navegador cuando accedes a un recurso (ej: issue) en {{platform}}.",
|
||||
"ar": "يمكن العثور على اسم مساحة العمل في عنوان URL للمتصفح عند الوصول إلى مورد (مثل: مشكلة) في {{platform}}.",
|
||||
"fr": "Le nom de l'espace de travail peut être trouvé dans l'URL du navigateur lorsque vous accédez à une ressource (ex : issue) dans {{platform}}.",
|
||||
"tr": "Çalışma alanı adı, {{platform}}'da bir kaynağa (örn: sorun) erişirken tarayıcı URL'sinde bulunabilir.",
|
||||
"de": "Der Arbeitsbereichsname ist in der Browser-URL zu finden, wenn Sie auf eine Ressource (z.B.: Issue) in {{platform}} zugreifen.",
|
||||
"uk": "Назву робочого простору можна знайти в URL браузера під час доступу до ресурсу (наприклад: проблема) в {{platform}}."
|
||||
},
|
||||
"PROJECT_MANAGEMENT$WORKSPACE_NAME_LABEL": {
|
||||
"en": "Workspace Name",
|
||||
"ja": "ワークスペース名",
|
||||
@@ -12079,53 +12015,21 @@
|
||||
"de": "Arbeitsbereichsname",
|
||||
"uk": "Назва робочої області"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$JIRA_WORKSPACE_NAME_PLACEHOLDER": {
|
||||
"en": "yourcompany.atlassian.net",
|
||||
"ja": "yourcompany.atlassian.net",
|
||||
"zh-CN": "yourcompany.atlassian.net",
|
||||
"zh-TW": "yourcompany.atlassian.net",
|
||||
"ko-KR": "yourcompany.atlassian.net",
|
||||
"no": "yourcompany.atlassian.net",
|
||||
"it": "yourcompany.atlassian.net",
|
||||
"pt": "yourcompany.atlassian.net",
|
||||
"es": "yourcompany.atlassian.net",
|
||||
"ar": "yourcompany.atlassian.net",
|
||||
"fr": "yourcompany.atlassian.net",
|
||||
"tr": "yourcompany.atlassian.net",
|
||||
"de": "yourcompany.atlassian.net",
|
||||
"uk": "yourcompany.atlassian.net"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$JIRA_DC_WORKSPACE_NAME_PLACEHOLDER": {
|
||||
"en": "jira.yourcompany.com",
|
||||
"ja": "jira.yourcompany.com",
|
||||
"zh-CN": "jira.yourcompany.com",
|
||||
"zh-TW": "jira.yourcompany.com",
|
||||
"ko-KR": "jira.yourcompany.com",
|
||||
"no": "jira.yourcompany.com",
|
||||
"it": "jira.yourcompany.com",
|
||||
"pt": "jira.yourcompany.com",
|
||||
"es": "jira.yourcompany.com",
|
||||
"ar": "jira.yourcompany.com",
|
||||
"fr": "jira.yourcompany.com",
|
||||
"tr": "jira.yourcompany.com",
|
||||
"de": "jira.yourcompany.com",
|
||||
"uk": "jira.yourcompany.com"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$LINEAR_WORKSPACE_NAME_PLACEHOLDER": {
|
||||
"en": "yourcompany",
|
||||
"ja": "yourcompany",
|
||||
"zh-CN": "yourcompany",
|
||||
"zh-TW": "yourcompany",
|
||||
"ko-KR": "yourcompany",
|
||||
"no": "yourcompany",
|
||||
"it": "yourcompany",
|
||||
"pt": "yourcompany",
|
||||
"es": "yourcompany",
|
||||
"ar": "yourcompany",
|
||||
"fr": "yourcompany",
|
||||
"tr": "yourcompany",
|
||||
"de": "yourcompany",
|
||||
"uk": "yourcompany"
|
||||
"PROJECT_MANAGEMENT$WORKSPACE_NAME_PLACEHOLDER": {
|
||||
"en": "myworkspace",
|
||||
"ja": "私のワークスペース",
|
||||
"zh-CN": "我的工作空间",
|
||||
"zh-TW": "我的工作區",
|
||||
"ko-KR": "내워크스페이스",
|
||||
"no": "mittarbeidsområde",
|
||||
"it": "mioworkspace",
|
||||
"pt": "meuworkspace",
|
||||
"es": "miespaciodetrabajo",
|
||||
"ar": "مساحةعملي",
|
||||
"fr": "monworkspace",
|
||||
"tr": "benimworkspace",
|
||||
"de": "meinarbeitsbereich",
|
||||
"uk": "моя-робоча-область"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$WEBHOOK_SECRET_LABEL": {
|
||||
"en": "Webhook Secret",
|
||||
@@ -12255,6 +12159,54 @@
|
||||
"de": "Aktiv",
|
||||
"uk": "Активний"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$CONFIGURE_MODAL_DESCRIPTION": {
|
||||
"en": "<b>Important:</b> Check the <a>documentation</a> for more information about configuring the workspace integration or updating an existing integration.",
|
||||
"ja": "<b>重要:</b> ワークスペース統合の設定や既存の統合の更新について詳しくは<a>ドキュメント</a>をご確認ください。",
|
||||
"zh-CN": "<b>重要提示:</b>有关配置工作区集成或更新现有集成的更多信息,请查看<a>文档</a>。",
|
||||
"zh-TW": "<b>重要提示:</b>有關配置工作區整合或更新現有整合的更多資訊,請查看<a>文件</a>。",
|
||||
"ko-KR": "<b>중요:</b> 작업공간 통합 구성이나 기존 통합 업데이트에 대한 자세한 내용은 <a>설명서</a>를 확인하세요.",
|
||||
"no": "<b>Viktig:</b> Se <a>dokumentasjonen</a> for mer informasjon om konfigurering av arbeidsområdeintegrasjon eller oppdatering av eksisterende integrasjon.",
|
||||
"it": "<b>Importante:</b> Consulta la <a>documentazione</a> per ulteriori informazioni sulla configurazione dell'integrazione dell'area di lavoro o sull'aggiornamento di un'integrazione esistente.",
|
||||
"pt": "<b>Importante:</b> Consulte a <a>documentação</a> para obter mais informações sobre como configurar a integração do workspace ou atualizar uma integração existente.",
|
||||
"es": "<b>Importante:</b> Consulte la <a>documentación</a> para obtener más información sobre la configuración de la integración del espacio de trabajo o la actualización de una integración existente.",
|
||||
"ar": "<b>هام:</b> راجع <a>الوثائق</a> لمزيد من المعلومات حول تكوين تكامل مساحة العمل أو تحديث تكامل موجود.",
|
||||
"fr": "<b>Important :</b> Consultez la <a>documentation</a> pour plus d'informations sur la configuration de l'intégration de l'espace de travail ou la mise à jour d'une intégration existante.",
|
||||
"tr": "<b>Önemli:</b> Çalışma alanı entegrasyonunu yapılandırma veya mevcut bir entegrasyonu güncelleme hakkında daha fazla bilgi için <a>belgelere</a> bakın.",
|
||||
"de": "<b>Wichtig:</b> Weitere Informationen zur Konfiguration der Arbeitsbereichsintegration oder zur Aktualisierung einer bestehenden Integration finden Sie in der <a>Dokumentation</a>.",
|
||||
"uk": "<b>Важливо:</b> Перегляньте <a>документацію</a> для отримання додаткової інформації про налаштування інтеграції робочого простору або оновлення існуючої інтеграції."
|
||||
},
|
||||
"PROJECT_MANAGEMENT$CONFIGURE_MODAL_TITLE": {
|
||||
"en": "Configure {{platform}} Integration",
|
||||
"ja": "{{platform}}統合を設定",
|
||||
"zh-CN": "配置{{platform}}集成",
|
||||
"zh-TW": "配置{{platform}}集成",
|
||||
"ko-KR": "{{platform}} 통합 구성",
|
||||
"no": "Konfigurer {{platform}}-integrasjon",
|
||||
"it": "Configura integrazione {{platform}}",
|
||||
"pt": "Configurar integração {{platform}}",
|
||||
"es": "Configurar integración de {{platform}}",
|
||||
"ar": "تكوين تكامل {{platform}}",
|
||||
"fr": "Configurer l'intégration {{platform}}",
|
||||
"tr": "{{platform}} Entegrasyonunu Yapılandır",
|
||||
"de": "{{platform}}-Integration konfigurieren",
|
||||
"uk": "Налаштувати інтеграцію {{platform}}"
|
||||
},
|
||||
"PROJECT_MANAGEMENT$IMPORTANT_WORKSPACE_INTEGRATION": {
|
||||
"en": "<b>Important: </b>Make sure the workspace integration for your target workspace is already configured. Check the <a>documentation</a> for more information.",
|
||||
"ja": "<b>重要: </b>対象のワークスペースのワークスペース統合がすでに設定されていることを確認してください。詳しくは<a>ドキュメント</a>をご覧ください。",
|
||||
"zh-CN": "<b>重要提示:</b>请确保目标工作区的工作区集成已配置完毕。查看<a>文档</a>了解更多信息。",
|
||||
"zh-TW": "<b>重要提示:</b>請確保目標工作區的工作區整合已設定完成。查看<a>文件</a>以了解更多資訊。",
|
||||
"ko-KR": "<b>중요:</b>대상 작업 공간에 대한 작업 공간 통합이 이미 구성되어 있는지 확인하세요. 자세한 내용은 <a>설명서</a>를 참조하세요.",
|
||||
"no": "<b>Viktig:</b>Sørg for at arbeidsområdeintegrasjonen for målarbeidsområdet ditt allerede er konfigurert. Se <a>dokumentasjonen</a> for mer informasjon.",
|
||||
"it": "<b>Importante: </b>Assicurati che l'integrazione dell'area di lavoro per l'area di lavoro di destinazione sia già configurata. Consulta la <a>documentazione</a> per ulteriori informazioni.",
|
||||
"pt": "<b>Importante:</b>Certifique-se de que a integração do workspace de destino já esteja configurada. Consulte a <a>documentação</a> para obter mais informações.",
|
||||
"es": "Importante: Asegúrate de que la integración del espacio de trabajo de destino ya esté configurada. Consulta la documentación para obtener más información.",
|
||||
"ar": "<b>هام: </b>تأكد من إعداد تكامل مساحة العمل لمساحة العمل المستهدفة. <a>راجع المستند لمزيد من المعلومات</a>.",
|
||||
"fr": "<b>Important :</b>Assurez-vous que l'intégration de l'espace de travail cible est déjà configurée. Consultez la <a>documentation</a> pour plus d'informations.",
|
||||
"tr": "<b>Önemli: </b>Hedef çalışma alanınız için çalışma alanı entegrasyonunun zaten yapılandırılmış olduğundan emin olun. Daha fazla bilgi için <a>belgelere</a> bakın.",
|
||||
"de": "<b>Wichtig:</b>Stellen Sie sicher, dass die Arbeitsbereichsintegration für Ihren Zielarbeitsbereich bereits konfiguriert ist. Weitere Informationen finden Sie in der <a>Dokumentation</a>.",
|
||||
"uk": "<b>Важливо: </b>Переконайтеся, що інтеграцію робочого простору для вашого цільового робочого простору вже налаштовано. Перегляньте <a>документацію</a> для отримання додаткової інформації."
|
||||
},
|
||||
"PROJECT_MANAGEMENT$WORKSPACE_NAME_VALIDATION_ERROR": {
|
||||
"en": "Workspace name can only contain letters, numbers, hyphens, and underscores",
|
||||
"ja": "ワークスペース名は文字、数字、ハイフン、アンダースコアのみ使用できます",
|
||||
|
||||
@@ -249,11 +249,11 @@ function AppSettingsScreen() {
|
||||
className="w-full max-w-[680px]" // Match the width of the language field
|
||||
/>
|
||||
|
||||
<div className="border-t border-t-tertiary pt-6 mt-2">
|
||||
<div className="border-t border-t-tertiary pt-6 mt-2 hidden">
|
||||
<h3 className="text-lg font-medium mb-2">
|
||||
{t(I18nKey.SETTINGS$GIT_SETTINGS)}
|
||||
</h3>
|
||||
<p className="text-xs mb-4">
|
||||
<p className="text-sm text-secondary mb-4">
|
||||
{t(I18nKey.SETTINGS$GIT_SETTINGS_DESCRIPTION)}
|
||||
</p>
|
||||
<div className="flex flex-col gap-6">
|
||||
|
||||
@@ -154,49 +154,47 @@ function GitSettingsScreen() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{!isSaas && (
|
||||
<GitHubTokenInput
|
||||
name="github-token-input"
|
||||
isGitHubTokenSet={isGitHubTokenSet}
|
||||
onChange={(value) => {
|
||||
setGithubTokenInputHasValue(!!value);
|
||||
}}
|
||||
onGitHubHostChange={(value) => {
|
||||
setGithubHostInputHasValue(!!value);
|
||||
}}
|
||||
githubHostSet={existingGithubHost}
|
||||
/>
|
||||
)}
|
||||
{!isSaas && (
|
||||
<GitHubTokenInput
|
||||
name="github-token-input"
|
||||
isGitHubTokenSet={isGitHubTokenSet}
|
||||
onChange={(value) => {
|
||||
setGithubTokenInputHasValue(!!value);
|
||||
}}
|
||||
onGitHubHostChange={(value) => {
|
||||
setGithubHostInputHasValue(!!value);
|
||||
}}
|
||||
githubHostSet={existingGithubHost}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isSaas && (
|
||||
<GitLabTokenInput
|
||||
name="gitlab-token-input"
|
||||
isGitLabTokenSet={isGitLabTokenSet}
|
||||
onChange={(value) => {
|
||||
setGitlabTokenInputHasValue(!!value);
|
||||
}}
|
||||
onGitLabHostChange={(value) => {
|
||||
setGitlabHostInputHasValue(!!value);
|
||||
}}
|
||||
gitlabHostSet={existingGitlabHost}
|
||||
/>
|
||||
)}
|
||||
{!isSaas && (
|
||||
<GitLabTokenInput
|
||||
name="gitlab-token-input"
|
||||
isGitLabTokenSet={isGitLabTokenSet}
|
||||
onChange={(value) => {
|
||||
setGitlabTokenInputHasValue(!!value);
|
||||
}}
|
||||
onGitLabHostChange={(value) => {
|
||||
setGitlabHostInputHasValue(!!value);
|
||||
}}
|
||||
gitlabHostSet={existingGitlabHost}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isSaas && (
|
||||
<BitbucketTokenInput
|
||||
name="bitbucket-token-input"
|
||||
isBitbucketTokenSet={isBitbucketTokenSet}
|
||||
onChange={(value) => {
|
||||
setBitbucketTokenInputHasValue(!!value);
|
||||
}}
|
||||
onBitbucketHostChange={(value) => {
|
||||
setBitbucketHostInputHasValue(!!value);
|
||||
}}
|
||||
bitbucketHostSet={existingBitbucketHost}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{!isSaas && (
|
||||
<BitbucketTokenInput
|
||||
name="bitbucket-token-input"
|
||||
isBitbucketTokenSet={isBitbucketTokenSet}
|
||||
onChange={(value) => {
|
||||
setBitbucketTokenInputHasValue(!!value);
|
||||
}}
|
||||
onBitbucketHostChange={(value) => {
|
||||
setBitbucketHostInputHasValue(!!value);
|
||||
}}
|
||||
bitbucketHostSet={existingBitbucketHost}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import posthog from "posthog-js";
|
||||
import { useSettings } from "#/hooks/query/use-settings";
|
||||
@@ -18,15 +18,11 @@ function MCPSettingsScreen() {
|
||||
const { data: settings, isLoading } = useSettings();
|
||||
const { mutate: saveSettings, isPending } = useSaveSettings();
|
||||
|
||||
const [mcpConfig, setMcpConfig] = useState<MCPConfig | undefined>(undefined);
|
||||
const [mcpConfig, setMcpConfig] = useState<MCPConfig | undefined>(
|
||||
settings?.MCP_CONFIG,
|
||||
);
|
||||
const [isDirty, setIsDirty] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mcpConfig && settings?.MCP_CONFIG) {
|
||||
setMcpConfig(settings.MCP_CONFIG);
|
||||
}
|
||||
}, [settings, mcpConfig]);
|
||||
|
||||
const handleConfigChange = (config: MCPConfig) => {
|
||||
setMcpConfig(config);
|
||||
setIsDirty(true);
|
||||
|
||||
@@ -203,17 +203,16 @@ export default function MainApp() {
|
||||
>
|
||||
<Sidebar />
|
||||
|
||||
<div className="flex flex-col w-full h-[calc(100%-50px)] md:h-full gap-3">
|
||||
<div
|
||||
id="root-outlet"
|
||||
className="h-[calc(100%-50px)] md:h-full w-full relative overflow-auto"
|
||||
>
|
||||
{config.data?.MAINTENANCE && (
|
||||
<div className="flex-shrink-0">
|
||||
<MaintenanceBanner startTime={config.data.MAINTENANCE.startTime} />
|
||||
</div>
|
||||
<MaintenanceBanner startTime={config.data.MAINTENANCE.startTime} />
|
||||
)}
|
||||
<div id="root-outlet" className="flex-1 relative overflow-auto">
|
||||
<EmailVerificationGuard>
|
||||
<Outlet />
|
||||
</EmailVerificationGuard>
|
||||
</div>
|
||||
<EmailVerificationGuard>
|
||||
<Outlet />
|
||||
</EmailVerificationGuard>
|
||||
</div>
|
||||
|
||||
{renderAuthModal && (
|
||||
|
||||
@@ -89,7 +89,6 @@ function SecretsSettingsScreen() {
|
||||
to="/settings/integrations"
|
||||
data-testid="connect-git-button"
|
||||
type="button"
|
||||
className="self-start"
|
||||
>
|
||||
<BrandButton type="button" variant="secondary">
|
||||
{t(I18nKey.SECRETS$CONNECT_GIT_PROVIDER)}
|
||||
|
||||
@@ -47,8 +47,7 @@ export function getIndicatorColor(
|
||||
webSocketStatus === "DISCONNECTED" ||
|
||||
conversationStatus === "STOPPED" ||
|
||||
runtimeStatus === "STATUS$STOPPED" ||
|
||||
agentState === AgentState.STOPPED ||
|
||||
agentState === AgentState.ERROR
|
||||
agentState === AgentState.STOPPED
|
||||
) {
|
||||
return IndicatorColor.RED;
|
||||
}
|
||||
|
||||
@@ -31,52 +31,6 @@ export default defineConfig(({ mode }) => {
|
||||
svgr(),
|
||||
tailwindcss(),
|
||||
],
|
||||
optimizeDeps: {
|
||||
include: [
|
||||
// Pre-bundle ALL dependencies to prevent runtime optimization and page reloads
|
||||
// These are discovered during initial app load:
|
||||
"react-redux",
|
||||
"posthog-js",
|
||||
"@tanstack/react-query",
|
||||
"react-hot-toast",
|
||||
"@reduxjs/toolkit",
|
||||
"i18next",
|
||||
"i18next-http-backend",
|
||||
"i18next-browser-languagedetector",
|
||||
"react-i18next",
|
||||
"axios",
|
||||
"date-fns",
|
||||
"@uidotdev/usehooks",
|
||||
"react-icons/fa6",
|
||||
"react-icons/fa",
|
||||
"clsx",
|
||||
"tailwind-merge",
|
||||
"@heroui/react",
|
||||
"lucide-react",
|
||||
"react-select",
|
||||
"react-select/async",
|
||||
"@microlink/react-json-view",
|
||||
"socket.io-client",
|
||||
// These are discovered when launching conversations:
|
||||
"react-icons/vsc",
|
||||
"react-icons/lu",
|
||||
"react-icons/di",
|
||||
"react-icons/io5",
|
||||
"react-icons/io", // Added to prevent runtime optimization
|
||||
"@monaco-editor/react",
|
||||
"react-textarea-autosize",
|
||||
"react-markdown",
|
||||
"remark-gfm",
|
||||
"remark-breaks",
|
||||
"react-syntax-highlighter",
|
||||
"react-syntax-highlighter/dist/esm/styles/prism",
|
||||
"react-syntax-highlighter/dist/esm/styles/hljs",
|
||||
// Terminal dependencies - added to prevent runtime optimization
|
||||
"@xterm/addon-fit",
|
||||
"@xterm/xterm",
|
||||
"@xterm/xterm/css/xterm.css",
|
||||
],
|
||||
},
|
||||
server: {
|
||||
port: FE_PORT,
|
||||
host: true,
|
||||
|
||||
@@ -1,4 +1,79 @@
|
||||
{% include "system_prompt.j2" %}
|
||||
You are OpenHands agent, a helpful AI assistant that can interact with a computer to solve tasks.
|
||||
|
||||
<ROLE>
|
||||
Your primary role is to assist users by executing commands, modifying code, and solving technical problems effectively. You should be thorough, methodical, and prioritize quality over speed.
|
||||
* If the user asks a question, like "why is X happening", don't try to fix the problem. Just give an answer to the question.
|
||||
</ROLE>
|
||||
|
||||
<EFFICIENCY>
|
||||
* Each action you take is somewhat expensive. Wherever possible, combine multiple actions into a single action, e.g. combine multiple bash commands into one, using sed and grep to edit/view multiple files at once.
|
||||
* When exploring the codebase, use efficient tools like find, grep, and git commands with appropriate filters to minimize unnecessary operations.
|
||||
</EFFICIENCY>
|
||||
|
||||
<FILE_SYSTEM_GUIDELINES>
|
||||
* When a user provides a file path, do NOT assume it's relative to the current working directory. First explore the file system to locate the file before working on it.
|
||||
* If asked to edit a file, edit the file directly, rather than creating a new file with a different filename.
|
||||
* For global search-and-replace operations, consider using `sed` instead of opening file editors multiple times.
|
||||
</FILE_SYSTEM_GUIDELINES>
|
||||
|
||||
<CODE_QUALITY>
|
||||
* Write clean, efficient code with minimal comments. Avoid redundancy in comments: Do not repeat information that can be easily inferred from the code itself.
|
||||
* When implementing solutions, focus on making the minimal changes needed to solve the problem.
|
||||
* Before implementing any changes, first thoroughly understand the codebase through exploration.
|
||||
* If you are adding a lot of code to a function or file, consider splitting the function or file into smaller pieces when appropriate.
|
||||
* Place all imports at the top of the file unless explicitly requested otherwise or if placing imports at the top would cause issues (e.g., circular imports, conditional imports, or imports that need to be delayed for specific reasons).
|
||||
</CODE_QUALITY>
|
||||
|
||||
<VERSION_CONTROL>
|
||||
* If there are existing git user credentials already configured, use them and add Co-authored-by: openhands <openhands@all-hands.dev> to any commits messages you make. if a git config doesn't exist use "openhands" as the user.name and "openhands@all-hands.dev" as the user.email by default, unless explicitly instructed otherwise.
|
||||
* Exercise caution with git operations. Do NOT make potentially dangerous changes (e.g., pushing to main, deleting repositories) unless explicitly asked to do so.
|
||||
* When committing changes, use `git status` to see all modified files, and stage all files necessary for the commit. Use `git commit -a` whenever possible.
|
||||
* Do NOT commit files that typically shouldn't go into version control (e.g., node_modules/, .env files, build directories, cache files, large binaries) unless explicitly instructed by the user.
|
||||
* If unsure about committing certain files, check for the presence of .gitignore files or ask the user for clarification.
|
||||
</VERSION_CONTROL>
|
||||
|
||||
<PULL_REQUESTS>
|
||||
* **Important**: Do not push to the remote branch and/or start a pull request unless explicitly asked to do so.
|
||||
* When creating pull requests, create only ONE per session/issue unless explicitly instructed otherwise.
|
||||
* When working with an existing PR, update it with new commits rather than creating additional PRs for the same issue.
|
||||
* When updating a PR, preserve the original PR title and purpose, updating description only when necessary.
|
||||
</PULL_REQUESTS>
|
||||
|
||||
<PROBLEM_SOLVING_WORKFLOW>
|
||||
1. EXPLORATION: Thoroughly explore relevant files and understand the context before proposing solutions
|
||||
2. ANALYSIS: Consider multiple approaches and select the most promising one
|
||||
3. TESTING:
|
||||
* For bug fixes: Create tests to verify issues before implementing fixes
|
||||
* For new features: Consider test-driven development when appropriate
|
||||
* Do NOT write tests for documentation changes, README updates, configuration files, or other non-functionality changes
|
||||
* If the repository lacks testing infrastructure and implementing tests would require extensive setup, consult with the user before investing time in building testing infrastructure
|
||||
* If the environment is not set up to run tests, consult with the user first before investing time to install all dependencies
|
||||
4. IMPLEMENTATION: Make focused, minimal changes to address the problem
|
||||
5. VERIFICATION: If the environment is set up to run tests, test your implementation thoroughly, including edge cases. If the environment is not set up to run tests, consult with the user first before investing time to run tests.
|
||||
</PROBLEM_SOLVING_WORKFLOW>
|
||||
|
||||
<SECURITY>
|
||||
* Only use GITHUB_TOKEN and other credentials in ways the user has explicitly requested and would expect.
|
||||
* Use APIs to work with GitHub or other platforms, unless the user asks otherwise or your task requires browsing.
|
||||
</SECURITY>
|
||||
|
||||
<ENVIRONMENT_SETUP>
|
||||
* When user asks you to run an application, don't stop if the application is not installed. Instead, please install the application and run the command again.
|
||||
* If you encounter missing dependencies:
|
||||
1. First, look around in the repository for existing dependency files (requirements.txt, pyproject.toml, package.json, Gemfile, etc.)
|
||||
2. If dependency files exist, use them to install all dependencies at once (e.g., `pip install -r requirements.txt`, `npm install`, etc.)
|
||||
3. Only install individual packages directly if no dependency files are found or if only specific packages are needed
|
||||
* Similarly, if you encounter missing dependencies for essential tools requested by the user, install them when possible.
|
||||
</ENVIRONMENT_SETUP>
|
||||
|
||||
<TROUBLESHOOTING>
|
||||
* If you've made repeated attempts to solve a problem but tests still fail or the user reports it's still broken:
|
||||
1. Step back and reflect on 5-7 different possible sources of the problem
|
||||
2. Assess the likelihood of each possible cause
|
||||
3. Methodically address the most likely causes, starting with the highest probability
|
||||
4. Document your reasoning process
|
||||
* When you run into any major issue while executing a plan from the user, please don't try to directly work around it. Instead, propose a new plan and confirm with the user before proceeding.
|
||||
</TROUBLESHOOTING>
|
||||
|
||||
<INTERACTION_RULES>
|
||||
* When the user instructions are high-level or vague, explore the codebase before implementing solutions or interacting with users to figure out the best approach.
|
||||
|
||||
@@ -1,4 +1,55 @@
|
||||
{% include "system_prompt.j2" %}
|
||||
You are OpenHands agent, a helpful AI assistant that can interact with a computer to solve tasks.
|
||||
|
||||
<ROLE>
|
||||
Your primary role is to assist users by executing commands, modifying code, and solving technical problems effectively. You should be thorough, methodical, and prioritize quality over speed.
|
||||
* If the user asks a question, like "why is X happening", don't try to fix the problem. Just give an answer to the question.
|
||||
</ROLE>
|
||||
|
||||
<EFFICIENCY>
|
||||
* Each action you take is somewhat expensive. Wherever possible, combine multiple actions into a single action, e.g. combine multiple bash commands into one, using sed and grep to edit/view multiple files at once.
|
||||
* When exploring the codebase, use efficient tools like find, grep, and git commands with appropriate filters to minimize unnecessary operations.
|
||||
</EFFICIENCY>
|
||||
|
||||
<FILE_SYSTEM_GUIDELINES>
|
||||
* When a user provides a file path, do NOT assume it's relative to the current working directory. First explore the file system to locate the file before working on it.
|
||||
* If asked to edit a file, edit the file directly, rather than creating a new file with a different filename.
|
||||
* For global search-and-replace operations, consider using `sed` instead of opening file editors multiple times.
|
||||
</FILE_SYSTEM_GUIDELINES>
|
||||
|
||||
<CODE_QUALITY>
|
||||
* Write clean, efficient code with minimal comments. Avoid redundancy in comments: Do not repeat information that can be easily inferred from the code itself.
|
||||
* When implementing solutions, focus on making the minimal changes needed to solve the problem.
|
||||
* Before implementing any changes, first thoroughly understand the codebase through exploration.
|
||||
* If you are adding a lot of code to a function or file, consider splitting the function or file into smaller pieces when appropriate.
|
||||
* Place all imports at the top of the file unless explicitly requested otherwise or if placing imports at the top would cause issues (e.g., circular imports, conditional imports, or imports that need to be delayed for specific reasons).
|
||||
</CODE_QUALITY>
|
||||
|
||||
<VERSION_CONTROL>
|
||||
* If there are existing git user credentials already configured, use them and add Co-authored-by: openhands <openhands@all-hands.dev> to any commits messages you make. if a git config doesn't exist use "openhands" as the user.name and "openhands@all-hands.dev" as the user.email by default, unless explicitly instructed otherwise.
|
||||
* Exercise caution with git operations. Do NOT make potentially dangerous changes (e.g., pushing to main, deleting repositories) unless explicitly asked to do so.
|
||||
* When committing changes, use `git status` to see all modified files, and stage all files necessary for the commit. Use `git commit -a` whenever possible.
|
||||
* Do NOT commit files that typically shouldn't go into version control (e.g., node_modules/, .env files, build directories, cache files, large binaries) unless explicitly instructed by the user.
|
||||
* If unsure about committing certain files, check for the presence of .gitignore files or ask the user for clarification.
|
||||
</VERSION_CONTROL>
|
||||
|
||||
<PULL_REQUESTS>
|
||||
* When creating pull requests, create only ONE per session/issue unless explicitly instructed otherwise.
|
||||
* When working with an existing PR, update it with new commits rather than creating additional PRs for the same issue.
|
||||
* When updating a PR, preserve the original PR title and purpose, updating description only when necessary.
|
||||
</PULL_REQUESTS>
|
||||
|
||||
<PROBLEM_SOLVING_WORKFLOW>
|
||||
1. EXPLORATION: Thoroughly explore relevant files and understand the context before proposing solutions
|
||||
2. ANALYSIS: Consider multiple approaches and select the most promising one
|
||||
3. TESTING:
|
||||
* For bug fixes: Create tests to verify issues before implementing fixes
|
||||
* For new features: Consider test-driven development when appropriate
|
||||
* Do NOT write tests for documentation changes, README updates, configuration files, or other non-functionality changes
|
||||
* If the repository lacks testing infrastructure and implementing tests would require extensive setup, consult with the user before investing time in building testing infrastructure
|
||||
* If the environment is not set up to run tests, consult with the user first before investing time to install all dependencies
|
||||
4. IMPLEMENTATION: Make focused, minimal changes to address the problem
|
||||
5. VERIFICATION: If the environment is set up to run tests, test your implementation thoroughly, including edge cases. If the environment is not set up to run tests, consult with the user first before investing time to run tests.
|
||||
</PROBLEM_SOLVING_WORKFLOW>
|
||||
|
||||
<TASK_MANAGEMENT>
|
||||
* For complex, long-horizon tasks, create a TODO.md file to track progress:
|
||||
@@ -37,3 +88,26 @@
|
||||
d. Once complete, check off the item in TODO.md
|
||||
e. Proceed to the next unchecked item
|
||||
</TASK_MANAGEMENT>
|
||||
|
||||
<SECURITY>
|
||||
* Only use GITHUB_TOKEN and other credentials in ways the user has explicitly requested and would expect.
|
||||
* Use APIs to work with GitHub or other platforms, unless the user asks otherwise or your task requires browsing.
|
||||
</SECURITY>
|
||||
|
||||
<ENVIRONMENT_SETUP>
|
||||
* When user asks you to run an application, don't stop if the application is not installed. Instead, please install the application and run the command again.
|
||||
* If you encounter missing dependencies:
|
||||
1. First, look around in the repository for existing dependency files (requirements.txt, pyproject.toml, package.json, Gemfile, etc.)
|
||||
2. If dependency files exist, use them to install all dependencies at once (e.g., `pip install -r requirements.txt`, `npm install`, etc.)
|
||||
3. Only install individual packages directly if no dependency files are found or if only specific packages are needed
|
||||
* Similarly, if you encounter missing dependencies for essential tools requested by the user, install them when possible.
|
||||
</ENVIRONMENT_SETUP>
|
||||
|
||||
<TROUBLESHOOTING>
|
||||
* If you've made repeated attempts to solve a problem but tests still fail or the user reports it's still broken:
|
||||
1. Step back and reflect on 5-7 different possible sources of the problem
|
||||
2. Assess the likelihood of each possible cause
|
||||
3. Methodically address the most likely causes, starting with the highest probability
|
||||
4. Document your reasoning process
|
||||
* When you run into any major issue while executing a plan from the user, please don't try to directly work around it. Instead, propose a new plan and confirm with the user before proceeding.
|
||||
</TROUBLESHOOTING>
|
||||
|
||||
@@ -3,7 +3,8 @@ import sys
|
||||
|
||||
|
||||
def refine_prompt(prompt: str):
|
||||
"""Refines the prompt based on the platform.
|
||||
"""
|
||||
Refines the prompt based on the platform.
|
||||
|
||||
On Windows systems, replaces 'bash' with 'powershell' and 'execute_bash' with 'execute_powershell'
|
||||
to ensure commands work correctly on the Windows platform.
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""ReadOnlyAgent - A specialized version of CodeActAgent that only uses read-only tools."""
|
||||
"""
|
||||
ReadOnlyAgent - A specialized version of CodeActAgent that only uses read-only tools.
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
@@ -723,6 +723,9 @@ def run_cli_command(args):
|
||||
except ConnectionRefusedError as e:
|
||||
print_formatted_text(f'Connection refused: {e}')
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print_formatted_text(f'An error occurred: {e}')
|
||||
sys.exit(1)
|
||||
finally:
|
||||
try:
|
||||
# Cancel all running tasks
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from prompt_toolkit import PromptSession, print_formatted_text
|
||||
from prompt_toolkit.completion import FuzzyWordCompleter
|
||||
@@ -20,7 +19,6 @@ from openhands.cli.utils import (
|
||||
VERIFIED_OPENAI_MODELS,
|
||||
VERIFIED_OPENHANDS_MODELS,
|
||||
VERIFIED_PROVIDERS,
|
||||
extract_model_and_provider,
|
||||
organize_models_and_providers,
|
||||
)
|
||||
from openhands.controller.agent import Agent
|
||||
@@ -126,36 +124,12 @@ async def get_validated_input(
|
||||
completer=None,
|
||||
validator=None,
|
||||
error_message: str = 'Input cannot be empty',
|
||||
*,
|
||||
default_value: str = '',
|
||||
enter_keeps_value: Optional[str] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get validated input from user.
|
||||
|
||||
Args:
|
||||
session: PromptSession instance
|
||||
prompt_text: The text to display before the input
|
||||
completer: Completer instance
|
||||
validator: Function to validate input
|
||||
error_message: Error message to display if input is invalid
|
||||
default_value: Value to show prefilled in the prompt (prompt placeholder)
|
||||
enter_keeps_value: If provided, pressing Enter on an empty input will
|
||||
return this value (useful for keeping existing sensitive values)
|
||||
|
||||
Returns:
|
||||
str: The validated input
|
||||
"""
|
||||
|
||||
session.completer = completer
|
||||
value = None
|
||||
|
||||
while True:
|
||||
value = await session.prompt_async(prompt_text, default=default_value)
|
||||
|
||||
# If user submits empty input and a keep-value is provided, use it.
|
||||
if not value.strip() and enter_keeps_value is not None:
|
||||
value = enter_keeps_value
|
||||
value = await session.prompt_async(prompt_text)
|
||||
|
||||
if validator:
|
||||
is_valid = validator(value)
|
||||
@@ -186,50 +160,6 @@ def save_settings_confirmation(config: OpenHandsConfig) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _get_current_values_for_modification_basic(
|
||||
config: OpenHandsConfig,
|
||||
) -> tuple[str, str, str]:
|
||||
llm_config = config.get_llm_config()
|
||||
current_provider = ''
|
||||
current_model = ''
|
||||
current_api_key = (
|
||||
llm_config.api_key.get_secret_value() if llm_config.api_key else ''
|
||||
)
|
||||
if llm_config.model:
|
||||
model_info = extract_model_and_provider(llm_config.model)
|
||||
current_provider = model_info.provider or ''
|
||||
current_model = model_info.model or ''
|
||||
return current_provider, current_model, current_api_key
|
||||
|
||||
|
||||
def _get_default_provider(provider_list: list[str]) -> str:
|
||||
if 'anthropic' in provider_list:
|
||||
return 'anthropic'
|
||||
else:
|
||||
return provider_list[0] if provider_list else ''
|
||||
|
||||
|
||||
def _get_initial_provider_index(
|
||||
verified_providers: list[str],
|
||||
current_provider: str,
|
||||
default_provider: str,
|
||||
provider_choices: list[str],
|
||||
) -> int:
|
||||
if (current_provider or default_provider) in verified_providers:
|
||||
return verified_providers.index(current_provider or default_provider)
|
||||
elif current_provider or default_provider:
|
||||
return len(provider_choices) - 1
|
||||
return 0
|
||||
|
||||
|
||||
def _get_initial_model_index(
|
||||
verified_models: list[str], current_model: str, default_model: str
|
||||
) -> int:
|
||||
if (current_model or default_model) in verified_models:
|
||||
return verified_models.index(current_model or default_model)
|
||||
return 0
|
||||
|
||||
|
||||
async def modify_llm_settings_basic(
|
||||
config: OpenHandsConfig, settings_store: FileSettingsStore
|
||||
) -> None:
|
||||
@@ -241,35 +171,26 @@ async def modify_llm_settings_basic(
|
||||
provider_list = [p for p in provider_list if p not in verified_providers]
|
||||
provider_list = verified_providers + provider_list
|
||||
|
||||
provider_completer = FuzzyWordCompleter(provider_list, WORD=True)
|
||||
provider_completer = FuzzyWordCompleter(provider_list)
|
||||
session = PromptSession(key_bindings=kb_cancel())
|
||||
|
||||
current_provider, current_model, current_api_key = (
|
||||
_get_current_values_for_modification_basic(config)
|
||||
)
|
||||
|
||||
default_provider = _get_default_provider(provider_list)
|
||||
|
||||
provider = None
|
||||
# Set default provider - prefer 'anthropic' if available, otherwise use first
|
||||
provider = 'anthropic' if 'anthropic' in provider_list else provider_list[0]
|
||||
model = None
|
||||
api_key = None
|
||||
|
||||
try:
|
||||
# Show the default provider but allow changing it
|
||||
print_formatted_text(
|
||||
HTML(f'\n<grey>Default provider: </grey><green>{default_provider}</green>')
|
||||
HTML(f'\n<grey>Default provider: </grey><green>{provider}</green>')
|
||||
)
|
||||
|
||||
# Show verified providers plus "Select another provider" option
|
||||
provider_choices = verified_providers + ['Select another provider']
|
||||
|
||||
provider_choice = cli_confirm(
|
||||
config,
|
||||
'(Step 1/3) Select LLM Provider:',
|
||||
provider_choices,
|
||||
initial_selection=_get_initial_provider_index(
|
||||
verified_providers, current_provider, default_provider, provider_choices
|
||||
),
|
||||
)
|
||||
|
||||
# Ensure provider_choice is an integer (for test compatibility)
|
||||
@@ -290,19 +211,8 @@ async def modify_llm_settings_basic(
|
||||
completer=provider_completer,
|
||||
validator=lambda x: x in organized_models,
|
||||
error_message='Invalid provider selected',
|
||||
default_value=(
|
||||
# Prefill only for unverified providers.
|
||||
current_provider
|
||||
if current_provider not in verified_providers
|
||||
else ''
|
||||
),
|
||||
)
|
||||
|
||||
# Reset current model and api key if provider changes
|
||||
if provider != current_provider:
|
||||
current_model = ''
|
||||
current_api_key = ''
|
||||
|
||||
# Make sure the provider exists in organized_models
|
||||
if provider not in organized_models:
|
||||
# If the provider doesn't exist, prefer 'anthropic' if available,
|
||||
@@ -366,9 +276,6 @@ async def modify_llm_settings_basic(
|
||||
+ 'LLM usage is billed at the providers’ rates with no markup. Details: https://docs.all-hands.dev/usage/llms/openhands-llms'
|
||||
),
|
||||
model_choices,
|
||||
initial_selection=_get_initial_model_index(
|
||||
VERIFIED_OPENHANDS_MODELS, current_model, default_model
|
||||
),
|
||||
)
|
||||
|
||||
# Get the selected model from the list
|
||||
@@ -384,15 +291,12 @@ async def modify_llm_settings_basic(
|
||||
config,
|
||||
'Do you want to use a different model?',
|
||||
[f'Use {default_model}', 'Select another model'],
|
||||
initial_selection=0
|
||||
if (current_model or default_model) == default_model
|
||||
else 1,
|
||||
)
|
||||
== 1
|
||||
)
|
||||
|
||||
if change_model:
|
||||
model_completer = FuzzyWordCompleter(provider_models, WORD=True)
|
||||
model_completer = FuzzyWordCompleter(provider_models)
|
||||
|
||||
# Define a validator function that allows custom models but shows a warning
|
||||
def model_validator(x):
|
||||
@@ -416,10 +320,6 @@ async def modify_llm_settings_basic(
|
||||
completer=model_completer,
|
||||
validator=model_validator,
|
||||
error_message='Model name cannot be empty',
|
||||
default_value=(
|
||||
# Prefill only for models that are not the default model.
|
||||
current_model if current_model != default_model else ''
|
||||
),
|
||||
)
|
||||
else:
|
||||
# Use the default model
|
||||
@@ -432,15 +332,10 @@ async def modify_llm_settings_basic(
|
||||
)
|
||||
)
|
||||
|
||||
prompt_text = '(Step 3/3) Enter API Key (CTRL-c to cancel): '
|
||||
if current_api_key:
|
||||
prompt_text = f'(Step 3/3) Enter API Key [{current_api_key[:4]}***{current_api_key[-4:]}] (CTRL-c to cancel, ENTER to keep current, type new to change): '
|
||||
api_key = await get_validated_input(
|
||||
session,
|
||||
prompt_text,
|
||||
'(Step 3/3) Enter API Key (CTRL-c to cancel): ',
|
||||
error_message='API Key cannot be empty',
|
||||
default_value='',
|
||||
enter_keeps_value=current_api_key,
|
||||
)
|
||||
|
||||
except (
|
||||
@@ -491,7 +386,6 @@ async def modify_llm_settings_advanced(
|
||||
config: OpenHandsConfig, settings_store: FileSettingsStore
|
||||
) -> None:
|
||||
session = PromptSession(key_bindings=kb_cancel())
|
||||
llm_config = config.get_llm_config()
|
||||
|
||||
custom_model = None
|
||||
base_url = None
|
||||
@@ -503,39 +397,28 @@ async def modify_llm_settings_advanced(
|
||||
session,
|
||||
'(Step 1/6) Custom Model (CTRL-c to cancel): ',
|
||||
error_message='Custom Model cannot be empty',
|
||||
default_value=llm_config.model or '',
|
||||
)
|
||||
|
||||
base_url = await get_validated_input(
|
||||
session,
|
||||
'(Step 2/6) Base URL (CTRL-c to cancel): ',
|
||||
error_message='Base URL cannot be empty',
|
||||
default_value=llm_config.base_url or '',
|
||||
)
|
||||
|
||||
prompt_text = '(Step 3/6) API Key (CTRL-c to cancel): '
|
||||
current_api_key = (
|
||||
llm_config.api_key.get_secret_value() if llm_config.api_key else ''
|
||||
)
|
||||
if current_api_key:
|
||||
prompt_text = f'(Step 3/6) API Key [{current_api_key[:4]}***{current_api_key[-4:]}] (CTRL-c to cancel, ENTER to keep current, type new to change): '
|
||||
api_key = await get_validated_input(
|
||||
session,
|
||||
prompt_text,
|
||||
'(Step 3/6) API Key (CTRL-c to cancel): ',
|
||||
error_message='API Key cannot be empty',
|
||||
default_value='',
|
||||
enter_keeps_value=current_api_key,
|
||||
)
|
||||
|
||||
agent_list = Agent.list_agents()
|
||||
agent_completer = FuzzyWordCompleter(agent_list, WORD=True)
|
||||
agent_completer = FuzzyWordCompleter(agent_list)
|
||||
agent = await get_validated_input(
|
||||
session,
|
||||
'(Step 4/6) Agent (TAB for options, CTRL-c to cancel): ',
|
||||
completer=agent_completer,
|
||||
validator=lambda x: x in agent_list,
|
||||
error_message='Invalid agent selected',
|
||||
default_value=config.default_agent or '',
|
||||
)
|
||||
|
||||
enable_confirmation_mode = (
|
||||
@@ -543,7 +426,6 @@ async def modify_llm_settings_advanced(
|
||||
config,
|
||||
question='(Step 5/6) Confirmation Mode (CTRL-c to cancel):',
|
||||
choices=['Enable', 'Disable'],
|
||||
initial_selection=0 if config.security.confirmation_mode else 1,
|
||||
)
|
||||
== 0
|
||||
)
|
||||
@@ -553,7 +435,6 @@ async def modify_llm_settings_advanced(
|
||||
config,
|
||||
question='(Step 6/6) Memory Condensation (CTRL-c to cancel):',
|
||||
choices=['Enable', 'Disable'],
|
||||
initial_selection=0 if config.enable_default_condenser else 1,
|
||||
)
|
||||
== 0
|
||||
)
|
||||
@@ -582,7 +463,6 @@ async def modify_llm_settings_advanced(
|
||||
config.default_agent = agent
|
||||
|
||||
config.security.confirmation_mode = enable_confirmation_mode
|
||||
config.enable_default_condenser = enable_memory_condensation
|
||||
|
||||
agent_config = config.get_agent_config(config.default_agent)
|
||||
if enable_memory_condensation:
|
||||
|
||||
@@ -5,6 +5,7 @@ import warnings
|
||||
|
||||
def suppress_cli_warnings():
|
||||
"""Suppress common warnings that appear during CLI usage."""
|
||||
|
||||
# Suppress pydub warning about ffmpeg/avconv
|
||||
warnings.filterwarnings(
|
||||
'ignore',
|
||||
|
||||
@@ -239,7 +239,8 @@ def display_mcp_errors() -> None:
|
||||
|
||||
# Prompt output display functions
|
||||
def display_thought_if_new(thought: str, is_agent_message: bool = False) -> None:
|
||||
"""Display a thought only if it hasn't been displayed recently.
|
||||
"""
|
||||
Display a thought only if it hasn't been displayed recently.
|
||||
|
||||
Args:
|
||||
thought: The thought to display
|
||||
@@ -300,7 +301,8 @@ def display_event(event: Event, config: OpenHandsConfig) -> None:
|
||||
|
||||
|
||||
def display_message(message: str, is_agent_message: bool = False) -> None:
|
||||
"""Display a message in the terminal with markdown rendering.
|
||||
"""
|
||||
Display a message in the terminal with markdown rendering.
|
||||
|
||||
Args:
|
||||
message: The message to display
|
||||
@@ -336,7 +338,8 @@ def display_message(message: str, is_agent_message: bool = False) -> None:
|
||||
|
||||
|
||||
def convert_markdown_to_html(text: str) -> str:
|
||||
"""Convert markdown to HTML for prompt_toolkit's HTML renderer using the markdown library.
|
||||
"""
|
||||
Convert markdown to HTML for prompt_toolkit's HTML renderer using the markdown library.
|
||||
|
||||
Args:
|
||||
text: Markdown text to convert
|
||||
@@ -842,7 +845,6 @@ def cli_confirm(
|
||||
config: OpenHandsConfig,
|
||||
question: str = 'Are you sure?',
|
||||
choices: list[str] | None = None,
|
||||
initial_selection: int = 0,
|
||||
) -> int:
|
||||
"""Display a confirmation prompt with the given question and choices.
|
||||
|
||||
@@ -850,7 +852,7 @@ def cli_confirm(
|
||||
"""
|
||||
if choices is None:
|
||||
choices = ['Yes', 'No']
|
||||
selected = [initial_selection] # Using list to allow modification in closure
|
||||
selected = [0] # Using list to allow modification in closure
|
||||
|
||||
def get_choice_text() -> list:
|
||||
return [
|
||||
@@ -906,6 +908,7 @@ def cli_confirm(
|
||||
layout=layout,
|
||||
key_bindings=kb,
|
||||
style=style,
|
||||
mouse_support=True,
|
||||
full_screen=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -56,7 +56,8 @@ def download_latest_vsix_from_github() -> str | None:
|
||||
|
||||
|
||||
def attempt_vscode_extension_install():
|
||||
"""Checks if running in a supported editor and attempts to install the OpenHands companion extension.
|
||||
"""
|
||||
Checks if running in a supported editor and attempts to install the OpenHands companion extension.
|
||||
This is a best-effort, one-time attempt.
|
||||
"""
|
||||
# 1. Check if we are in a supported editor environment
|
||||
@@ -131,7 +132,8 @@ def attempt_vscode_extension_install():
|
||||
|
||||
|
||||
def _mark_installation_successful(flag_file: pathlib.Path, editor_name: str) -> None:
|
||||
"""Mark the extension installation as successful by creating the flag file.
|
||||
"""
|
||||
Mark the extension installation as successful by creating the flag file.
|
||||
|
||||
Args:
|
||||
flag_file: Path to the flag file to create
|
||||
@@ -145,7 +147,8 @@ def _mark_installation_successful(flag_file: pathlib.Path, editor_name: str) ->
|
||||
|
||||
|
||||
def _is_extension_installed(editor_command: str, extension_id: str) -> bool:
|
||||
"""Check if the OpenHands extension is already installed.
|
||||
"""
|
||||
Check if the OpenHands extension is already installed.
|
||||
|
||||
Args:
|
||||
editor_command: The command to run the editor (e.g., 'code', 'windsurf')
|
||||
@@ -171,7 +174,8 @@ def _is_extension_installed(editor_command: str, extension_id: str) -> bool:
|
||||
|
||||
|
||||
def _attempt_github_install(editor_command: str, editor_name: str) -> bool:
|
||||
"""Attempt to install the extension from GitHub Releases.
|
||||
"""
|
||||
Attempt to install the extension from GitHub Releases.
|
||||
|
||||
Downloads the latest VSIX file from GitHub releases and attempts to install it.
|
||||
Ensures proper cleanup of temporary files.
|
||||
@@ -223,7 +227,8 @@ def _attempt_github_install(editor_command: str, editor_name: str) -> bool:
|
||||
|
||||
|
||||
def _attempt_bundled_install(editor_command: str, editor_name: str) -> bool:
|
||||
"""Attempt to install the extension from the bundled VSIX file.
|
||||
"""
|
||||
Attempt to install the extension from the bundled VSIX file.
|
||||
|
||||
Uses the VSIX file packaged with the OpenHands installation.
|
||||
|
||||
@@ -275,7 +280,8 @@ def _attempt_bundled_install(editor_command: str, editor_name: str) -> bool:
|
||||
def _attempt_marketplace_install(
|
||||
editor_command: str, editor_name: str, extension_id: str
|
||||
) -> bool:
|
||||
"""Attempt to install the extension from the marketplace.
|
||||
"""
|
||||
Attempt to install the extension from the marketplace.
|
||||
|
||||
This method is currently unused as the OpenHands extension is not yet published
|
||||
to the VS Code/Windsurf marketplace. It's kept here for future use when the
|
||||
|
||||