chore: Drop monorepo (#1029)

* chore: Drop monorepo

* cr
This commit is contained in:
Brace Sproul 2026-03-06 16:10:34 -08:00 • committed by GitHub
parent ee3f88bd49
commit bd52e5e09d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
308 changed files with 26 additions and 46557 deletions

View file

@ -1,61 +0,0 @@
name: Agent CI
permissions:
contents: read
on:
push:
branches: ["main"]
paths:
- "apps/agent/**"
pull_request:
paths:
- "apps/agent/**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Agent lint
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/agent
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: uv sync --locked --extra dev
- name: Run lint
run: make lint
format:
name: Agent format check
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/agent
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: uv sync --locked --extra dev
- name: Run format check
run: make format-check
unit-tests:
name: Agent unit tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/agent
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: uv sync --locked --extra dev
- name: Run unit tests
run: make test

View file

@ -1,6 +1,4 @@
# Run formatting on all PRs
name: CI
name: Agent CI
permissions:
contents: read
@ -8,134 +6,43 @@ permissions:
on:
push:
branches: ["main"]
paths:
- "apps/web/**"
- "packages/shared/**"
- "*"
- ".*"
pull_request:
paths:
- "apps/web/**"
- "packages/shared/**"
- "*"
- ".*"
workflow_dispatch: # Allows triggering the workflow manually in GitHub UI
workflow_dispatch:
# If another push to the same PR or branch happens while this workflow is still running,
# cancel the earlier run in favor of the next run.
#
# There's no point in testing an outdated version of the code. GitHub only allows
# a limited number of job runners to be active at the same time, so it's better to cancel
# pointless jobs early so that more useful jobs can run sooner.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
format:
name: Check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: "yarn"
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
- name: Check formatting
run: yarn format:check
lint:
name: Check linting
name: Agent lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: "yarn"
- uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
- name: Check linting
run: yarn run lint
run: uv sync --locked --extra dev
- name: Run lint
run: make lint
build:
name: Build
format:
name: Agent format check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: "yarn"
- uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
- name: Build
run: yarn build
run: uv sync --locked --extra dev
- name: Run format check
run: make format-check
readme-spelling:
name: Check README spelling
unit-tests:
name: Agent unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: codespell-project/actions-codespell@v2
with:
ignore_words_file: .codespellignore
path: README.md
check-spelling:
name: Check code spelling
runs-on: ubuntu-latest
if: hashFiles('apps/open-swe/package.json') != ''
steps:
- uses: actions/checkout@v4
- uses: codespell-project/actions-codespell@v2
with:
ignore_words_file: .codespellignore
path: apps/open-swe/src
dev-server-check:
name: Check dev server startup
runs-on: ubuntu-latest
if: hashFiles('apps/open-swe/package.json') != ''
defaults:
run:
working-directory: apps/open-swe
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
working-directory: .
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: "yarn"
- uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
working-directory: .
- name: Build all packages
run: yarn build
working-directory: .
- name: Build dev server check script
run: yarn tsc --module commonjs --skipLibCheck --outDir ./dist/scripts ./scripts/check-dev-server.ts
- name: Rename compiled script to .cjs
run: mv ./dist/scripts/check-dev-server.js ./dist/scripts/check-dev-server.cjs
- name: Create .env file for dev server check
# .env needs to be in the apps/open-swe directory
run: touch .env
- name: Run dev server check
run: node ./dist/scripts/check-dev-server.cjs
env:
NODE_ENV: development
timeout-minutes: 2
run: uv sync --locked --extra dev
- name: Run unit tests
run: make test

View file

@ -1,52 +0,0 @@
name: Deploy LangGraph
on:
push:
branches: ["main"]
paths:
- "apps/open-swe/**"
- "packages/shared/**"
- "package.json"
workflow_dispatch: # Allows manual triggering
# If another push happens while this workflow is still running,
# cancel the earlier run in favor of the next run.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
name: Deploy to LangGraph Platform
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: "yarn"
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
- name: Execute deployment
run: |
cd apps/open-swe
yarn tsx ./scripts/deploy-langgraph.ts
env:
CONTROL_PLANE_HOST: ${{ secrets.CONTROL_PLANE_HOST }}
LANGSMITH_API_KEY_PROD: ${{ secrets.LANGSMITH_API_KEY_PROD }}
INTEGRATION_ID: ${{ secrets.INTEGRATION_ID }}
DEPLOYMENT_ID: ${{ secrets.DEPLOYMENT_ID }}
timeout-minutes: 35
- name: Deployment status
if: always()
run: echo "Deployment workflow completed with status ${{ job.status }}"

View file

@ -1,164 +0,0 @@
name: Sync Documentation Files
on:
push:
branches:
- main
paths:
- 'apps/docs/**'
workflow_dispatch:
jobs:
sync-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout source repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for MDX files and images in apps/docs
id: check-files
run: |
docs_found=false
images_found=false
if find apps/docs -name "*.mdx" -type f | head -1 | grep -q .; then
docs_found=true
echo "Found MDX files to sync"
fi
if [ -d "apps/docs/images" ] && find apps/docs/images -type f | head -1 | grep -q .; then
images_found=true
echo "Found images to sync"
fi
if [ "$docs_found" = true ] || [ "$images_found" = true ]; then
echo "docs_found=true" >> $GITHUB_OUTPUT
echo "At least MDX files or images found to sync"
else
echo "docs_found=false" >> $GITHUB_OUTPUT
echo "No MDX files or images found in apps/docs"
fi
echo "images_found=$images_found" >> $GITHUB_OUTPUT
- name: Clone LangChain docs repository
if: steps.check-files.outputs.docs_found == 'true'
run: |
git clone https://x-access-token:${{ secrets.LANGCHAIN_DOCS_TOKEN }}@github.com/langchain-ai/docs.git langchain-docs
cd langchain-docs
git config user.name "OpenSWE Bot"
git config user.email "openswe-bot@langchain.com"
- name: Create branch for documentation sync
if: steps.check-files.outputs.docs_found == 'true'
run: |
cd langchain-docs
BRANCH_NAME="sync-openswe-docs-$(date +%Y%m%d-%H%M%S)"
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
git checkout -b "$BRANCH_NAME"
- name: Prepare target directories
if: steps.check-files.outputs.docs_found == 'true'
run: |
cd langchain-docs
# Remove existing content to ensure clean sync
rm -rf src/labs/swe
mkdir -p src/labs/swe
# Prepare images directory
mkdir -p src/images
- name: Copy MDX files to LangChain docs
if: steps.check-files.outputs.docs_found == 'true'
run: |
# Copy all .mdx files from apps/docs to the target directory, preserving structure
if find apps/docs -name "*.mdx" -type f | head -1 | grep -q .; then
find apps/docs -name "*.mdx" -type f | while read file; do
# Get the relative path from apps/docs
rel_path="${file#apps/docs/}"
# Create the directory structure in target
target_dir="langchain-docs/src/labs/swe/$(dirname "$rel_path")"
mkdir -p "$target_dir"
# Copy the file
cp "$file" "langchain-docs/src/labs/swe/$rel_path"
done
# List copied files for verification
echo "Copied MDX files:"
find langchain-docs/src/labs/swe/ -name "*.mdx" -type f
else
echo "No MDX files found to copy"
fi
echo "Directory structure:"
tree langchain-docs/src/labs/swe/ || find langchain-docs/src/labs/swe/ -type d
- name: Copy images to LangChain docs
if: steps.check-files.outputs.docs_found == 'true' && steps.check-files.outputs.images_found == 'true'
run: |
# Copy all images from apps/docs/images to langchain-docs/src/images
if [ -d "apps/docs/images" ]; then
cp -r apps/docs/images/* langchain-docs/src/images/
# List copied images for verification
echo "Copied images:"
find langchain-docs/src/images/ -type f
else
echo "No images directory found to copy"
fi
- name: Commit changes
if: steps.check-files.outputs.docs_found == 'true'
id: commit
run: |
cd langchain-docs
git add src/labs/swe/
git add src/images/
if git diff --staged --quiet; then
echo "No changes to commit"
echo "has_changes=false" >> $GITHUB_OUTPUT
else
commit_message="Sync OpenSWE documentation files
- Updated MDX files from apps/docs/"
if [ "${{ steps.check-files.outputs.images_found }}" = "true" ]; then
commit_message="$commit_message
- Updated images from apps/docs/images/"
fi
commit_message="$commit_message
- Synced at $(date -u '+%Y-%m-%d %H:%M:%S UTC')
- Source commit: ${{ github.sha }}"
git commit -m "$commit_message"
echo "has_changes=true" >> $GITHUB_OUTPUT
fi
- name: Push branch and create pull request
if: steps.check-files.outputs.docs_found == 'true' && steps.commit.outputs.has_changes == 'true'
run: |
cd langchain-docs
git push origin "$BRANCH_NAME"
# Create pull request using GitHub CLI
gh pr create \
--title "Sync OpenSWE Documentation Files" \
--body "This PR syncs documentation files from the OpenSWE repository.
**Changes:**
- Updated MDX files from \`apps/docs/\`
- Target directory: \`src/labs/swe/\`$([ "${{ steps.check-files.outputs.images_found }}" = "true" ] && echo "
- Updated images from \`apps/docs/images/\`
- Target directory: \`src/images/\`" || echo "")
- Source commit: ${{ github.sha }}
- Synced at $(date -u '+%Y-%m-%d %H:%M:%S UTC')
**Auto-generated by:** OpenSWE Documentation Sync Workflow" \
--head "$BRANCH_NAME" \
--base main
env:
GH_TOKEN: ${{ secrets.LANGCHAIN_DOCS_TOKEN }}

View file

@ -1,50 +0,0 @@
# This workflow will run unit tests for the current project
name: Unit Tests
permissions:
contents: read
on:
push:
branches: ["main"]
paths:
- "apps/web/**"
- "packages/shared/**"
- "*"
- ".*"
pull_request:
paths:
- "apps/web/**"
- "packages/shared/**"
- "*"
- ".*"
workflow_dispatch: # Allows triggering the workflow manually in GitHub UI
# If another push to the same PR or branch happens while this workflow is still running,
# cancel the earlier run in favor of the next run.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
unit-tests:
name: Unit Tests
strategy:
matrix:
os: [ubuntu-latest]
node-version: [18.x, 20.x]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "yarn"
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
- name: Build project
run: yarn build
- name: Run tests
run: yarn test

View file

@ -1,2 +0,0 @@
npmRegistryServer: "https://registry.npmjs.org/"
nodeLinker: node-modules

View file

@ -1,75 +0,0 @@
<general_rules>
- Always use Yarn as the package manager - never use npm or other package managers
- Run all general commands (e.g. not for starting a server) from the repository root using Turbo orchestration (yarn build, yarn lint, yarn format)
- Before creating new utilities or shared functions, search in packages/shared/src to see if one already exists
- When importing from the shared package, use the @openswe/shared namespace with specific module paths
- Follow strict TypeScript practices - the codebase uses strict mode across all packages
- Use ESLint and Prettier for code quality - run yarn lint:fix and yarn format before committing
- Console logging is prohibited in the open-swe app (ESLint error) - use the `createLogger` function to create a new logger instance instead
- Build the shared package first before other packages can consume it (yarn build from the root handles this automatically via turbo repo)
- Follow existing code patterns and maintain consistency with the established architecture
- Include as few inline comments as possible
</general_rules>
<repository_structure>
This is a Yarn workspace monorepo with Turbo build orchestration containing three main packages:
**apps/open-swe**: LangGraph agent application
- Core LangChain/LangGraph agent implementation with TypeScript
- Contains three graphs: programmer, planner, and manager (configured in langgraph.json)
- Uses strict ESLint rules including no-console errors
**apps/web**: Next.js 15 web interface
- React 19 frontend with Shadcn UI components (wrapped Radix UI) and Tailwind CSS
- Modern web stack with TypeScript, ESLint, and Prettier with Tailwind plugin
- Serves as the user interface for the LangGraph agent
**packages/shared**: Common utilities package
- Central workspace dependency providing shared types, constants, and utilities
- Exports modules via @openswe/shared namespace (e.g., @openswe/shared/open-swe/types)
- Must be built before other packages can import from it
- Contains crypto utilities, GraphState types, and open-swe specific modules
**Root Configuration**:
- turbo.json: Build orchestration with task dependencies and parallel execution
- .yarnrc.yml: Yarn 3.5.1 configuration with node-modules linker
- tsconfig.json: Base TypeScript configuration extended by all packages
</repository_structure>
<dependencies_and_installation>
**Package Manager**: Use Yarn exclusively (configured in .yarnrc.yml)
**Installation Process**:
- Run `yarn install` from the repository root - this handles all workspace dependencies automatically
**Key Dependencies**:
- LangChain ecosystem: @langchain/langgraph, @langchain/anthropic for agent functionality
- Next.js 15 with React 19 for web interface
- Shadcn UI (wrapped Radix UI) and Tailwind CSS for component library and styling
- TypeScript with strict mode across all packages
- Jest with ts-jest for testing framework
**Workspace Structure**: Dependencies are managed on a per-package basis, meaning dependencies should only be installed in their specific app/package. Individual packages reference the shared package via @openswe/shared workspace dependency.
</dependencies_and_installation>
<testing_instructions>
**Testing Framework**: Jest with TypeScript support via ts-jest preset and ESM module handling
**Test Types**:
- Unit tests: *.test.ts files (e.g., take-action.test.ts in __tests__ directories)
- Integration tests: *.int.test.ts files (e.g., sandbox.int.test.ts)
**Running Tests**:
- `yarn test` - Run unit tests across all packages
- `yarn test:int` - Run integration tests (apps/open-swe only)
- `yarn test:single <file>` - Run a specific test file
**Test Configuration**:
- 20-second timeout for longer-running tests
- Environment variables loaded via dotenv integration
- ESM module support with .js extension mapping
- Pass-with-no-tests setting for CI/CD compatibility
**Writing Tests**: Focus on testing core business logic, utilities, and agent functionality. Integration tests should verify end-to-end workflows. Use the existing test patterns and maintain consistency with the established testing structure.
</testing_instructions>

View file

@ -1,3 +0,0 @@
# Open SWE Agent
LangGraph agent service for Open SWE.

View file

@ -1,13 +0,0 @@
{
"$schema": "https://langgra.ph/schema.json",
"python_version": "3.12",
"graphs": {
"agent": "agent.server:get_agent"
},
"dependencies": ["."],
"http": {
"app": "agent.webapp:app"
},
"env": ".env"
}

View file

@ -1,3 +0,0 @@
# Open SWE Documentation
Files in `docs` provide documentation for Open SWE, which includes comprehensive guides and API references.

View file

@ -1,88 +0,0 @@
{
"$schema": "https://mintlify.com/docs.json",
"theme": "mint",
"name": "Open SWE",
"colors": {
"primary": "#0ea5e9",
"light": "#38bdf8",
"dark": "#0369a1"
},
"favicon": "/favicon.svg",
"navigation": {
"tabs": [
{
"tab": "Guides",
"groups": [
{
"group": "Get Started",
"pages": ["index"]
},
{
"group": "Usage",
"pages": [
"usage/intro",
"usage/ui",
"usage/github",
"usage/pr-tagging",
"usage/examples",
"usage/best-practices",
"usage/custom-rules"
]
},
{
"group": "Development Setup",
"pages": [
"setup/intro",
"setup/development",
"setup/authentication",
"setup/customization",
"setup/monorepo",
"setup/ci"
]
},
{
"group": "Secrets",
"pages": ["secrets"]
},
{
"group": "FAQ",
"pages": ["faq"]
}
]
}
],
"global": {
"anchors": [
{
"anchor": "Demo",
"href": "https://swe.langchain.com",
"icon": "link"
},
{
"anchor": "GitHub",
"href": "https://github.com/langchain-ai/open-swe",
"icon": "github"
}
]
}
},
"logo": {
"light": "/logo/light.svg",
"dark": "/logo/dark.svg"
},
"navbar": {
"links": [
{
"label": "GitHub",
"href": "https://github.com/langchain-ai/open-swe"
}
]
},
"footer": {
"socials": {
"github": "https://github.com/langchain-ai/open-swe",
"twitter": "https://twitter.com/langchainai"
}
}
}

View file

@ -1,56 +0,0 @@
---
title: FAQ
description: Frequently Asked Questions
---
<Accordion title="How much does an end to end Open SWE run cost?">
The cost per run varies greatly based on the complexity of the task, the size of the repository, and the number of files that need to be changed.
For most tasks, you can expect to pay between `$0.50` -> `$3.00` when using Claude Opus 4.5.
For the same tasks running on Claude Opus 4/4.1, you can expect to pay between `$1.50` -> `$9.00`.
Always remember to monitor your runs if you're cost conscious. The most expensive run I've seen Open SWE complete was ~50M Opus 4 tokens, costing `$25.00`.
</Accordion>
<Accordion title="Does Open SWE automatically cache tokens?">
Yes. When using Anthropic models, all input tokens are cached on Anthropic's servers.
</Accordion>
<Accordion title="Can I disable Open SWE from creating an issue when I submit a request?">
Yes. There's two ways to disable Open SWE from creating an issue when you submit a request:
1. Toggle the 'eye' icon in the main chat area when submitting a request.
2. In the configuration tab in settings, toggle the 'Should Create Issue' switch.
By default, it's set to `true`. By modifying this setting in the configuration tab, all runs will default to that setting. You may override this setting on a per-run basis by toggling the 'eye' icon in the main chat area.
![Should Create Issue Toggle](/images/dont_create_issue_eye_screenshot.png)
![Global Should Create Issue Toggle](/images/dont_create_issue_global_toggle_screenshot.png)
</Accordion>
<Accordion title="My run failed midway through. What now?">
We're sorry you're experiencing this! Open SWE will automatically commit any changes it makes to a draft pull request. This means all of your progress is saved, and you can restart the run from the last checkpoint.
To restart the run, click the `Restart from last checkpoint` button. This will create a new thread and will resume from where it left off.
![Restart Run Screenshot](/images/restart_run_screenshot.png)
</Accordion>
<Accordion title="Can I use Open SWE in a production environment?">
Yes! We've been using Open SWE internally at LangChain for a while now, and it's been giving us great results.
We recommend forking and deploying Open SWE yourself if you plan on using it in a production environment. For checking out the product, the [demo application](https://swe.langchain.com) will work fine.
</Accordion>
<Accordion title="I installed Open SWE on a repository in my organization, but it doesn't show up in the UI. Why?">
Some GitHub organizations require administrator approval to install GitHub apps. Please reach out to an administrator in your organization to approve the installation request.
</Accordion>
<Accordion title="What sandbox environment is Open SWE running in?">
Open SWE's sandbox environment is powered by [Daytona.io](https://daytona.io).
</Accordion>
<Accordion title="Can I contribute to Open SWE?">
Yes! We're always looking for contributors to help us improve Open SWE. Feel free to pick up an [open issue](https://github.com/langchain-ai/open-swe/issues) or submit a pull request with a new feature or bug fix.
</Accordion>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 751 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 940 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

View file

@ -1,41 +0,0 @@
---
title: "Introduction"
description: "An introduction to Open SWE"
---
Open SWE is an open-source cloud-based coding agent built with [LangGraph](https://docs.langchain.com/oss/javascript/langgraph/overview). It's designed to autonomously understand, plan, and execute code changes across entire repositories.
## How It Works
Open SWE operates through three specialized LangGraph agents:
- **Manager Graph**: Orchestrates user interactions and coordinates between other graphs
- **Planner Graph**: Analyzes requirements and creates detailed execution plans
- **Programmer Graph**: Executes code changes based on approved plans
The agent can be used through a web interface or triggered automatically via GitHub webhooks, making it flexible for both interactive development and automated workflows.
![Open SWE UI Screenshot](/images/ui-screenshot.png)
<CardGroup cols={2}>
<Card title="Setup" icon="robot" href="/labs/swe/setup/intro">
How to set up Open SWE for development
</Card>
<Card title="Examples" icon="database" href="/labs/swe/usage/examples">
Examples of tasks you can try out
</Card>
<Card title="Usage" icon="database" href="/labs/swe/usage/intro">
Open SWE features, and how to use them
</Card>
</CardGroup>
## What's in These Docs
This documentation covers everything you need to know about Open SWE:
- **Usage**: How to interact with Open SWE through the web interface and GitHub webhooks
- **Setup**: Complete development environment setup including monorepo structure, dependencies, and authentication
<Tip>
Try out the [live demo](https://swe.langchain.com) to see Open SWE in action.
</Tip>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

View file

@ -1,16 +0,0 @@
{
"name": "@openswe/docs",
"author": "LangChain",
"repository": "https://github.com/langchain-ai/open-swe",
"private": true,
"scripts": {
"dev": "mint dev",
"format": "echo 'No formatting needed for docs' && exit 0",
"format:check": "echo 'No formatting needed for docs' && exit 0",
"clean": "rm -rf .turbo || true"
},
"devDependencies": {
"mint": "^4.2.12",
"prettier": "^3.5.2"
}
}

View file

@ -1,113 +0,0 @@
---
title: "Secrets"
description: "Environment variables & secrets access in dev environments"
---
Open-SWE implements a secure, encrypted way to share user environment variables with the agent and pass them to the development server.
## Environment Variables & API Keys
### How API Keys Are Protected
Your API keys are protected with industry-standard AES-256-GCM encryption both in transit and at rest.
**Storage Process:**
1. **Frontend**: Temporarily stored in browser localStorage (plain text)
2. **Transit**: Encrypted with AES-256-GCM before sending to backend
3. **Backend**: Stored encrypted in LangGraph's database
4. **Runtime**: Decrypted when necessary using server-side encryption keys
<Accordion title="Technical Encryption Details">
**Encryption Details:**
- **Algorithm**: [AES-256-GCM](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf) (authenticated encryption)
- **Key Derivation**: SHA-256 hash of system `SECRETS_ENCRYPTION_KEY`
- **IV Generation**: Random 12-byte initialization vector per encryption
- **Authentication**: 16-byte authentication tag prevents tampering
</Accordion>
### System vs User Environment Variables
Open-SWE handles two types of environment variables:
**System Environment Variables:**
- **Purpose**: Environment variables required to run Open-SWE. If you're self-hosting, you can set LLM, infrastructure, and authentication keys
- **Examples**: `DAYTONA_API_KEY`, `SECRETS_ENCRYPTION_KEY`, `GITHUB_APP_PRIVATE_KEY`
- **Access**: Server-side only via `process.env`
- **Security**: Never exposed to users or sandboxes
**User-Defined Environment Variables:**
- **Purpose**: Personal API keys and custom development variables
- **Examples**: `ANTHROPIC_API_KEY`, `MY_DATABASE_URL`, `STRIPE_TEST_KEY`
- **Access**: User-controlled via settings page in UI
- **Security**: AES-256-GCM encrypted + explicit user consent required to pass them to the development server
### When API Keys Are Exposed to Sandbox Environments
Your API keys are **never automatically exposed** to sandbox environments. Exposure requires explicit user consent.
You may wonder, why does the sandbox environment even need API keys? We've designed Open-SWE to be your junior dev. Part of development is testing code in real-time. **Open-SWE has the ability to spin up development servers that can run your LangGraph agents, Next.js apps, Flask servers, and more, all within the sandbox its running in.** If you think this option can be useful, please review what it means to pass your keys to the sandbox.
<Note>
Consider using separate development API keys with limited permissions instead of your production keys when enabling sandbox access. This reduces the impact of potential exposure while maintaining functionality.
</Note>
**Default Behavior (Secure):**
- API keys are only used for LLM model initialization & setting up Daytona using your own key, if you passed it
- Keys are NOT available as environment variables in sandboxes or accessible to LLMs
- Each key has `allowedInDev: false` by default
**When You Enable "Include in Dev Server":**
- You manually toggle the switch for each API key
- Key becomes available as an environment variable in sandbox
- You maintain control over which keys are exposed
## Sandbox Security & Isolation
### Container Isolation
Each user session runs in a completely isolated Daytona container with multiple security boundaries. These containers can modify the code in your repo on a new branch and don't exchange information across sessions.
<Accordion title="Isolation Details">
**Container Isolation:**
- Separate container per user session
- No shared file systems between containers
- Container-level resource limits and quotas
**Process Isolation:**
- User environment variables vs system environment variables
- Sandboxed processes cannot access host system or code
**Temporal Isolation:**
- Automatic deletion after 15 minutes of inactivity
- No persistent storage across sessions
- Fresh environment for each new session
**Network Isolation:**
- Containers cannot communicate with each other
</Accordion>
## Data Handling & Access Control
<Warning>
When enabling "Include in Dev Server" for API keys, you're expanding the attack surface of your credentials. AI agent may inadvertently expose environment variables in generated code or logs. Only use this feature when necessary for development server monitoring.
</Warning>
### Development Environment Exposure Risks
You should only use this feature if you find it useful to monitor development servers. We recommend not using your main keys and having access control for keys that you pass to sandbox environments.
While the Open-SWE UI and agent exchange these using encryption, **LLM may leave environment variables exposed in the code.**
## Security Best Practices
Our team loves using Open-SWE internally and we recommend the following best practices:
- Use the minimum required permissions for each API key
- Regularly rotate your API keys
- Only enable "Include in Dev Server" when necessary for specific tools
- Monitor the "Last Used" timestamps in settings
- Consider using separate development keys instead of production keys for sandbox environments
- Never enable sandbox access for production database credentials or high-privilege API keys
- Review generated code for accidentally exposed environment variables before deploying
---

View file

@ -1,198 +0,0 @@
---
title: "Authentication"
description: "How authentication works in Open SWE"
---
Open SWE implements a comprehensive authentication system that secures both client-side interactions and server-side operations. The authentication flow involves GitHub OAuth for user authentication, encrypted token handling, and multi-layered security for LangGraph server requests.
## GitHub OAuth Authentication
Open SWE uses GitHub OAuth for client-side authentication, providing secure access to user accounts and repository permissions.
### Authentication Flow
- **Unauthenticated users** are automatically redirected to GitHub OAuth login
- **Authenticated users** are redirected directly to the chat interface
- **Settings management** is available at `/settings` for updating GitHub authentication
<Note>
GitHub OAuth provides the foundation for all user interactions, enabling Open
SWE to access repositories and perform actions on behalf of authenticated
users.
</Note>
## LangGraph Server Authentication
All requests to the LangGraph server are authenticated through a sophisticated proxy system that ensures secure communication between the web interface and the agent backend.
### Proxy Route Architecture
The Next.js application includes a proxy route (`apps/web/src/app/api/[..._path]/route.ts`) that acts as an intermediary for all LangGraph server requests. This proxy uses the [`langgraph-nextjs-api-passthrough`](https://www.npmjs.com/package/langgraph-nextjs-api-passthrough) package to handle request forwarding with enhanced security.
<Tip>
The proxy route ensures that sensitive authentication tokens never reach the
client directly, maintaining security while enabling seamless communication
with the LangGraph server.
</Tip>
## Simple API Key (Bearer) Authentication
For local development and scripted access, the LangGraph server also supports a simple bearer token scheme. When a request includes an `Authorization: Bearer <token>` header, this path is used and the rest of the auth flow is skipped.
### Setup (development)
- **Generate a token** (32+ bytes, URL-safe):
- OpenSSL: `openssl rand -hex 32`
- **Add to your `.env` for the LangGraph server**:
```bash
API_BEARER_TOKEN=<your-generated-token>
```
- For multiple/rotation: use comma-separated tokens
```bash
API_BEARER_TOKENS=<token1>,<token2>,<token3>
```
Restart the LangGraph server after updating environment variables.
### Usage
```typescript
import { Client } from "@langchain/langgraph-sdk";
const client = new Client({
apiUrl: process.env.LANGGRAPH_API_URL,
defaultHeaders: {
authorization: `Bearer ${process.env.API_BEARER_TOKEN}`,
},
});
```
### Notes
- **Precedence**: If the `Authorization` header is present, bearer auth is used; otherwise the existing GitHub-based flow applies.
- **Rotation**: Add a new token to `API_BEARER_TOKENS`, deploy/restart, migrate clients, then remove old tokens and redeploy.
- **Scope**: All bearer tokens currently map to the same internal identity and permissions. If you need per-token identities/quotas, reach out to adjust the configuration.
### Header Injection System
The proxy route automatically injects the following encrypted headers into each request:
#### Authentication Headers
- **`x-github-access-token`** - User's GitHub access token for user-specific actions (creating issues, comments)
- **`x-github-installation-token`** - GitHub App installation token for app-level actions (commits, pull requests)
- **`x-github-installation-name`** - Installation name (username or organization name)
<Note>
All headers are prefixed with `x-` to ensure they're included in LangGraph run
configurations, making them accessible during execution while maintaining
security through encryption.
</Note>
### Token Encryption
Open SWE implements AES-256-GCM encryption for all secrets passed to the LangGraph server to prevent exposure in:
- LangSmith trace metadata
- Run configurations
- Potential unauthorized access scenarios
The encryption process uses the `SECRETS_ENCRYPTION_KEY` environment variable and includes:
- **Initialization Vector (IV)** for unique encryption per token
- **Authentication Tag** for data integrity verification
- **Base64 encoding** for safe transport
<Tip>
The same encryption key must be configured in both the web application and
LangGraph agent for proper token decryption.
</Tip>
## Authentication Middleware
The LangGraph server implements comprehensive authentication middleware (`apps/open-swe/src/security/auth.ts`) that validates all incoming requests.
### Webhook Authentication
The middleware first checks for GitHub webhook requests by detecting the `X-Hub-Signature-256` header:
1. **Signature verification** using the configured webhook secret
2. **Automatic authorization** for valid webhook signatures
3. **Separate user verification** in subsequent run creation requests
<Note>
Webhook authentication is handled separately from user authentication to
enable automated GitHub issue processing while maintaining security.
</Note>
### Standard Request Authentication
For non-webhook requests, the middleware validates:
#### Required Headers
- **Installation name** (`x-github-installation-name`)
- **Installation token** (`x-github-installation-token`)
Missing either header results in a 401 Unauthorized error.
#### User Verification Process
The middleware supports two authentication paths:
**Web Application Requests:**
- Uses encrypted GitHub access token (`x-github-access-token`)
- Verifies user identity through GitHub API
- Extracts user ID and login from token
**Webhook-Generated Requests:**
- Uses explicit user headers (`x-github-user-id`, `x-github-user-login`)
- Validates user ID and login against installation token
- Ensures webhook-created runs are properly attributed
### Identity and Permissions
Upon successful authentication, the middleware returns an identity object containing:
- **User ID** for resource ownership verification
- **Display name** (GitHub login)
- **Installation name** for repository context
- **Comprehensive permissions** for LangGraph operations
<Tip>
The user ID serves as the primary identifier for resource access control,
ensuring users can only access their own threads, runs, and assistants.
</Tip>
## Resource Access Control
Open SWE implements fine-grained access control for all LangGraph resources:
### Metadata-Based Ownership
- **Create operations** automatically add user ID to resource metadata
- **Read/Update/Delete operations** verify user ID matches resource owner
- **Search operations** filter results by user ownership
## Token Access During Execution
During LangGraph run execution, encrypted tokens are accessible through the run's configurable field:
1. **Token extraction** from run configuration
2. **Decryption** using the shared encryption key
3. **Action execution** (creating issues, making commits, etc.)
<Note>
This design ensures tokens remain encrypted in storage and traces while being
available for necessary GitHub operations during run execution.
</Note>
<Tip>
Always ensure the `SECRETS_ENCRYPTION_KEY` environment variable is identical
between your web application and LangGraph agent deployments.
</Tip>

View file

@ -1,69 +0,0 @@
---
title: "CI Configuration"
description: "How to configure your CI pipeline for Open SWE"
---
## Skip CI until last commit
Open SWE will push a commit after every change to the repository. This will cause your GitHub CI workflows to run for every commit, which is unnecessary.
To prevent this, Open SWE supports adding an environment variable which will append `[skip ci]` to the commit message. This can be used to make GitHub skip CI for that commit.
Below are instructions showing how to use this to skip creating Vercel CI preview deployments on every commit:
<Steps>
<Step title="Set the environment variable">
Set the environment variable `SKIP_CI_UNTIL_LAST_COMMIT` to `true` in Open SWE's environment variables:
`apps/open-swe/.env`
```bash
SKIP_CI_UNTIL_LAST_COMMIT="true"
```
</Step>
<Step title="Add custom 'Ignored Build Step' in Vercel">
Add a custom 'Ignored Build Step' in Vercel to skip CI when commit messages contain the string `[skip ci]`.
1. Navigate to your Vercel project dashboard
2. Go to **Settings** → **Git** tab
3. Scroll to **Ignored Build Step** section
4. Select **Custom** and add the following script:
![Vercel Custom Build Step Screenshot](/images/vercel_ignored_build_script_screenshot.png)
```bash
git log -1 --pretty=oneline --abbrev-commit | grep -w "\[skip ci\]" && exit 0 || exit 1
```
<Callout type="warning">
**Do not test this command locally** as it will close your terminal. Use the testing method below instead.
</Callout>
</Step>
<Step title="Test the configuration (optional)">
To verify your setup works without closing your terminal, use this safe testing command:
```bash
# Safe local testing - won't close your terminal
if git log -1 --pretty=oneline --abbrev-commit | grep -w "\[skip ci\]"; then
echo "Found [skip ci] - Vercel would skip this build"
else
echo "No [skip ci] found - Vercel would proceed with build"
fi
```
Test with both types of commits:
- Commits with `[skip ci]` should show "Vercel would skip this build"
- Commits without `[skip ci]` should show "Vercel would proceed with build"
</Step>
</Steps>
### How it works
1. **During task execution**: Open SWE includes `[skip ci]` in commit messages
2. **Vercel behavior**: Skips creating preview deployments for these commits (script exits with code 1)
3. **Task completion**: Open SWE pushes a final commit without `[skip ci]` to trigger deployment (script exits with code 0)
4. **Result**: Only one preview deployment per Open SWE task

View file

@ -1,108 +0,0 @@
---
title: "Custom Framework Configuration"
description: "How to configure and customize OpenSWE for custom libraries"
---
Open SWE includes support for LangGraph development through the **LangGraph Engineer** toggle. This feature adds framework-specific prompts and MCP tools for building LangGraph agents and workflows.
You can use a similar approach to build your own customization.
## LangGraph Engineer Configuration
### Setting Configuration in the UI
Enabling the **LangGraph Engineer** toggle in the main input area adds specific LangGraph prompts and allows LangGraph documentation access.
This sets the following configuration:
```typescript
const config = {
configurable: {
customFramework: true
}
}
```
## How Custom Framework Configuration Works
The configuration is passed to all agent nodes and used to conditionally include specialized prompts:
```typescript
// In prompt formatting functions
.replace(
"{CUSTOM_FRAMEWORK_PROMPT}",
shouldUseCustomFramework(config) ? CUSTOM_FRAMEWORK_PROMPT : "",
)
```
The `shouldUseCustomFramework(config)` function checks if `config.configurable?.customFramework === true`.
## Custom Framework Files
When `customFramework` is `true`, we inject a series of custom prompts and tools into the agent. The table below shows every place in the codebase that needs updating if you're customizing it for a framework other than LangGraph.
### Prompt Files
| Component | File | Prompt Constant | Description |
|-----------|------|-----------------|-------------|
| Planner | [prompt.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/graphs/planner/nodes/generate-message/prompt.ts) | `EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT` | Adds framework documentation access instructions |
| Planner | [prompt.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/graphs/planner/nodes/generate-message/prompt.ts) | `EXTERNAL_FRAMEWORK_PLAN_PROMPT` | Provides framework-specific planning requirements |
| Programmer | [prompt.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/graphs/programmer/nodes/generate-message/prompt.ts) | `CUSTOM_FRAMEWORK_PROMPT` | Comprehensive framework implementation patterns and best practices |
| Reviewer | [prompt.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/graphs/reviewer/nodes/generate-review-actions/prompt.ts) | `CUSTOM_FRAMEWORK_PROMPT` | Framework validation and testing requirements |
### Agent & UI Configuration Files
| Component | File | Description |
|-----------|------|-------------|
| Toggle Button | [default-view.tsx](https://github.com/langchain-ai/open-swe/tree/main/apps/web/src/components/v2/default-view.tsx) | Contains the LangGraph Engineer toggle button - modify this to add your own button or change the existing one |
| Framework Logic | [should-use-custom-framework.ts](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/utils/should-use-custom-framework.ts) | Logic function that determines when to enable custom framework features. Currently a boolean based on the UI toggle |
| Documentation | [constants.ts](https://github.com/langchain-ai/open-swe/tree/main/packages/shared/src/constants.ts) | MCP server configuration for framework documentation access |
## Customizing for Your Own Framework
Follow these steps to adapt Open SWE for your own framework:
### Modify Prompt Constants
1. **Update prompt files** from the table above to replace LangGraph-specific content:
- Replace `CUSTOM_FRAMEWORK_PROMPT` with your framework's patterns
- Update `EXTERNAL_FRAMEWORK_DOCUMENTATION_PROMPT` with your docs access instructions
- Modify `EXTERNAL_FRAMEWORK_PLAN_PROMPT` with your planning requirements
### Update UI Configuration
1. **Modify the toggle button** in [`default-view.tsx`](https://github.com/langchain-ai/open-swe/tree/main/apps/web/src/components/v2/default-view.tsx):
- Change button text from "LangGraph Engineer" to your framework name
- Update tooltip text and descriptions
2. **Optional**: Create a new toggle button for your framework while keeping the LangGraph one
### Configure Framework Logic
You can either:
- **Reuse the existing `customFramework: true` configuration** and modify the prompts to match your framework instead of LangGraph (no additional code changes needed)
- **Create a separate config variable** by adding a new field in your graph configuration (e.g., `yourFramework: true`) and adding the detection logic similar to [`should-use-custom-framework.ts`](https://github.com/langchain-ai/open-swe/tree/main/apps/open-swe/src/utils/should-use-custom-framework.ts)
### Add Documentation Access
1. **Configure MCP servers** in [`constants.ts`](https://github.com/langchain-ai/open-swe/tree/main/packages/shared/src/constants.ts):
```typescript
export const DEFAULT_MCP_SERVERS = {
"your-framework-docs-mcp": {
command: "uvx",
args: ["your-framework-docs-mcp"],
},
};
```
### Build and Test
1. **Rebuild the application**:
```bash
cd apps/open-swe
yarn build
```
2. **Test your customization** by enabling the toggle and verifying framework-specific prompts are used

View file

@ -1,305 +0,0 @@
---
title: "Development Setup"
description: "How to set up Open SWE for development"
---
This guide will walk you through setting up Open SWE for local development. You'll need to clone the repository, install dependencies, configure environment variables, create a GitHub App, and start the development servers.
<Note>
This setup is for development purposes. For production deployment, you'll need
to adjust URLs and create separate GitHub Apps for production use.
</Note>
## Prerequisites
Before starting, ensure you have the following installed:
- Node.js (version 18 or higher)
- Yarn (version 3.5.1 or higher)
- Git
## Setup Steps
<Steps>
<Step title="Clone the Repository">
Clone the Open SWE repository to your local machine:
```bash
git clone https://github.com/langchain-ai/open-swe.git
```
```bash
cd open-swe
```
</Step>
<Step title="Install Dependencies">
Install all dependencies using Yarn from the repository root:
```bash
yarn install
```
This will install dependencies for all packages in the monorepo workspace.
</Step>
<Step title="Set Up Environment Files">
Copy the environment example files and configure them:
```bash
# Copy web app environment file
cp apps/web/.env.example apps/web/.env
```
```bash
# Copy agent environment file
cp apps/open-swe/.env.example apps/open-swe/.env
```
### Web App Environment Variables (`apps/web/.env`)
Fill in the following variables (GitHub App values will be added in the next step):
```bash Environment Variables [expandable]
# API URLs for development
NEXT_PUBLIC_API_URL="http://localhost:3000/api"
LANGGRAPH_API_URL="http://localhost:2024"
# Encryption key for secrets (generate with: openssl rand -hex 32)
SECRETS_ENCRYPTION_KEY=""
# GitHub App OAuth settings (will be filled after creating GitHub App)
NEXT_PUBLIC_GITHUB_APP_CLIENT_ID=""
GITHUB_APP_CLIENT_SECRET=""
GITHUB_APP_REDIRECT_URI="http://localhost:3000/api/auth/github/callback"
# GitHub App details (will be filled after creating GitHub App)
GITHUB_APP_NAME="open-swe-dev" # this must match the name of your GitHub app, excluding spaces
GITHUB_APP_ID=""
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
...add your private key here...
-----END RSA PRIVATE KEY-----
"
# List of GitHub usernames that are allowed to use Open SWE without providing API keys
# This is only used in production. In development every user is an "allowed user".
# Must be a valid JSON array of strings.
NEXT_PUBLIC_ALLOWED_USERS_LIST='["your-github-username", "teammate-username"]'
```
### Agent Environment Variables (`apps/open-swe/.env`)
Configure the agent environment variables:
```bash Environment Variables [expandable]
# LangSmith tracing & LangGraph platform
LANGCHAIN_PROJECT="default"
LANGCHAIN_API_KEY="lsv2_pt_..." # Get from LangSmith
LANGCHAIN_TRACING_V2="true"
LANGCHAIN_TEST_TRACKING="false"
# LLM Provider Keys (at least one required)
ANTHROPIC_API_KEY="" # Recommended - default provider
OPENAI_API_KEY="" # Optional
GOOGLE_API_KEY="" # Optional
# Infrastructure
DAYTONA_API_KEY="" # Required. For cloud sandboxes
# Tools
FIRECRAWL_API_KEY="" # For URL content extraction
# GitHub App settings (same as web app)
GITHUB_APP_NAME="open-swe-dev" # this must match the name of your GitHub app, excluding spaces
GITHUB_APP_ID=""
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
...add your private key here...
-----END RSA PRIVATE KEY-----
"
GITHUB_WEBHOOK_SECRET="" # Will be generated in next step
# Server configuration
PORT="2024"
OPEN_SWE_APP_URL="http://localhost:3000"
SECRETS_ENCRYPTION_KEY="" # Must match web app value
# CI/CD. See the /labs/swe/setup/ci for more information
SKIP_CI_UNTIL_LAST_COMMIT="true"
# List of GitHub usernames that are allowed to use Open SWE without providing API keys
# This is only used in production. In development every user is an "allowed user".
# Must be a valid JSON array of strings.
NEXT_PUBLIC_ALLOWED_USERS_LIST='["your-github-username", "teammate-username"]'
```
If you don't set the `NEXT_PUBLIC_ALLOWED_USERS_LIST` environment variable, every user will be required to set their own LLM API keys to use the agent. Additionally, none of the webhook features (triggering new runs by adding labels to GitHub issues, tagging the agent in PR reviews, etc.) will work unless you include the username's of the GitHub users you want to allow access to in the `NEXT_PUBLIC_ALLOWED_USERS_LIST` environment variable (in both web and agent deployments).
<Tip>
Generate the `SECRETS_ENCRYPTION_KEY` using: `openssl rand -hex 32`. This key must be identical in both environment files.
</Tip>
</Step>
<Step title="Create GitHub App">
<Note>
You'll need to create a **GitHub App** (not a GitHub OAuth App). These are different types of applications with different capabilities. Consider creating separate GitHub apps for development and production environments.
</Note>
### Create the GitHub App
1. Go to [GitHub App creation page](https://github.com/settings/apps/new)
2. Fill in the basic information:
- **GitHub App name**: Your preferred name
- **Description**: Development instance of Open SWE coding agent
- **Homepage URL**: Your repository URL
- **Callback URL**: `http://localhost:3000/api/auth/github/callback`
### Configure OAuth Settings
- ✅ **Request user authorization (OAuth) during installation** - Allows users to log in to the web app
- ✅ **Redirect on update** - Redirects users back to your app after permission updates
- ❌ **Expire user authorization tokens** - Keep tokens from expiring
### Set Up Webhook
1. ✅ **Enable webhook**
2. **Webhook URL**: You'll need to use a tool like ngrok to expose your local server:
```bash
# Install ngrok if you haven't already
# Then expose your local LangGraph server
ngrok http 2024
```
Use the ngrok URL + `/webhooks/github` (e.g., `https://abc123.ngrok.io/webhooks/github`)
3. **Webhook secret**: Generate and save this value:
```bash
openssl rand -hex 32
```
Add this value to `GITHUB_WEBHOOK_SECRET` in `apps/open-swe/.env`
### Configure Permissions
**Repository permissions:**
- **Contents**: Read & Write
- **Issues**: Read & Write
- **Pull requests**: Read & Write
- **Metadata**: Read only (automatically enabled)
**Organization permissions:** None
**Account permissions:** None
This gif shows how/where to enable the permissions on the GitHub App:
<iframe
className="w-full aspect-video rounded-xl"
src="https://www.youtube.com/embed/rw6ddYmiTMo"
title="Permissions Walkthrough"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
### Subscribe to Events
- ✅ **Issues** - Required for webhook functionality
- ✅ **Pull request review** - Required for PR tagging functionality
- ✅ **Pull request review comment** - Required for PR tagging functionality
- ✅ **Issue comment** - Required for PR tagging functionality
### Installation Settings
- **Where can this GitHub App be installed?**:
- Choose "Any account" for broader testing
- Or "Only on this account" to limit to your repositories
### Complete App Creation
Click **Create GitHub App** to finish the setup.
### Collect App Credentials
After creating the app, collect the following values and add them to both environment files:
- **GITHUB_APP_NAME**: The name you chose
- **GITHUB_APP_ID**: Found in the "About" section (e.g., `12345678`)
- **NEXT_PUBLIC_GITHUB_APP_CLIENT_ID**: Found in the "About" section
- **GITHUB_APP_CLIENT_SECRET**:
1. Scroll to "Client secrets" section
2. Click "Generate new client secret"
3. Copy the generated value
- **GITHUB_APP_PRIVATE_KEY**:
1. Scroll to "Private keys" section
2. Click "Generate a private key"
3. Download the `.pem` file and copy its contents
4. Format as a single line with `\\n` for line breaks, or use the multiline format shown in the example
- **GITHUB_APP_REDIRECT_URI**: Should be `http://localhost:3000/api/auth/github/callback` for local development, or `https://your-production-url.com/api/auth/github/callback` for production.
- **GITHUB_WEBHOOK_SECRET**: Generate and save this value:
```bash
openssl rand -hex 32
```
Add this value to `GITHUB_WEBHOOK_SECRET` in `apps/open-swe/.env` and `apps/web/.env`.
Below are screenshots showing where in the GitHub App settings you can find the values for the environment variables.
<Accordion title="GitHub App OAuth Settings">
The following screenshots show where to find the following values:
- `GITHUB_APP_ID`
- `NEXT_PUBLIC_GITHUB_APP_CLIENT_ID`
- `GITHUB_APP_CLIENT_SECRET`
- `GITHUB_APP_REDIRECT_URI`
- `GITHUB_APP_PRIVATE_KEY`
![GitHub Secrets Screenshot 1](/images/gh_app_token_screenshot_1.png)
![GitHub Secrets Screenshot 2](/images/gh_app_token_screenshot_2.png)
![GitHub Secrets Screenshot 3](/images/gh_app_token_screenshot_3.png)
</Accordion>
<Tip>
Keep your GitHub App credentials secure and never commit them to version control. The `.env` files are already included in `.gitignore`.
</Tip>
</Step>
<Step title="Start Development Servers">
With all environment variables configured, start both development servers:
**Terminal 1 - Start the LangGraph Agent:**
```bash
# apps/open-swe
yarn dev
```
This starts the LangGraph server at `http://localhost:2024`
**Terminal 2 - Start the Web Application:**
```bash
# apps/web
yarn dev
```
This starts the Next.js web app at `http://localhost:3000`
<Note>
Both servers need to be running simultaneously for full functionality. The web app communicates with the LangGraph agent through API calls.
</Note>
</Step>
</Steps>
## Verification
Once both servers are running:
1. **Visit the web app**: Navigate to `http://localhost:3000`
2. **Test GitHub authentication**: Try logging in with your GitHub account
<Tip>
If you encounter issues, check the console logs in both terminal windows for
error messages. Common issues include missing environment variables or
incorrect GitHub App configuration.
</Tip>
## Next Steps
- Learn about [Authentication](/labs/swe/setup/authentication) to understand how the GitHub App integration works
- Explore [Usage](/labs/swe/usage/intro) to start using Open SWE for code changes
- Review the [Monorepo Structure](/labs/swe/setup/monorepo) for development best practices

View file

@ -1,73 +0,0 @@
---
title: "Introduction"
description: "How to set up Open SWE for development"
---
# Development Setup Overview
Welcome to the Open SWE development setup guide. This section will walk you through everything you need to know to get Open SWE running locally for development.
## Setup Sections
The setup process is organized into focused sections to help you get up and running efficiently:
<CardGroup cols={2}>
<Card
title="Development Setup"
icon="code"
href="/labs/swe/setup/development"
>
Complete guide to cloning the repository, installing dependencies,
configuring environment variables, and starting the development servers.
</Card>
<Card
title="Authentication"
icon="shield-check"
href="/labs/swe/setup/authentication"
>
Understanding the authentication flow, GitHub App configuration, and
security mechanisms used throughout Open SWE.
</Card>
</CardGroup>
## Technology Stack
Open SWE is built with modern technologies designed for scalability and developer experience:
### Core Technologies
- **TypeScript** - Strict type safety across the entire codebase
- **Yarn** - Package manager with workspace support (v3.5.1)
- **Turbo** - Monorepo build orchestration and task running
### Agent Infrastructure
- **LangGraph** - Multi-agent orchestration framework with three specialized graphs:
- Manager graph for user interaction orchestration
- Planner graph for execution plan creation
- Programmer graph for code change execution
- **Daytona** - Sandboxed development environments for safe code execution
### Web Application
- **Next.js** - React framework with App Router
- **Shadcn UI** - Component library built on Radix UI primitives
- **Tailwind CSS** - Utility-first CSS framework
### Documentation
- **Mintlify** - Documentation platform with MDX support
## Monorepo Structure
<Note>
Open SWE uses a Yarn workspace monorepo with three main applications and a
shared package for common utilities and types.
</Note>
- **`apps/open-swe`** - LangGraph agent application
- **`apps/web`** - Next.js web interface
- **`apps/docs`** - Mintlify documentation site
- **`packages/shared`** - Shared utilities, types, and constants
The monorepo is orchestrated by Turbo, which handles build dependencies and parallel task execution across packages.

View file

@ -1,152 +0,0 @@
---
title: "Monorepo"
description: "How the Open SWE monorepo is configured"
---
Open SWE is organized as a Yarn workspace monorepo with Turbo build orchestration, designed to efficiently manage multiple applications and shared code. This structure enables code reuse, consistent tooling, and streamlined development workflows.
## Repository Structure
The monorepo is organized into two main directories:
### Applications (`apps/`)
**`apps/open-swe`** - LangGraph Agent Application
- Contains the core LangGraph agent implementation with TypeScript
- Includes three specialized graphs: manager, planner, and programmer
- Handles GitHub webhook integration and LLM interactions
**`apps/web`** - Next.js Web Interface
- React frontend with Next.js framework
- Uses Shadcn UI components (Radix UI) with Tailwind CSS
- Provides the user interface for interacting with the LangGraph agent
- Includes authentication and proxy routes for secure communication
**`apps/docs`** - Mintlify Documentation
- Contains this documentation site built with Mintlify
- Provides comprehensive setup and usage guides
- Includes API documentation and development resources
### Packages (`packages/`)
**`packages/shared`** - Common Utilities Package
- Centralized location for shared types, constants, and utilities
- Used by both the agent and web applications
- Exports modules via `@openswe/shared` namespace
- Contains crypto utilities, GraphState types, and Open SWE specific modules
<Note>
The shared package must be built before other packages can import from it. Any
code used by both the agent and web app should be placed here to avoid
duplication.
</Note>
## Turbo Orchestration
The monorepo uses [Turbo](https://turbo.build/) for efficient build orchestration and task management:
### Task Dependencies
```json
{
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"lint": {
"dependsOn": ["^lint"]
}
}
```
The `^build` dependency ensures that shared packages are built before dependent packages, maintaining proper build order across the monorepo.
### Available Scripts
Run these commands from the repository root:
- `yarn build` - Build all packages in dependency order
- `yarn lint` - Run linting across all packages
- `yarn format` - Format code using Prettier
## Dependency Management Best Practices
<Steps>
<Step title="Install dependencies in specific packages">
Always install dependencies in the specific package where they're used, never in the root `package.json` unless adding a resolution.
```bash
# Correct - install in specific package
cd apps/web
yarn add some-package
# Incorrect - don't install in root
yarn add some-package
```
</Step>
<Step title="Use resolutions for shared dependencies">
When multiple packages need the same dependency, add a resolution to the root `package.json` to ensure version consistency.
```json
{
"resolutions": {
"@langchain/langgraph-sdk": "^0.0.95",
"@langchain/core": "^0.3.58"
}
}
```
</Step>
<Step title="Build shared packages after changes">
Run `yarn build` from the root when making changes to `packages/shared` to make them available to other packages.
```bash
# After modifying packages/shared
yarn build
```
</Step>
</Steps>
## Postinstall Hook Requirements
The `apps/open-swe` package includes a critical `postinstall` hook:
```json
{
"scripts": {
"postinstall": "turbo build"
}
}
```
<Note>
This postinstall hook is **required** for LangGraph Platform deployment. Since
Open SWE is a monorepo and the agent requires access to built files from the
shared package, we must run the build process before starting the LangGraph
server.
</Note>
### Why This Matters
1. **Deployment Compatibility**: The LangGraph Platform needs all dependencies built and available
2. **Shared Package Access**: The agent imports utilities from `@openswe/shared` which must be compiled
3. **Build Order**: Ensures the shared package is built before the agent attempts to use it
<Tip>
If you encounter import errors related to the shared package during
development, run `yarn build` from the root to ensure all packages are
properly built and linked.
</Tip>
## Workspace Configuration
The monorepo uses Yarn 3.5.1 with the following configuration:
- **Node Linker**: `node-modules` for compatibility
- **Workspaces**: Automatically discovers packages in `apps/*` and `packages/*`
- **Package Manager**: Enforced via `packageManager` field in `package.json`

View file

@ -1,85 +0,0 @@
---
title: Best Practices
description: Guidelines for effective use of Open SWE
---
# Best Practices
Follow these guidelines to get the best results from Open SWE.
## Prompting Tips
### Be Clear and Direct
- Include specific file paths and function names in your requests. Open SWE preforms best when its given a clear starting point.
- Provide concrete examples of what you want to achieve. Describe the end state you want to reach so Open SWE knows what it's working towards.
### Create Custom Rules
Create an `AGENTS.md` file in your repository root to provide project-specific context. This helps Open SWE understand your codebase conventions and requirements.
<Tip>
See the [Custom Rules](/labs/swe/usage/custom-rules) page for detailed guidance on
setting up your `AGENTS.md` file.
</Tip>
### Keep Tasks Well-Scoped
- Focus on one specific feature or fix per request
- Break large changes into smaller, manageable tasks
- Avoid combining multiple unrelated changes in a single request
### Avoid Multiple Tasks
Submit separate requests for different features or fixes. This allows Open SWE to:
- Generate more focused plans
- Provide better error handling
- Make changes easier to review
## Model Selection
- **Claude Opus 4.5 (Default)**: The default model for planning, writing code, and reviewing changes. This model offers the best balance of performance, speed and cost.
- **Claude Opus 4.1**: A larger, more powerful model for difficult, or open-ended tasks. Opus 4.1 is more expensive and slower, but will provide better results for complex tasks.
### Avoid Other Models
Although Open SWE allows you to select any model from Anthropic, OpenAI and Google, its prompts are tuned specifically for Anthropic models, and other providers will not preform as well.
## Mode Selection
### `open-swe` vs `open-swe-max`
**`open-swe`**: Uses Claude Opus 4.5
- Suitable for most development tasks
- Faster execution
- Cost-effective
**`open-swe-max`** (Deprecated): Uses Claude Opus 4.1 (only for the planning and code writing agents)
- **This label is deprecated** - use `open-swe` instead, which now uses Claude Opus 4.5 by default
- The max label uses an outdated model configuration
- For complex tasks, the default `open-swe` label with Opus 4.5 provides better performance
### Auto vs Manual Labels
**Auto Mode (`-auto` labels)**
It's recommended to use the auto mode for most tasks. Open SWE is very good at planning, and in most cases it does not need a manual review before execution.
If you're running Open SWE against an open-ended or very complex task, you may want to use manual mode to review the plan before execution.
## Label Reference
- `open-swe`: Manual mode with Opus 4.5
- `open-swe-auto`: Auto mode with Opus 4.5
- `open-swe-max`: **[DEPRECATED]** Manual mode with Opus 4.1 - use `open-swe` instead
- `open-swe-max-auto`: **[DEPRECATED]** Auto mode with Opus 4.1 - use `open-swe-auto` instead
<Note>
In development environments, append `-dev` to all labels (e.g.,
`open-swe-dev`, `open-swe-auto-dev`).
</Note>

View file

@ -1,165 +0,0 @@
---
title: Custom Rules
description: Configure Open SWE with project-specific rules using `AGENTS.md`
---
# Custom Rules
Custom rules allow you to provide project-specific context and guidelines to Open SWE through a markdown file in your repository root.
## Getting Started
The easiest way to get started is to have Open SWE itself write this file for you! The Open SWE UI provides a "Generate `AGENTS.md`" quick action which will insert a predefined prompt into the input. This prompt has been designed to generate a well-formatted `AGENTS.md` file from the agent.
![Quick Action Generate AGENTS.md](/images/quick_action_generate_agents_md.png)
## What is `AGENTS.md`?
`AGENTS.md` is a markdown file that tells Open SWE about your project's:
- Coding standards and conventions
- Repository structure and architecture
- Dependencies and installation procedures
- Testing frameworks and practices
- Pull request formatting requirements
## Why Use Custom Rules?
Custom rules help Open SWE:
- Follow your project's specific conventions
- Understand your codebase architecture
- Use the correct package managers and tools
- Write tests that match your testing patterns
- Generate properly formatted pull requests
Without custom rules, Open SWE uses generic best practices that may not align with your project.
## Supported Sections
### `<general_rules>`
Project-specific coding standards and conventions:
- Package manager preferences (e.g., "Always use Yarn")
- Code style requirements
- Import/export patterns
- Architectural guidelines
- Common scripts, and how to run them
Example: "Run `yarn test` to run tests", or "Run `yarn build` to build the project"
### `<repository_structure>`
Description of your codebase organization:
- Monorepo vs single package structure
- Key directories and their purposes
- Package relationships and dependencies
- Build system configuration
Include context about where/how to create new files, apps, packages, etc. in this section.
Example: "When creating new packages, place them inside the `packages` directory."
### `<dependencies_and_installation>`
Package management and setup instructions:
- Package manager commands
- Installation procedures
- Key dependencies and their purposes
- Workspace configuration details
This section should include information about the package manager(s) used in the project, and how/where to install dependencies.
Example: "Always install dependencies inside the specific app/package where they're used, never in the root `package.json` unless adding a resolution."
### `<testing_instructions>`
Testing framework and practices:
- Test runner configuration (Jest, Vitest, etc.)
- Test file naming conventions
- How to run different test types
- Testing best practices for your project (e.g. what types of tests to write)
Example: "Always run `yarn test` after making changes."
### `<pull_request_formatting>`
PR creation and formatting guidelines:
- Title and description templates
- Required sections or checklists
- Review process requirements
- Linking conventions
Example: "The pull request body should include a `Testing Steps` section which includes bullet points describing how to test the changes."
## File Names
Open SWE reads custom rules from these files (in order of precedence):
1. `AGENTS.md` (recommended)
2. `AGENT.md`
3. `CLAUDE.md`
4. `CURSOR.md`
<Note>
Use `AGENTS.md` as the standard filename for consistency across projects.
</Note>
## Missing Sections
If your custom rules file doesn't include XML section tags, the entire file content will be passed to the prompt inside a custom rules section.
## Formatting Example
If your custom rules file does include the proper XML section tags, only the content from inside the known tags will be passed to the system prompt.
Known tags:
- `<general_rules>`
- `<repository_structure>`
- `<dependencies_and_installation>`
- `<testing_instructions>`
- `<pull_request_formatting>`
Each tag _must_ contain a proper closing tag. If a tag is missing a closing tag, the entire file content will be passed to the prompt inside a custom rules section.
Here is an example showing what your `AGENTS.md` file should look like:
```markdown
<general_rules>
- Always use Yarn as the package manager
- Follow strict TypeScript practices
- Use ESLint and Prettier for code quality
</general_rules>
<repository_structure>
This is a Yarn workspace monorepo with three main packages:
- apps/web: Next.js frontend
- apps/api: Express backend
- packages/shared: Common utilities
</repository_structure>
<dependencies_and_installation>
Run `yarn install` from the repository root to install all dependencies.
Key dependencies include React 18, TypeScript, and Jest for testing.
</dependencies_and_installation>
<testing_instructions>
- Run `yarn test` for unit tests
- Run `yarn test:e2e` for end-to-end tests
- Test files use `.test.ts` extension
- Use Jest with React Testing Library
</testing_instructions>
<pull_request_formatting>
PR titles should follow: "feat: description" or "fix: description"
Include a brief description and link any related issues.
</pull_request_formatting>
```

View file

@ -1,106 +0,0 @@
---
title: Examples
description: A collection of example tasks for Open SWE
---
If you want to try out Open SWE, but aren't sure where to start, here are a few example tasks you can try out.
<Steps>
<Step title="Clone the TypeScript template">
The examples below are all for TypeScript tasks. For these, you should clone an empty TypeScript template repository:
[bracesproul/typescript-template](https://github.com/bracesproul/typescript-template)
Visit the GitHub UI, and click `Use this template` to create a new repository based on this template.
</Step>
<Step title="Give Open SWE access to your repository">
If you did not give Open SWE to all of your repositories when setting it up the first time, you'll need to give Open SWE access to this repository.
1. Visit the settings page on Open SWE: [swe.langchain.com/settings](https://swe.langchain.com/settings)
2. To the right of your user inside the `Current User` section, click the `+` button. This will redirect you to GitHub where you can authorize Open SWE to access your repositories.
3. Select the new TypeScript template you just created.
Once done, it will redirect you back to Open SWE. Once here, find the new repository under the repo dropdown above the chat input.
</Step>
<Step title="Submit one of the example tasks">
After cloning the template and giving Open SWE access to it, you can submit one of the example tasks!
</Step>
</Steps>
Below are a series of examples you can try out:
<Accordion title="RESTful 'Books' micro-service">
```txt
Create an Express-based API exposing CRUD endpoints for `/books`.
Include request validation with `zod`, proper HTTP status codes, and an
in-memory repository layer that can later be swapped for a database. Add Jest
tests covering happy-path and a 'missing ISBN' error case.
```
</Accordion>
<Accordion title="WebSocket live counter">
```txt
Add a small WebSocket server (using `ws`) that tracks how many clients
are currently connected and broadcasts the updated count every 5 seconds.
Expose a health-check HTTP route returning the same value for monitoring
tools. Provide a minimal HTML demo page that shows the live number.
```
</Accordion>
<Accordion title="GitHub issue sync CLI">
```txt
Implement a CLI (`src/sync-issues.ts`) that reads a `config.json` with
one or more GitHub repos, fetches their open issues via the GitHub REST API,
and writes a local `issues.{owner}.{repo}.csv`. Use `yargs` for parsing and
include a `--since YYYY-MM-DD` flag to filter by creation date.
```
</Accordion>
<Accordion title="JWT + refresh-token auth layer">
```txt
Build a middleware that issues short-lived access tokens and long-lived
refresh tokens. Store refresh tokens in a signed, HttpOnly cookie and expose
`/auth/refresh` to rotate them. Protect a sample route (`/profile`) and supply
Postman collections for login and refresh flows.
```
</Accordion>
<Accordion title="Pluggable caching service">
```txt
Design a generic cache interface with `get`, `set`, and `invalidate`
methods, then provide two adapters: an in-memory `Map` implementation and a
Redis adapter (mock Redis with `ioredis-mock` for tests). Demonstrate
hot-swapping the adapter via an environment variable without code changes.
```
</Accordion>
<Accordion title="GraphQL wrapper over REST API">
```txt
Stand up an Apollo Server that federates data from the public
JSONPlaceholder `/users` and `/posts` endpoints. Expose a `user(id)` query
returning the user plus their posts in a single round-trip. Add schema-driven
TypeScript types (`codegen.yml`) and example queries in `README.md`.
```
</Accordion>
<Accordion title="Image upload to S3 with signed URLs">
```txt
Create an endpoint that returns a time-limited pre-signed PUT URL for
an S3 bucket (use `@aws-sdk/client-s3`). Include a small React demo (Vite)
that lets a user pick an image and upload it directly. Validate MIME type on
the server before signing.
```
</Accordion>
<Accordion title="Locale-aware date utility library">
```txt
Publish an internal `/src/date` module that formats, parses, and
time-zone-converts dates using `luxon`. Support at least 'en-US', 'de-DE', and
'ja-JP'. Provide type-safe wrappers, exhaustive unit tests, and a benchmark
script comparing it to native `Intl.DateTimeFormat`.
```
</Accordion>

View file

@ -1,132 +0,0 @@
---
title: "From Github"
description: "How to use Open SWE from Github"
---
<Warning>
GitHub webhooks are _not_ available via the demo application. To use GitHub webhooks, you must set up your own instance of Open SWE following the [development setup guide](/labs/swe/setup/development).
</Warning>
# GitHub Webhook Integration
Open SWE integrates seamlessly with GitHub through webhooks, allowing you to trigger automated code changes directly from GitHub issues. This provides a streamlined workflow where you can request code changes by simply adding labels to issues in repositories where Open SWE is installed.
## Triggering Runs with Labels
Open SWE monitors GitHub issues for specific labels that trigger automated runs. When you add one of these labels to an issue, Open SWE will automatically create a new run to process your request.
### Label Types
Open SWE supports three types of labels that control how the agent operates:
**Manual Mode (`open-swe`)**
- Requires manual approval of the generated plan before code execution
- Gives you full control over what changes will be made
- Ideal for complex or sensitive changes where you want to review the approach first
**Auto Mode (`open-swe-auto`)**
- Automatically approves and executes the generated plan
- Provides faster turnaround for straightforward requests
- Best for simple changes or when you trust the agent to proceed autonomously
**Max Mode (`open-swe-max` and `open-swe-max-auto`) - DEPRECATED**
<Warning>
These labels are **deprecated**. Please use `open-swe` or `open-swe-auto` instead, which now use Claude Opus 4.5 by default for better performance.
</Warning>
- Uses Claude Opus 4.1 for both planning and programming tasks (outdated model configuration)
- The standard `open-swe` labels now provide better performance with Claude Opus 4.5
- These labels are maintained for backward compatibility but will be removed in a future release
<Note>
In development environments, the labels are `open-swe-dev`,
`open-swe-auto-dev`, `open-swe-max-dev`, and `open-swe-max-auto-dev`
respectively. The system automatically uses the appropriate labels based on
the `NODE_ENV` environment variable.
</Note>
## Automatic Run Creation
When you add a supported label to a GitHub issue, Open SWE's webhook handler automatically:
1. **Validates the request** - Verifies webhook signatures and authentication
2. **Extracts issue context** - Captures the issue title, description, and metadata
3. **Creates a new thread** - Generates a unique thread ID for the conversation
4. **Starts the Manager Graph** - Initiates the agent workflow with the issue content
5. **Configures execution mode** - Sets auto-accept based on the label type used
The entire process happens within seconds of adding the label, providing immediate feedback through issue comments.
## Issue Comments and Run Links
Once a run is created, Open SWE automatically posts a comment on the triggering issue to confirm processing has started. This comment includes:
- **Status confirmation** - "🤖 Open SWE has been triggered for this issue. Processing..."
- **Run link** - Direct URL to view the run in the Open SWE web interface
- **Access restriction notice** - Clarifies that only the issue creator can access the run
- **Development metadata** - Run ID and thread ID for debugging (in a collapsible section)
<Tip>
The run link allows you to monitor progress in real-time, view the generated
plan, and interact with the agent if needed. You can switch between manual and
auto mode even after the run has started.
</Tip>
## User Access Restrictions
Open SWE implements strict access controls to ensure security and privacy:
### Issue Creator Access
- **Only the user who created the issue** can access the generated run URL
- This prevents unauthorized users from viewing or modifying runs triggered by others
- Access is enforced through GitHub authentication and user verification
### Repository Permissions
- Open SWE respects GitHub's repository permissions
- Users must have appropriate access to the repository to trigger runs
- The GitHub App installation determines which repositories can use Open SWE
<Note>
If you need to share access to a run with team members, you can do so through
the Open SWE web interface after the run is created, or by having team members
with repository access create their own issues.
</Note>
## Pull Request Integration
When Open SWE successfully completes code changes, it automatically creates pull requests that are linked back to the original issue:
### Automatic PR Creation
- **Generated after plan execution** - PRs are created once the Programmer Graph completes its work
- **Linked to triggering issue** - PRs reference the original issue in their description
- **Preserves commit history** - All intermediate commits are maintained for transparency
### Issue Resolution
- **Automatic closure** - When the generated PR is merged, GitHub automatically closes the linked issue
- **Clear audit trail** - The connection between issue, run, and PR provides complete traceability
- **Status updates** - Issue comments track the progress from request to completion
<Tip>
You can review the generated PR before merging, even in auto mode. The
auto-accept setting only applies to plan approval, not PR merging, giving you
final control over what code enters your repository.
</Tip>
## Getting Started
To start using Open SWE with webhooks:
1. **Ensure Open SWE is installed** on your repository as a GitHub App
2. **Create a detailed issue** describing the changes you want
3. **Add the appropriate label** (`open-swe` for manual mode or `open-swe-auto` for automatic mode)
4. **Monitor the issue comments** for the run link and status updates
5. **Review and merge the PR** when Open SWE completes the changes
For setup instructions, see the [Development Setup](/labs/swe/setup/development) guide.

View file

@ -1,49 +0,0 @@
---
title: "Introduction"
description: "How to use Open SWE"
---
# Using Open SWE
Open SWE provides two primary ways to interact with the coding agent, each designed for different workflows and use cases. Whether you prefer direct interaction through a web interface or automated triggers through GitHub webhooks, Open SWE adapts to your development process.
## Usage Methods
<CardGroup cols={2}>
<Card title="Web Interface" icon="browser" href="/labs/swe/usage/ui">
Interactive chat interface with manual and auto modes for direct agent
communication
</Card>
<Card title="GitHub Webhooks" icon="webhook" href="/labs/swe/usage/github">
Automated triggers through GitHub issue labels for seamless repository
integration
</Card>
</CardGroup>
## Getting Started
### Try the Demo
You can explore Open SWE's capabilities using our hosted demo at [swe.langchain.com](https://swe.langchain.com).
<Note>
The demo application requires you to provide your own LLM API keys. For
production use or development, we recommend setting up your own instance
following our [development setup guide](/labs/swe/setup/development).
</Note>
### Choose Your Workflow
- **Web Interface**: Best for interactive development, experimentation, and when you want direct control over the agent's planning and execution process
- **GitHub Webhooks**: Ideal for automated workflows, issue-driven development, and team collaboration where coding tasks are tracked through GitHub issues
## What's Next?
<Tip>
Start with the [Web Interface guide](/labs/swe/usage/ui) to understand Open
SWE's core capabilities, then explore [GitHub
Webhooks](/labs/swe/usage/github) for automated integration into your
development workflow.
</Tip>
Both usage methods leverage the same underlying LangGraph agent architecture with specialized graphs for planning, programming, and management, ensuring consistent behavior regardless of how you interact with Open SWE.

View file

@ -1,185 +0,0 @@
---
title: "PR Tagging"
description: "How to trigger Open SWE runs by tagging it in PR comments and reviews"
---
<Warning>
PR tagging functionality is **not available** on the demo application at [swe.langchain.com](https://swe.langchain.com). To use PR tagging, you must self-host Open SWE following the [development setup guide](/labs/swe/setup/development).
</Warning>
# PR Tagging Integration
Open SWE supports triggering automated runs directly from pull request interactions by tagging the agent in comments and reviews. This feature provides a seamless way to request code changes, ask questions, or resolve review feedback without leaving the GitHub interface.
## How PR Tagging Works
You can trigger Open SWE by mentioning `@open-swe` in any of the following contexts:
### PR Reviews
- **Leave a review** on a pull request and tag `@open-swe` in the review body
- Open SWE will process the entire review and resolve all comments within it
- Ideal for comprehensive feedback that requires multiple changes
### Review Comments
- **Tag `@open-swe`** in specific line-by-line review comments
- Open SWE will focus on resolving that particular comment
- Perfect for targeted fixes on specific code sections
### General PR Comments
- **Comment on the PR** and tag `@open-swe` to request general changes
- Open SWE will implement the requested modifications
- Best for broader changes or new feature requests
<Note>
Each time you tag `@open-swe`, it creates a **new independent run**. Multiple tags in the same comment or across different comments will result in separate runs being executed.
</Note>
## Context and Security Considerations
<Warning>
**Security Notice**: When you tag Open SWE in a PR, it will have access to **all comments, reviews, and review comments** on that pull request, along with any linked issues. Be aware of potential prompt injection attacks and avoid including sensitive information in PR discussions, or linked issues, when using this feature.
</Warning>
### What Context is Included
When Open SWE processes a PR tagging request, it automatically gathers:
- **All PR comments** (general discussion comments)
- **All reviews** (including their associated review comments)
- **All review comments** (line-by-line feedback)
- **Linked issues** referenced in the PR description (issue title and description)
### Linked Issues Detection
Open SWE automatically detects and includes linked issues using these keywords in the PR description:
- `fixes #123` or `fix #123`
- `closes #456` or `close #456`
- `resolves #789` or `resolve #789`
When these patterns are found, Open SWE will include the issue title and description as additional context for better understanding of the requirements.
## How Open SWE Responds
Open SWE can handle both **code changes** and **questions** depending on your request:
### Code Changes
When you request code modifications, Open SWE will:
1. **Create a new branch** pointing to the original PR branch
2. **Implement the requested changes** on this new branch
3. **Create a new pull request** with the changes
4. **Link the new PR** back to the original PR for easy review
This workflow ensures your original PR remains unchanged while allowing you to review the proposed modifications separately.
### Questions and Investigations
Open SWE can also respond to questions that don't require code changes:
- Ask about code functionality or architecture decisions
- Request explanations of existing implementations
- Get suggestions for alternative approaches
### Response Types by Comment Location
**Review Comments (line-by-line)**
- Open SWE replies **directly to the review comment**
- Response appears in the same conversation thread
**Review Messages (overall review)**
- Open SWE creates a **new comment** on the PR
- Quotes the original review and tags the reviewer
- Provides a comprehensive response to the review
**General PR Comments**
- Open SWE creates a **new comment** on the PR
- Quotes the original comment and tags the commenter
- Addresses the specific request or question
## Getting Started
To start using PR tagging:
1. **Ensure Open SWE is properly configured** for your repository (see self-hosting section below)
2. **Navigate to any pull request** in a repository where Open SWE is installed
3. **Leave a comment, review, or review comment** describing what you want
4. **Tag `@open-swe`** anywhere in your message
5. **Wait for Open SWE to respond** - it will reply immediately and begin processing
### Example Usage
```markdown
@open-swe can you refactor this function to use async/await instead of promises?
```
```markdown
This code looks good overall, but I think we need better error handling.
@open-swe please add try-catch blocks and proper error logging.
```
```markdown
@open-swe what's the performance impact of this approach compared to the previous implementation?
```
## Self-Hosting Configuration
To use PR tagging with your self-hosted Open SWE instance, you'll need to configure a custom trigger username:
### Step 1: Create a GitHub User Account
Create a dedicated GitHub user account that will serve as your tagging trigger:
- Choose a username that's easy to remember (e.g., matching your GitHub App name)
- This account doesn't need any special permissions - it's just used for tagging
### Step 2: Update the Trigger Function
Modify the `mentionsGitHubUserForTrigger` function in `apps/open-swe/src/routes/github/utils.ts`:
```typescript
export function mentionsGitHubUserForTrigger(commentBody: string): boolean {
return /@your-custom-username\b/.test(commentBody);
}
```
Replace `your-custom-username` with the GitHub username you created.
### Step 3: Set Environment Variable
Add the trigger username to your environment configuration in `apps/open-swe/.env`:
```bash
GITHUB_TRIGGER_USERNAME="your-custom-username"
```
This should match the username from Step 1 (without the @ symbol).
### Step 4: Update Allowed Users
Add the GitHub usernames who should be allowed to trigger runs to the `NEXT_PUBLIC_ALLOWED_USERS_LIST` environment variable in your agent's deployment environment (include in the production deployments for both the web app and agent to take advantage of the allowed users functionality).
```bash
NEXT_PUBLIC_ALLOWED_USERS_LIST='["your-github-username", "teammate-username"]'
```
When running locally, every user is an "allowed user". It's also recommended to set this environment variable in your web app's production environment variables so your users won't need to set their own API keys when invoking Open SWE via the web app.
<Tip>
After making these configuration changes, restart your Open SWE instance to ensure the new settings take effect. You can then test the functionality by tagging your custom username in a PR comment.
</Tip>
## Best Practices
- **Be specific** in your requests to get better results
- **Use separate tags** for different types of changes to keep runs focused
- **Review generated PRs** before merging to ensure quality
- **Be mindful of context** - avoid sensitive information in PR discussions
- **Test with simple requests** first when setting up self-hosting
## Troubleshooting
If PR tagging isn't working:
1. **Check GitHub App permissions** - Ensure you have the required event subscriptions enabled
2. **Verify environment variables** - Confirm `GITHUB_TRIGGER_USERNAME` is set correctly
3. **Review allowed users** - Make sure your GitHub username is in the `ALLOWED_USERS` list
4. **Check webhook configuration** - Ensure webhooks are properly configured and receiving events
5. **Monitor logs** - Check the Open SWE logs for any error messages or debugging information
For additional help, refer to the [development setup guide](/labs/swe/setup/development) or check the GitHub repository for troubleshooting resources.

View file

@ -1,224 +0,0 @@
---
title: "From the UI"
description: "How to use Open SWE from the UI"
---
Open SWE provides a powerful web interface that allows you to interact with the coding agent through a chat-like experience. The UI supports both automated and manual workflows, giving you control over how the agent processes your requests.
## Auto vs Manual Mode
The UI offers two distinct modes for handling your coding requests:
<Note>
You can toggle between auto and manual mode using the checklist icon in the
main input area.
</Note>
### Auto Mode
When **Auto Mode** is enabled (checklist icon is highlighted):
- Plans are automatically accepted and executed without user intervention
- The agent proceeds directly from planning to implementation
- Ideal for straightforward requests where you trust the agent's planning
### Manual Mode
When **Manual Mode** is active (checklist icon is not highlighted):
- You must manually review and accept proposed plans before execution
- Provides opportunity to edit, modify, or reject plans
- Allows for more control over the implementation approach
<Tip>
Start with manual mode for important changes to review the agent's approach
before execution.
</Tip>
## Manager Agent Capabilities
The Manager agent acts as the central orchestrator, intelligently routing your messages and managing the overall workflow. Here's what the Manager can and cannot do:
### What the manager _can_ do
<Steps>
<Step title="Respond to User Messages">
Provides contextual responses and status updates about ongoing operations.
You can ask the manager what it can do, what the status of different agents
is, etc.
</Step>
<Step title="Create New Planning Runs">
Initiates new planning sessions when you submit coding requests. If your
message contains a coding request, the manager will create a new planning
session & corresponding GitHub issue.
</Step>
<Step title="Send Messages to Active Planning Runs">
Forwards additional context or requirements to running planner sessions. If
you send a message while the planner is running, the manager can forward
that message to the planner, without interrupting its flow.
</Step>
<Step title="Resume Interrupted Planners">
Resumes planner sessions that were paused for the user to accept or reject
plans. If the planner has paused for plan acceptance, you can message the
manager with feedback about the plan. This will be forwarded to the planner,
and it will continue its flow based on your feedback.
</Step>
<Step title="Send Messages to Active Programmer Runs">
Provides additional context or instructions to running implementation
sessions. If you send a message while the programmer is running, the manager
can forward that message to the programmer, without interrupting its flow.
</Step>
<Step title="Create new tasks">
Creates new tasks, independent of the current request. If you send a message
to the manager with a request for a task that is unrelated to the current
request, or can be implemented in parallel via a different session, the
manager will create a new GitHub issue, and initiate a new planning session
for that issue.
</Step>
</Steps>
### What the Manager _cannot_ Do
<Note>
The Manager has several important limitations to ensure proper workflow
control:
</Note>
- **Cannot create new Programmer runs** - This only happens after plan acceptance or in auto mode.
- **Cannot stop running Planner/Programmer sessions** - To stop a session, you must click the cancel button in the UI.
- **Cannot re-plan while Programmer is running** - Once the programmer session has started, you can not go back to the planner. However, you can send a message to the manager which can be forwarded to the programmer.
- **Cannot open Pull Requests directly** - PRs are created automatically after Programmer completion (and changes are automatically committed anytime a file is modified).
- **Cannot accept plans in manual mode** - You must manually click accept for plan approval.
## Message Handling and Routing
The Manager intelligently classifies your messages and routes them to the appropriate component:
### Message Classification
When you send a message, the Manager analyzes:
- Current status of Planner and Programmer graphs
- Content and intent of your message
- Existing conversation context
- Active plans and tasks
### Routing Options
Based on the analysis, messages are routed to:
- **Start Planner**: For new coding requests requiring planning
- **Update Planner**: To add context to active planning sessions
- **Resume Planner**: To continue interrupted planning with new information
- **Update Programmer**: To provide context to active implementation sessions
- **Create New Issue**: For independent requests that should be separate GitHub issues
- **No Operation**: For messages that don't require specific routing
## Planning Runs
Planning runs are handled by the Planner graph, which creates detailed execution plans for your requests.
### Planning Process
<Steps>
<Step title="Context Gathering">
The Planner analyzes your repository and gathers relevant context about the
codebase
</Step>
<Step title="Plan Generation">
Creates a structured plan with specific, actionable steps
</Step>
<Step title="Plan Presentation">
Presents the proposed plan for review (in manual mode) or automatic
acceptance (in auto mode)
</Step>
</Steps>
### Plan Interruption
Plans are presented as interruptions that require user response:
- **Manual Mode**: You must explicitly accept or reject the plan
- **Auto Mode**: Plans are automatically accepted and execution begins
- **Plan Editing**: You can modify proposed plans before acceptance
- **Feedback**: You can provide feedback to the manager if you want the plan to be changed in some way
### Automatic Approval
When auto mode is enabled, plans are automatically accepted and implementation begins immediately after plan generation.
## Programmer Runs
Once a plan is accepted, the Programmer graph executes the implementation.
### Programming Process
<Steps>
<Step title="Plan Execution">
Works through each step of the accepted plan systematically
</Step>
<Step title="Code Implementation">
Makes actual changes to files in your repository
</Step>
<Step title="Progress Tracking">
Updates plan status and provides summaries of completed work
</Step>
<Step title="Pull Request Creation">
Automatically opens a PR with all changes when implementation is complete
</Step>
</Steps>
<Note>
The Programmer automatically commits changes after each step, ensuring your
work is preserved even if the session is interrupted.
</Note>
## LangGraph Engineer Toggle
The UI includes a **LangGraph Engineer** toggle button that optimizes the agent's performance when working with LangGraph code:
<Note>
The LangGraph Engineer toggle is located in the main input area, represented by the Open SWE icon.
</Note>
### When to Enable LangGraph Engineer
Enable this toggle when your request involves creating or modifying LangGraph agents or workflows. It is very useful for building LLM apps from scratch.
### What LangGraph Engineer Does
When enabled, the toggle:
- **Provides specific prompts**: Adds LangGraph-specific guidance and best practices to all agent prompts
- **Includes documentation access**: Agents can automatically fetch up-to-date LangGraph documentation during planning and implementation through an MCP server.
- **Follows LangGraph patterns**: Ensures agents use proper LangGraph structure, primitives, and deployment methods.
<Tip>
Enable LangGraph Engineer for any project that imports from `@langchain/langgraph` or `langgraph` to get the best results.
</Tip>
## Getting Started
To begin using the Open SWE UI:
<Steps>
<Step title="Select Repository">
Choose your GitHub repository and branch using the repository selector
</Step>
<Step title="Choose Mode">
Toggle auto/manual mode based on your preference for plan approval
</Step>
<Step title="Submit Request">
Type your coding request in the terminal input and press Cmd+Enter to send
</Step>
<Step title="Monitor Progress">
Watch as the Manager routes your request and coordinates the planning and
implementation
</Step>
</Steps>
<Note>
Make sure you have properly configured your GitHub App and authentication
before using the UI. See the [Development Setup](/labs/swe/setup/development)
guide for details.
</Note>

View file

@ -1 +0,0 @@
productionize

View file

@ -1,4 +0,0 @@
node_modules
.next
.git
.env

View file

@ -1,34 +0,0 @@
# ------------------Github App Secrets-----------------
# GitHub app client secrets. Used for the GitHub OAuth login flow.
NEXT_PUBLIC_GITHUB_APP_CLIENT_ID=""
GITHUB_APP_CLIENT_SECRET=""
# Should be updated to your production URL when deployed.
# This value should match the redirect URL you have configured in
# your GitHub app settings.
GITHUB_APP_REDIRECT_URI="http://localhost:3000/api/auth/github/callback"
GITHUB_APP_NAME="open-swe-dev" # this must match the name of your GitHub app, excluding spaces
GITHUB_APP_ID=""
# App secret key. Should be multi-line.
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
...add your private key here...
-----END RSA PRIVATE KEY-----
"
# ------------------------Other------------------------
# The API URL of the proxy route in the Next.js app. This route forwards
# requests to the LangGraph server, injecting some secrets.
NEXT_PUBLIC_API_URL="http://localhost:3000/api"
# The API URL of the LangGraph server. Used in the proxy route to forward
# requests to the LangGraph server.
LANGGRAPH_API_URL="http://localhost:2024"
# Encryption key for secrets (32-byte hex string for AES-256)
# Should be the same value as the one used in the web app, so that secrets
# encrypted in the web app can be decrypted in the agent.
SECRETS_ENCRYPTION_KEY=""
# List of GitHub usernames that are allowed to use Open SWE without providing API keys
# This is only used in production. In development every user is an "allowed user".
# Must be a valid JSON array of strings.
NEXT_PUBLIC_ALLOWED_USERS_LIST='["your-github-username", "teammate-username"]'

30
apps/web/.gitignore vendored
View file

@ -1,30 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# LangGraph API
.langgraph_api
.env
.next/
next-env.d.ts

View file

@ -1,6 +0,0 @@
# Ignore artifacts:
build
coverage
#
pnpm-lock.yaml

View file

@ -1,13 +0,0 @@
# Open SWE Web
The Next.js web interface for Open SWE, providing a user-friendly chat interface to interact with the LangGraph agent.
## Documentation
For detailed usage information, see the [UI documentation](https://github.com/langchain-ai/open-swe/blob/main/apps/docs/usage/ui.mdx).
## Development
1. Copy the environment file: `cp .env.example .env` and fill in the required values
2. Install dependencies: `yarn install`
3. Start the development server: `yarn dev`

View file

@ -1,21 +0,0 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View file

@ -1,33 +0,0 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-unused-vars": [
"warn",
{ args: "none", argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
},
);

View file

@ -1,10 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverActions: {
bodySizeLimit: "10mb",
},
},
};
export default nextConfig;

View file

@ -1,106 +0,0 @@
{
"name": "@openswe/web",
"readme": "https://github.com/langchain-ai/open-swe/blob/main/apps/web/README.md",
"homepage": "https://github.com/langchain-ai/open-swe/blob/main/README.md",
"repository": {
"type": "git",
"url": "git+https://github.com/langchain-ai/open-swe.git"
},
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint:fix": "next lint --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"clean": "rm -rf .turbo .next || true && rm next-env.d.ts || true"
},
"dependencies": {
"@langchain/core": "^0.3.65",
"@langchain/langgraph": "^0.3.8",
"@langchain/langgraph-sdk": "^0.0.95",
"@octokit/app": "^16.0.1",
"@openswe/shared": "*",
"@radix-ui/react-alert-dialog": "^1.1.14",
"@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-collapsible": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-hover-card": "^1.1.14",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-popover": "^1.1.14",
"@radix-ui/react-scroll-area": "^1.2.9",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slider": "^1.3.5",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-tooltip": "^1.1.8",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"esbuild": "^0.25.0",
"esbuild-plugin-tailwindcss": "^2.0.1",
"framer-motion": "^12.4.9",
"jsonwebtoken": "^9.0.2",
"katex": "^0.16.21",
"langgraph-nextjs-api-passthrough": "^0.1.4",
"lodash": "^4.17.21",
"lucide-react": "^0.532.0",
"next-themes": "^0.4.4",
"nuqs": "^2.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-markdown": "^10.0.1",
"react-syntax-highlighter": "^15.5.0",
"recharts": "^2.15.1",
"rehype-katex": "^7.0.1",
"remark-gfm": "^4.0.1",
"remark-math": "^6.0.0",
"sonner": "^2.0.1",
"swr": "^2.3.4",
"tailwind-merge": "^3.0.2",
"tailwindcss-animate": "^1.0.7",
"use-stick-to-bottom": "^1.0.46",
"uuid": "^11.1.0",
"zod": "^3.25.32",
"zustand": "^5.0.5"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
"@octokit/types": "^14.1.0",
"@tailwindcss/postcss": "^4.0.13",
"@types/jsonwebtoken": "^9.0.10",
"@types/lodash": "^4.17.16",
"@types/node": "^22.13.5",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@types/react-syntax-highlighter": "^15.5.13",
"@types/uuid": "^10.0.0",
"autoprefixer": "^10.4.20",
"dotenv": "^16.4.7",
"eslint": "^9.19.0",
"eslint-config-next": "15.2.2",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.18",
"globals": "^15.14.0",
"next": "^15.4.8",
"postcss": "^8.5.3",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"shadcn": "^2.6.1",
"tailwind-scrollbar": "^4.0.1",
"tailwindcss": "^4.0.13",
"typescript": "~5.7.2",
"typescript-eslint": "^8.22.0"
},
"overrides": {
"react-is": "^19.0.0-rc-69d4b800-20241021"
},
"packageManager": "yarn@3.5.1"
}

View file

@ -1,5 +0,0 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};

View file

@ -1,11 +0,0 @@
/**
* @see https://prettier.io/docs/configuration
* @type {import("prettier").Config}
*/
const config = {
endOfLine: "auto",
singleAttributePerLine: true,
plugins: ["prettier-plugin-tailwindcss"],
};
export default config;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -1,21 +0,0 @@
import type { Metadata } from "next";
import "../../../globals.css";
import React from "react";
export const metadata: Metadata = {
title: "Open SWE - Thread",
description: "Open SWE thread view",
icons: {
icon: "/favicon.ico",
shortcut: "/favicon.ico",
apple: "/favicon.ico",
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return children;
}

View file

@ -1,5 +0,0 @@
import { ThreadViewLoading } from "@/components/v2/thread-view-loading";
export default function Loading() {
return <ThreadViewLoading />;
}

View file

@ -1,121 +0,0 @@
"use client";
import { ThreadView } from "@/components/v2/thread-view";
import { ThreadViewLoading } from "@/components/v2/thread-view-loading";
import { ThreadErrorCard } from "@/components/v2/thread-error-card";
import { useThreadMetadata } from "@/hooks/useThreadMetadata";
import { useThreadsSWR } from "@/hooks/useThreadsSWR";
import { useStream } from "@langchain/langgraph-sdk/react";
import { MANAGER_GRAPH_ID } from "@openswe/shared/constants";
import { ManagerGraphState } from "@openswe/shared/open-swe/manager/types";
import { useRouter } from "next/navigation";
import * as React from "react";
import { use, useEffect, useRef, useState } from "react";
import { Client, Thread } from "@langchain/langgraph-sdk";
async function fetchInitialThread(
client: Client<ManagerGraphState>,
threadId: string,
reqCount = 0,
): Promise<Thread<ManagerGraphState> | null> {
try {
return await client.threads.get(threadId);
} catch (e) {
console.error("Failed to fetch thread", {
requestCount: reqCount,
error: e,
});
// Retry a max of 5 times
if (reqCount < 5) {
return fetchInitialThread(client, threadId, reqCount + 1);
}
return null;
}
}
interface ThreadPageProps {
thread_id: string;
}
export default function ThreadPage({
params,
}: {
params: Promise<ThreadPageProps>;
}) {
const [initialFetchedThread, setInitialFetchedThread] =
useState<Thread<ManagerGraphState> | null>(null);
const router = useRouter();
const { thread_id } = use(params);
const stream = useStream<ManagerGraphState>({
apiUrl: process.env.NEXT_PUBLIC_API_URL ?? "",
assistantId: MANAGER_GRAPH_ID,
threadId: thread_id,
reconnectOnMount: true,
fetchStateHistory: false,
});
const { threads, isLoading: threadsLoading } = useThreadsSWR({
assistantId: MANAGER_GRAPH_ID,
disableOrgFiltering: true,
});
// Find the thread by ID
const thread = threads.find((t) => t.thread_id === thread_id);
// We need a thread object for the hook, so use a dummy if not found
const dummyThread = thread ||
initialFetchedThread || {
thread_id,
values: {},
status: "idle" as const,
updated_at: new Date().toISOString(),
created_at: new Date().toISOString(),
};
const { metadata: currentDisplayThread, statusError } = useThreadMetadata(
dummyThread as any,
);
const handleBackToHome = () => {
router.push("/chat");
};
const initialThreadFetched = useRef(false);
useEffect(() => {
if (!thread && !initialFetchedThread && !initialThreadFetched.current) {
fetchInitialThread(stream.client as Client<ManagerGraphState>, thread_id)
.then(setInitialFetchedThread)
.finally(() => (initialThreadFetched.current = true));
}
if (initialThreadFetched.current && initialFetchedThread && thread) {
setInitialFetchedThread(null);
}
}, [thread_id, thread]);
if (statusError && "message" in statusError && "type" in statusError) {
return (
<ThreadErrorCard
error={statusError}
onGoBack={handleBackToHome}
/>
);
}
if (
(!thread || threadsLoading) &&
(!initialFetchedThread || !initialThreadFetched.current)
) {
return <ThreadViewLoading onBackToHome={handleBackToHome} />;
}
return (
<div className="bg-background fixed inset-0">
<ThreadView
stream={stream}
displayThread={currentDisplayThread}
onBackToHome={handleBackToHome}
/>
</div>
);
}

View file

@ -1,21 +0,0 @@
import type { Metadata } from "next";
import "../../globals.css";
import React, { Suspense } from "react";
export const metadata: Metadata = {
title: "Open SWE - Chat",
description: "Open SWE chat",
icons: {
icon: "/favicon.ico",
shortcut: "/favicon.ico",
apple: "/favicon.ico",
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return <Suspense fallback={<div>Loading...</div>}>{children}</Suspense>;
}

View file

@ -1,40 +0,0 @@
"use client";
import { DefaultView } from "@/components/v2/default-view";
import { useThreadsSWR } from "@/hooks/useThreadsSWR";
import { GitHubAppProvider, useGitHubAppProvider } from "@/providers/GitHubApp";
import { Toaster } from "@/components/ui/sonner";
import { Suspense } from "react";
import { MANAGER_GRAPH_ID } from "@openswe/shared/constants";
function ChatPageComponent() {
const { currentInstallation } = useGitHubAppProvider();
const { threads, isLoading: threadsLoading } = useThreadsSWR({
assistantId: MANAGER_GRAPH_ID,
currentInstallation,
});
if (!threads) {
return <div>No threads</div>;
}
return (
<div className="bg-background h-screen">
<Suspense>
<Toaster />
<DefaultView
threads={threads}
threadsLoading={threadsLoading}
/>
</Suspense>
</div>
);
}
export default function ChatPage() {
return (
<GitHubAppProvider>
<ChatPageComponent />
</GitHubAppProvider>
);
}

View file

@ -1,21 +0,0 @@
import type { Metadata } from "next";
import "../../../globals.css";
import React from "react";
export const metadata: Metadata = {
title: "Open SWE - All Threads",
description: "Open SWE view all threads",
icons: {
icon: "/favicon.ico",
shortcut: "/favicon.ico",
apple: "/favicon.ico",
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return children;
}

View file

@ -1,304 +0,0 @@
"use client";
import { useState, Suspense, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { ArrowLeft, Search, Filter } from "lucide-react";
import { useRouter } from "next/navigation";
import { ThreadMetadata } from "@/components/v2/types";
import { useThreadsSWR } from "@/hooks/useThreadsSWR";
import { ThreadCard, ThreadCardLoading } from "@/components/v2/thread-card";
import { ThemeToggle } from "@/components/theme-toggle";
import { InstallationSelector } from "@/components/github/installation-selector";
import { GitHubAppProvider, useGitHubAppProvider } from "@/providers/GitHubApp";
import { MANAGER_GRAPH_ID } from "@openswe/shared/constants";
import { useThreadsStatus } from "@/hooks/useThreadsStatus";
import { cn } from "@/lib/utils";
import { threadsToMetadata } from "@/lib/thread-utils";
import { UserPopover } from "@/components/user-popover";
import { OpenSWELogo } from "@/components/icons/openswe-logo";
type FilterStatus =
| "all"
| "running"
| "completed"
| "failed"
| "pending"
| "idle"
| "paused"
| "error";
function AllThreadsPageContent() {
const router = useRouter();
const limit = 25;
const [offset, setOffset] = useState(0);
const { currentInstallation, installationsLoading } = useGitHubAppProvider();
const {
threads,
isLoading: threadsLoading,
hasMore,
} = useThreadsSWR({
assistantId: MANAGER_GRAPH_ID,
currentInstallation,
pagination: {
limit,
offset,
sortBy: "updated_at",
sortOrder: "desc",
},
});
const [searchQuery, setSearchQuery] = useState("");
const [statusFilter, setStatusFilter] = useState<FilterStatus>("all");
const threadsMetadata = useMemo(() => threadsToMetadata(threads), [threads]);
const threadIds = threadsMetadata.map((thread) => thread.id);
const {
statusMap,
taskPlanMap,
statusCounts,
isLoading: statusLoading,
} = useThreadsStatus(threadIds, threads);
const filteredThreads = useMemo(() => {
return threadsMetadata.filter((thread: ThreadMetadata) => {
const matchesSearch =
thread.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
thread.repository.toLowerCase().includes(searchQuery.toLowerCase());
const matchesStatus =
statusFilter === "all" || statusMap[thread.id] === statusFilter;
return matchesSearch && matchesStatus;
});
}, [threadsMetadata, searchQuery, statusFilter, statusMap]);
const groupedThreads = useMemo(() => {
return {
running: filteredThreads.filter(
(thread: ThreadMetadata) => statusMap[thread.id] === "running",
),
completed: filteredThreads.filter(
(thread: ThreadMetadata) => statusMap[thread.id] === "completed",
),
failed: filteredThreads.filter(
(thread: ThreadMetadata) => statusMap[thread.id] === "failed",
),
pending: filteredThreads.filter(
(thread: ThreadMetadata) => statusMap[thread.id] === "pending",
),
idle: filteredThreads.filter(
(thread: ThreadMetadata) => statusMap[thread.id] === "idle",
),
paused: filteredThreads.filter(
(thread: ThreadMetadata) => statusMap[thread.id] === "paused",
),
error: filteredThreads.filter(
(thread: ThreadMetadata) => statusMap[thread.id] === "error",
),
};
}, [filteredThreads, statusMap]);
// Show loading state if threads/status/installation requests are loading, and there are no
// threads to display (conditional of the status filter)
const showThreadsLoading =
(threadsLoading || statusLoading || installationsLoading) &&
(statusFilter === "all"
? Object.values(groupedThreads).flat().length === 0
: filteredThreads.length === 0);
const showNoThreads =
filteredThreads.length === 0 &&
!threadsLoading &&
!statusLoading &&
!showThreadsLoading;
return (
<div className="bg-background flex h-screen flex-col">
{/* Header */}
<div className="border-border bg-card border-b px-4 py-3">
<div className="flex items-center gap-3">
<Button
variant="ghost"
size="sm"
className="text-muted-foreground hover:bg-muted hover:text-foreground h-6 w-6 p-0"
onClick={() => router.push("/chat")}
>
<ArrowLeft className="h-3 w-3" />
</Button>
<div className="flex items-center gap-2">
<OpenSWELogo
width={120}
height={18}
/>
</div>
<div className="ml-auto flex items-center gap-4">
<div className="flex items-center gap-2">
<span className="text-muted-foreground text-xs">
{filteredThreads.length} threads
</span>
</div>
<div className="flex items-center gap-2">
<ThemeToggle />
<UserPopover />
</div>
</div>
</div>
</div>
{/* Search and Filters */}
<div className="border-border bg-muted/50 border-b px-4 py-3">
<div className="flex items-center gap-3">
<div className="relative max-w-md flex-1">
<Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform" />
<Input
placeholder="Search threads..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="border-border bg-background text-foreground placeholder:text-muted-foreground pl-10"
/>
</div>
<div className="flex items-center gap-1">
<Filter className="text-muted-foreground h-4 w-4" />
<span className="text-muted-foreground mr-2 text-xs">Filter:</span>
{(
[
"all",
"running",
"completed",
"failed",
"pending",
"idle",
"paused",
"error",
] as FilterStatus[]
).map((status) => (
<Button
key={status}
variant={statusFilter === status ? "secondary" : "ghost"}
size="sm"
className={cn(
"h-7 text-xs",
statusFilter === status
? "bg-muted text-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)}
onClick={() => setStatusFilter(status)}
>
{status === "all"
? "All"
: status.charAt(0).toUpperCase() + status.slice(1)}
<Badge
variant="secondary"
className="bg-muted/70 text-muted-foreground ml-1 text-xs"
>
{statusCounts[status]}
</Badge>
</Button>
))}
</div>
</div>
</div>
{/* Content */}
<div className="flex-1 overflow-auto">
<div className="mx-auto max-w-6xl p-4">
{statusFilter === "all" ? (
<div className="space-y-6">
{Object.entries(groupedThreads).map(([status, threads]) => {
if (threads.length === 0) return null;
return (
<div key={status}>
<div className="mb-3 flex items-center gap-2">
<h2 className="text-foreground text-base font-semibold capitalize">
{status} Threads
</h2>
<Badge
variant="secondary"
className="bg-muted/70 text-muted-foreground text-xs"
>
{threads.length}
</Badge>
</div>
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
{threads.map((thread) => (
<ThreadCard
key={thread.id}
thread={thread}
status={statusMap[thread.id]}
statusLoading={statusLoading}
taskPlan={taskPlanMap[thread.id]}
/>
))}
</div>
</div>
);
})}
</div>
) : (
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
{filteredThreads.map((thread) => (
<ThreadCard
key={thread.id}
thread={thread}
status={statusMap[thread.id]}
statusLoading={statusLoading}
taskPlan={taskPlanMap[thread.id]}
/>
))}
</div>
)}
{showNoThreads && (
<div className="py-12 text-center">
<div className="text-muted-foreground mb-2">No threads found</div>
<div className="text-muted-foreground/70 text-xs">
{!threads || threads.length === 0
? "No threads have been created yet"
: searchQuery
? "Try adjusting your search query"
: "No threads match the selected filter"}
</div>
</div>
)}
{showThreadsLoading && (
<div>
<div className="mb-3 flex items-center gap-2">
<h2 className="text-foreground text-base font-semibold capitalize">
Loading threads...
</h2>
</div>
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
{Array.from({ length: 9 }).map((_, index) => (
<ThreadCardLoading key={`all-threads-loading-${index}`} />
))}
</div>
</div>
)}
{!showNoThreads && hasMore && (
<div className="flex items-center justify-center">
<Button
variant="outline"
onClick={() => setOffset((prev) => prev + limit)}
>
Load more
</Button>
</div>
)}
</div>
</div>
</div>
);
}
export default function AllThreadsPage() {
return (
<Suspense fallback={<div>Loading...</div>}>
<GitHubAppProvider>
<AllThreadsPageContent />
</GitHubAppProvider>
</Suspense>
);
}

View file

@ -1,28 +0,0 @@
"use client";
import { Suspense } from "react";
import SettingsPage from "@/features/settings-page/index";
function SettingsPageLoading() {
return (
<div className="mx-auto max-w-6xl p-6">
<div className="mb-8">
<h1 className="text-foreground mb-2 text-3xl font-bold">Settings</h1>
<p className="text-muted-foreground">Loading your settings...</p>
</div>
<div className="space-y-6">
<div className="bg-muted h-20 animate-pulse rounded"></div>
<div className="bg-muted h-40 animate-pulse rounded"></div>
<div className="bg-muted h-60 animate-pulse rounded"></div>
</div>
</div>
);
}
export default function Page() {
return (
<Suspense fallback={<SettingsPageLoading />}>
<SettingsPage />
</Suspense>
);
}

View file

@ -1,79 +0,0 @@
import { initApiPassthrough } from "langgraph-nextjs-api-passthrough";
import {
GITHUB_TOKEN_COOKIE,
GITHUB_INSTALLATION_ID_COOKIE,
GITHUB_INSTALLATION_TOKEN_COOKIE,
GITHUB_INSTALLATION_NAME,
GITHUB_INSTALLATION_ID,
} from "@openswe/shared/constants";
import {
getGitHubInstallationTokenOrThrow,
getInstallationNameFromReq,
getGitHubAccessTokenOrThrow,
} from "./utils";
import { encryptSecret } from "@openswe/shared/crypto";
// This file acts as a proxy for requests to your LangGraph server.
// Read the [Going to Production](https://github.com/langchain-ai/agent-chat-ui?tab=readme-ov-file#going-to-production) section for more information.
export const { GET, POST, PUT, PATCH, DELETE, OPTIONS, runtime } =
initApiPassthrough({
apiUrl: process.env.LANGGRAPH_API_URL ?? "http://localhost:2024",
runtime: "edge", // default
disableWarningLog: true,
bodyParameters: (req, body) => {
if (body.config?.configurable && "apiKeys" in body.config.configurable) {
const encryptionKey = process.env.SECRETS_ENCRYPTION_KEY;
if (!encryptionKey) {
throw new Error(
"SECRETS_ENCRYPTION_KEY environment variable is required",
);
}
const apiKeys = body.config.configurable.apiKeys;
const encryptedApiKeys: Record<string, unknown> = {};
// Encrypt each field in the apiKeys object
for (const [key, value] of Object.entries(apiKeys)) {
if (typeof value === "string" && value.trim() !== "") {
encryptedApiKeys[key] = encryptSecret(value, encryptionKey);
} else {
encryptedApiKeys[key] = value;
}
}
// Update the body with encrypted apiKeys
body.config.configurable.apiKeys = encryptedApiKeys;
return body;
}
return body;
},
headers: async (req) => {
const encryptionKey = process.env.SECRETS_ENCRYPTION_KEY;
if (!encryptionKey) {
throw new Error(
"SECRETS_ENCRYPTION_KEY environment variable is required",
);
}
const installationIdCookie = req.cookies.get(
GITHUB_INSTALLATION_ID_COOKIE,
)?.value;
if (!installationIdCookie) {
throw new Error(
"No GitHub installation ID found. GitHub App must be installed first.",
);
}
const [installationToken, installationName] = await Promise.all([
getGitHubInstallationTokenOrThrow(installationIdCookie, encryptionKey),
getInstallationNameFromReq(req.clone(), installationIdCookie),
]);
return {
[GITHUB_TOKEN_COOKIE]: getGitHubAccessTokenOrThrow(req, encryptionKey),
[GITHUB_INSTALLATION_TOKEN_COOKIE]: installationToken,
[GITHUB_INSTALLATION_NAME]: installationName,
[GITHUB_INSTALLATION_ID]: installationIdCookie,
};
},
});

View file

@ -1,87 +0,0 @@
import { getInstallationToken } from "@openswe/shared/github/auth";
import { App } from "@octokit/app";
import { GITHUB_TOKEN_COOKIE } from "@openswe/shared/constants";
import { encryptSecret } from "@openswe/shared/crypto";
import { NextRequest } from "next/server";
export function getGitHubAccessTokenOrThrow(
req: NextRequest,
encryptionKey: string,
): string {
const token = req.cookies.get(GITHUB_TOKEN_COOKIE)?.value ?? "";
if (!token) {
throw new Error(
"No GitHub access token found. User must authenticate first.",
);
}
return encryptSecret(token, encryptionKey);
}
export async function getGitHubInstallationTokenOrThrow(
installationIdCookie: string,
encryptionKey: string,
): Promise<string> {
const appId = process.env.GITHUB_APP_ID;
const privateAppKey = process.env.GITHUB_APP_PRIVATE_KEY;
if (!appId || !privateAppKey) {
throw new Error("GitHub App ID or Private App Key is not configured.");
}
const token = await getInstallationToken(
installationIdCookie,
appId,
privateAppKey,
);
return encryptSecret(token, encryptionKey);
}
async function getInstallationName(installationId: string) {
if (!process.env.GITHUB_APP_ID || !process.env.GITHUB_APP_PRIVATE_KEY) {
throw new Error("GitHub App ID or Private App Key is not configured.");
}
const app = new App({
appId: process.env.GITHUB_APP_ID,
privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
});
// Get installation details
const { data } = await app.octokit.request(
"GET /app/installations/{installation_id}",
{
installation_id: Number(installationId),
},
);
const installationName =
data.account && "name" in data.account
? data.account.name
: data.account?.login;
return installationName ?? "";
}
export async function getInstallationNameFromReq(
req: Request,
installationId: string,
): Promise<string> {
try {
const reqCopy = req.clone();
const requestJson = await reqCopy.json();
const installationName = requestJson?.input?.targetRepository?.owner;
if (installationName) {
return installationName;
}
} catch {
// no-op
}
try {
return await getInstallationName(installationId);
} catch (error) {
console.error("Failed to get installation name:", error);
return "";
}
}

View file

@ -1,130 +0,0 @@
import {
GITHUB_AUTH_STATE_COOKIE,
GITHUB_INSTALLATION_ID_COOKIE,
GITHUB_TOKEN_TYPE_COOKIE,
GITHUB_TOKEN_COOKIE,
} from "@openswe/shared/constants";
import { getInstallationCookieOptions } from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const code = searchParams.get("code");
const state = searchParams.get("state");
const error = searchParams.get("error");
const installationId = searchParams.get("installation_id");
// Handle GitHub App errors
if (error) {
return NextResponse.redirect(
new URL(`/?error=${encodeURIComponent(error)}`, request.url),
);
}
// Validate required parameters
if (!code) {
return NextResponse.redirect(
new URL("/?error=missing_code_parameter", request.url),
);
}
// Verify state parameter to prevent CSRF attacks
const storedState = request.cookies.get(GITHUB_AUTH_STATE_COOKIE)?.value;
if (storedState && state !== storedState) {
return NextResponse.redirect(
new URL("/?error=invalid_state", request.url),
);
}
const clientId = process.env.NEXT_PUBLIC_GITHUB_APP_CLIENT_ID;
const clientSecret = process.env.GITHUB_APP_CLIENT_SECRET;
const redirectUri = process.env.GITHUB_APP_REDIRECT_URI;
if (!clientId || !clientSecret || !redirectUri) {
return NextResponse.redirect(
new URL("/?error=configuration_missing", request.url),
);
}
// Exchange authorization code for access token
const tokenResponse = await fetch(
"https://github.com/login/oauth/access_token",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: clientId,
client_secret: clientSecret,
code: code,
redirect_uri: redirectUri,
}),
},
);
if (!tokenResponse.ok) {
console.error("Token exchange failed:", await tokenResponse.text());
return NextResponse.redirect(
new URL("/?error=token_exchange_failed", request.url),
);
}
const tokenData = await tokenResponse.json();
if (tokenData.error) {
return NextResponse.redirect(
new URL(`/?error=${encodeURIComponent(tokenData.error)}`, request.url),
);
}
// Create the success response
const response = NextResponse.redirect(new URL("/chat", request.url));
// Clear the state cookie as it's no longer needed
response.cookies.set(GITHUB_AUTH_STATE_COOKIE, "", {
expires: new Date(0),
path: "/",
});
// Set token cookies directly on the response
response.cookies.set(GITHUB_TOKEN_COOKIE, tokenData.access_token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 30, // 30 days
path: "/",
});
response.cookies.set(
GITHUB_TOKEN_TYPE_COOKIE,
tokenData.token_type || "bearer",
{
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 30, // 30 days
path: "/",
},
);
// If there's an installation_id, store that as well for future API calls
if (installationId) {
response.cookies.set(
GITHUB_INSTALLATION_ID_COOKIE,
installationId,
getInstallationCookieOptions(),
);
}
return response;
} catch (error) {
console.error("GitHub App callback error:", error);
return NextResponse.redirect(
new URL("/?error=callback_failed", request.url),
);
}
}

View file

@ -1,45 +0,0 @@
import { GITHUB_AUTH_STATE_COOKIE } from "@openswe/shared/constants";
import { NextRequest, NextResponse } from "next/server";
export async function GET(_request: NextRequest) {
try {
const clientId = process.env.NEXT_PUBLIC_GITHUB_APP_CLIENT_ID;
const redirectUri = process.env.GITHUB_APP_REDIRECT_URI;
if (!clientId || !redirectUri) {
return NextResponse.json(
{ error: "GitHub App configuration missing" },
{ status: 500 },
);
}
// Generate a random state parameter for security
const state = crypto.randomUUID();
// Build the GitHub App authorization URL
const authUrl = new URL("https://github.com/login/oauth/authorize");
authUrl.searchParams.set("client_id", clientId);
authUrl.searchParams.set("redirect_uri", redirectUri);
authUrl.searchParams.set("allow_signup", "true");
authUrl.searchParams.set("state", state);
// Create response with redirect and store state in a cookie
const response = NextResponse.redirect(authUrl.toString());
response.cookies.set(GITHUB_AUTH_STATE_COOKIE, state, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 10, // 10 minutes
path: "/",
});
return response;
} catch (error) {
console.error("GitHub App login error:", error);
return NextResponse.json(
{ error: "Failed to initiate GitHub App authentication flow" },
{ status: 500 },
);
}
}

