Co-authored-by: openhands <openhands@all-hands.dev>
agent-server-gui
Run this with OpenHands Agent Server first
If you only read one section of this README, read this one. Most users will want to clone this repo, point it at a real openhands-agent-server, and start using the UI immediately.
Prerequisites
- Node.js 22.12.x or later
npm- A running OpenHands Agent Server (
openhands-agent-server)
1. Clone and install the frontend
git clone https://github.com/neubig/agent-server-gui.git
cd agent-server-gui
npm install
2. Install and start OpenHands Agent Server
If you do not already have the backend installed, install it first:
python3 -m pip install openhands-agent-server
Then start the backend on the default local port:
openhands-agent-server --host 127.0.0.1 --port 8000
The frontend expects the backend at 127.0.0.1:8000 by default, so this is the easiest from-scratch setup.
If you start the backend with SESSION_API_KEY or OH_SESSION_API_KEYS_0, every /api/* route is authenticated with X-Session-API-Key. In that case the frontend must send the same key via VITE_SESSION_API_KEY.
If you prefer installing from source or want the full SDK setup flow, see the OpenHands SDK docs: https://docs.openhands.dev/sdk/getting-started
3. Optional: create a .env file
If your backend is not running on 127.0.0.1:8000, or if it requires a session API key, create a .env file:
cp .env.sample .env
Then update the values you need:
VITE_BACKEND_HOST="127.0.0.1:8000"
VITE_BACKEND_BASE_URL="http://127.0.0.1:8000"
VITE_FRONTEND_PORT="3001"
# Use the same value as backend SESSION_API_KEY or OH_SESSION_API_KEYS_0 when auth is enabled.
# VITE_SESSION_API_KEY="your-session-api-key"
# VITE_WORKING_DIR="/absolute/path/to/the-workspace-used-by-agent-server"
Notes:
VITE_BACKEND_HOSTis used by the Vite dev proxy for/api,/server_info, and/sockets.VITE_BACKEND_BASE_URLis used by browser-side direct requests. Keep it pointed at the same backend.VITE_WORKING_DIRshould match the workspace path the backend will use when starting new conversations.- If the backend is secured with
SESSION_API_KEYorOH_SESSION_API_KEYS_0, setVITE_SESSION_API_KEYto the same value or live requests will fail with401 Unauthorized. - If your backend does not require
X-Session-API-Key, leaveVITE_SESSION_API_KEYunset.
4. Start the frontend
npm run dev
This starts the frontend on http://localhost:3001.
5. First-run sanity check
After the page opens:
/should load without errors- you should be able to open or create a conversation
/conversations/:idshould load conversation content/settingsshould load- on secured backends, make sure
VITE_SESSION_API_KEYmatches the backend session key - if your backend is too old to expose settings schemas, the UI will show an actionable
SDK settings schema unavailable.message instead of crashing
- on secured backends, make sure
Mock mode
If you want to run the frontend without a live backend, use:
npm run dev:mock
# or
npm run dev:mock:saas
Overview
This repository is a near-direct port of the OpenHands frontend adapted to talk directly to software-agent-sdk / agent_server without the usual OpenHands app backend.
Tech Stack
- Remix SPA Mode (React + Vite + React Router)
- TypeScript
- Redux
- TanStack Query
- Tailwind CSS
- i18next
- React Testing Library
- Vitest
- Mock Service Worker
Building for Production
There is no Makefile in this repository. Use the npm scripts instead:
npm run build
npm run start
Environment Variables
The frontend application uses the following environment variables:
| Variable | Description | Default Value |
|---|---|---|
VITE_BACKEND_BASE_URL |
Full base URL for the agent server used by direct browser requests | current browser origin |
VITE_BACKEND_HOST |
Backend host used by the Vite dev proxy | 127.0.0.1:8000 |
VITE_SESSION_API_KEY |
Optional X-Session-API-Key header value for authenticated agent_server instances |
- |
VITE_WORKING_DIR |
Workspace path sent when starting new conversations | /workspace/project |
VITE_WORKER_URLS |
Optional comma-separated worker/app URLs for the Browser tab | - |
VITE_ENABLE_BROWSER_TOOLS |
Set to false to omit BrowserToolSet from new conversation payloads |
true |
VITE_MOCK_API |
Enable/disable API mocking with MSW | false |
VITE_MOCK_SAAS |
Simulate SaaS mode in development | false |
VITE_USE_TLS |
Use HTTPS/WSS for the Vite proxy target | false |
VITE_FRONTEND_PORT |
Port to run the frontend application | 3001 |
VITE_INSECURE_SKIP_VERIFY |
Skip TLS certificate verification for proxied backend requests | false |
VITE_GITHUB_TOKEN |
GitHub token for repository access (used in some tests) | - |
You can create a .env file in the project directory with these variables based on .env.sample.
Project Structure
frontend
├── __tests__ # Tests
├── public
├── src
│ ├── api # API calls
│ ├── assets
│ ├── components
│ ├── context # Local state management
│ ├── hooks # Custom hooks
│ ├── i18n # Internationalization
│ ├── mocks # MSW mocks for development
│ ├── routes # React Router file-based routes
│ ├── services
│ ├── state # Redux state management
│ ├── types
│ ├── utils # Utility/helper functions
│ └── root.tsx # Entry point
└── .env.sample # Sample environment variables
Components
Components are organized into folders based on their domain, feature, or shared functionality.
components
├── features # Domain-specific components
├── layout
├── modals
└── ui # Shared UI components
Features
- Real-time updates with WebSockets
- Internationalization
- Router data loading with Remix
- User authentication with GitHub OAuth (if saas mode is enabled)
Testing
Testing Framework and Tools
We use the following testing tools:
- Test Runner: Vitest
- Rendering: React Testing Library
- User Interactions: @testing-library/user-event
- API Mocking: Mock Service Worker (MSW)
- Code Coverage: Vitest with V8 coverage
Running Tests
To run all tests:
npm run test
To run tests with coverage:
npm run test:coverage
Testing Best Practices
-
Component Testing
- Test components in isolation
- Use our custom
renderWithProviders()that wraps the components we want to test in our providers. It is especially useful for components that use Redux - Use
render()from React Testing Library to render components - Prefer querying elements by role, label, or test ID over CSS selectors
- Test both rendering and interaction scenarios
-
User Event Simulation
- Use
userEventfor simulating realistic user interactions - Test keyboard events, clicks, typing, and other user actions
- Handle edge cases like disabled states, empty inputs, etc.
- Use
-
Mocking
- We test components that make network requests by mocking those requests with Mock Service Worker (MSW)
- Use
vi.fn()to create mock functions for callbacks and event handlers - Mock external dependencies and API calls (more info)[https://mswjs.io/docs/getting-started]
- Verify mock function calls using
.toHaveBeenCalledWith(),.toHaveBeenCalledTimes()
-
Accessibility Testing
- Use
toBeInTheDocument()to check element presence - Test keyboard navigation and screen reader compatibility
- Verify correct ARIA attributes and roles
- Use
-
State and Prop Testing
- Test component behavior with different prop combinations
- Verify state changes and conditional rendering
- Test error states and loading scenarios
-
Internationalization (i18n) Testing
- Test translation keys and placeholders
- Verify text rendering across different languages
Example Test Structure:
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi } from "vitest";
describe("ComponentName", () => {
it("should render correctly", () => {
render(<Component />);
expect(screen.getByRole("button")).toBeInTheDocument();
});
it("should handle user interactions", async () => {
const mockCallback = vi.fn();
const user = userEvent.setup();
render(<Component onClick={mockCallback} />);
const button = screen.getByRole("button");
await user.click(button);
expect(mockCallback).toHaveBeenCalledOnce();
});
});
Example Tests in the Codebase
For real-world examples of testing, check out these test files:
-
Chat Input Component Test:
__tests__/components/chat/chat-input.test.tsx- Demonstrates comprehensive testing of a complex input component
- Covers various scenarios like submission, disabled states, and user interactions
-
File Explorer Component Test:
__tests__/components/file-explorer/file-explorer.test.tsx- Shows testing of a more complex component with multiple interactions
- Illustrates testing of nested components and state management
Test Coverage
- Aim for high test coverage, especially for critical components
- Focus on testing different scenarios and edge cases
- Use code coverage reports to identify untested code paths
Continuous Integration
Tests are automatically run during:
- Pre-commit hooks
- Pull request checks
- CI/CD pipeline
Contributing
Please read the CONTRIBUTING.md file for details on our code of conduct, and the process for submitting pull requests to us.
Troubleshooting
TODO