View file

@ -1,19 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { clearGitHubToken } from "@/lib/auth";
/**
* API route to handle GitHub logout
*/
export async function POST(request: NextRequest) {
try {
const response = NextResponse.json({ success: true });
clearGitHubToken(response);
return response;
} catch (error) {
console.error("Error during logout:", error);
return NextResponse.json(
{ success: false, error: "Failed to logout" },
{ status: 500 },
);
}
}

View file

@ -1,18 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { isAuthenticated } from "@/lib/auth";
/**
* API route to check GitHub authentication status
*/
export async function GET(request: NextRequest) {
try {
const authenticated = isAuthenticated(request);
return NextResponse.json({ authenticated });
} catch (error) {
console.error("Error checking auth status:", error);
return NextResponse.json(
{ authenticated: false, error: "Failed to check authentication status" },
{ status: 500 },
);
}
}

View file

@ -1,34 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { getGitHubToken } from "@/lib/auth";
import { verifyGithubUser } from "@openswe/shared/github/verify-user";
export async function GET(request: NextRequest) {
try {
const token = getGitHubToken(request);
if (!token || !token.access_token) {
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
}
const user = await verifyGithubUser(token.access_token);
if (!user) {
return NextResponse.json(
{ error: "Invalid GitHub token" },
{ status: 401 },
);
}
// Only return safe fields
return NextResponse.json({
user: {
login: user.login,
avatar_url: user.avatar_url,
html_url: user.html_url,
name: user.name,
email: user.email,
},
});
} catch (error) {
return NextResponse.json(
{ error: "Failed to fetch user info" },
{ status: 500 },
);
}
}

View file

@ -1,71 +0,0 @@
import { GITHUB_INSTALLATION_ID_COOKIE } from "@openswe/shared/constants";
import {
GITHUB_INSTALLATION_RETURN_TO_COOKIE,
GITHUB_INSTALLATION_STATE_COOKIE,
getInstallationCookieOptions,
} from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";
/**
* Handles callbacks from GitHub App installations
* This endpoint is called by GitHub after a user installs or configures the GitHub App
*/
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const installationId = searchParams.get("installation_id");
// Get the return URL from cookies
const returnTo =
request.cookies.get(GITHUB_INSTALLATION_RETURN_TO_COOKIE)?.value || "/";
// Verify state parameter to prevent CSRF attacks
// GitHub App installation doesn't return the state directly, but we included it in our callback URL
const customState = searchParams.get("custom_state");
const storedState = request.cookies.get(
GITHUB_INSTALLATION_STATE_COOKIE,
)?.value;
// Validate state if it exists
if (storedState && customState && storedState !== customState) {
console.warn("Invalid installation state detected");
// We'll still proceed but log the warning
}
// Create the response that will redirect back to the app
const response = NextResponse.redirect(returnTo);
// Clear cookies as they're no longer needed
const expiredCookieOptions = {
expires: new Date(0),
path: "/",
};
response.cookies.set(
GITHUB_INSTALLATION_RETURN_TO_COOKIE,
"",
expiredCookieOptions,
);
response.cookies.set(
GITHUB_INSTALLATION_STATE_COOKIE,
"",
expiredCookieOptions,
);
// If we have an installation ID, store it in a cookie
if (installationId) {
response.cookies.set(
GITHUB_INSTALLATION_ID_COOKIE,
installationId,
getInstallationCookieOptions(),
);
}
return response;
} catch (error) {
console.error("GitHub App installation callback error:", error);
return NextResponse.redirect(
new URL("/?error=installation_callback_failed", request.url),
);
}
}

View file

@ -1,84 +0,0 @@
import {
GITHUB_INSTALLATION_RETURN_TO_COOKIE,
GITHUB_INSTALLATION_STATE_COOKIE,
} from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";
import { randomBytes } from "crypto";
import { GITHUB_TOKEN_COOKIE } from "@openswe/shared/constants";
/**
* Initiates the GitHub App installation flow
* This redirects users to the GitHub App installation page where they can
* select which repositories to grant access to
*/
export async function GET(request: NextRequest) {
try {
const accessToken = request.cookies.get(GITHUB_TOKEN_COOKIE)?.value;
if (!accessToken) {
return NextResponse.json(
{ error: "GitHub access token not found" },
{ status: 401 },
);
}
// Get GitHub App name from environment variables
const githubAppName = process.env.GITHUB_APP_NAME;
if (!githubAppName) {
return NextResponse.json(
{ error: "GitHub App name not configured" },
{ status: 500 },
);
}
// Check for existing state or generate a new one
let state = request.cookies.get(GITHUB_INSTALLATION_STATE_COOKIE)?.value;
// If no state exists or we want to ensure a fresh state, generate a new one
if (!state) {
state = randomBytes(16).toString("hex");
}
// Create a response that will redirect to the GitHub App installation page
// Include the callback URL as a parameter to ensure GitHub redirects back to our app
// Add the state as a custom parameter in the callback URL
const baseCallbackUrl = `${request.nextUrl.origin}/api/github/installation-callback`;
const callbackUrl = `${baseCallbackUrl}?custom_state=${encodeURIComponent(state)}`;
const response = NextResponse.redirect(
`https://github.com/apps/${githubAppName}/installations/new?redirect_uri=${encodeURIComponent(callbackUrl)}`,
);
// Cookie options for security and proper expiration
const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax" as const,
maxAge: 60 * 10, // 10 minutes
path: "/",
};
// Store the state in a cookie for validation when GitHub redirects back
response.cookies.set(
GITHUB_INSTALLATION_STATE_COOKIE,
state,
cookieOptions,
);
// Store the current URL as the return_to URL so we can redirect back after installation
const returnTo = request.headers.get("referer") || "/";
response.cookies.set(
GITHUB_INSTALLATION_RETURN_TO_COOKIE,
returnTo,
cookieOptions,
);
return response;
} catch (error) {
console.error("Error initiating GitHub App installation:", error);
return NextResponse.json(
{ error: "Failed to initiate GitHub App installation" },
{ status: 500 },
);
}
}

View file

@ -1,55 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { getGitHubToken } from "@/lib/auth";
import { Endpoints } from "@octokit/types";
type GitHubInstallationsResponse =
Endpoints["GET /user/installations"]["response"]["data"];
/**
* Fetches all GitHub App installations accessible to the current user
* Uses the user's access token from GITHUB_TOKEN_COOKIE to call GET /user/installations
*/
export async function GET(request: NextRequest) {
try {
// Get the user's access token from cookies
const tokenData = getGitHubToken(request);
if (!tokenData || !tokenData.access_token) {
return NextResponse.json(
{
error: "GitHub access token not found. Please authenticate first.",
},
{ status: 401 },
);
}
// Fetch installations from GitHub API
const response = await fetch("https://api.github.com/user/installations", {
headers: {
Authorization: `${tokenData.token_type} ${tokenData.access_token}`,
Accept: "application/vnd.github.v3+json",
"User-Agent": "OpenSWE-Agent",
},
});
if (!response.ok) {
const errorData = await response.json();
return NextResponse.json(
{
error: `Failed to fetch installations: ${JSON.stringify(errorData)}`,
},
{ status: response.status },
);
}
const data: GitHubInstallationsResponse = await response.json();
return NextResponse.json(data);
} catch (error) {
console.error("Error fetching GitHub installations:", error);
return NextResponse.json(
{ error: "Failed to fetch installations" },
{ status: 500 },
);
}
}

Some files were not shown because too many files have changed in this diff Show more