Compare commits

...

7 Commits

Author SHA1 Message Date
Robert Brennan 2405019af6 Remove upload functionality and add tooltip for Code not in GitHub link (#7431)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-03-22 23:03:55 -04:00
Robert Brennan 6ad2de81d4 Refactor runtime documentation and add hardened Docker installation guide (#7429)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-03-22 23:03:55 -04:00
Robert Brennan 678c73df06 Add logo color (#CFB755) for tab icons (#7433)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-03-22 23:03:55 -04:00
Robert Brennan b2ca92a042 update placeholder text for credits (#7430) 2025-03-22 23:03:55 -04:00
Robert Brennan 5fb408ac1a Move documentation link above settings gear in sidebar (#7432)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-03-22 23:03:55 -04:00
openhands fc95c21986 Fix TypeScript errors in logout page 2025-03-23 02:09:05 +00:00
openhands 1630d49146 Add logout page without sidebar 2025-03-23 02:04:58 +00:00
27 changed files with 410 additions and 306 deletions
+4
View File
@@ -56,6 +56,10 @@ docker run -it --rm --pull=always \
docker.all-hands.dev/all-hands-ai/openhands:0.29
```
> [!WARNING]
> On a public network? See our [Hardened Docker Installation](https://docs.all-hands.dev/modules/usage/runtimes/docker#hardened-docker-installation) guide
> to secure your deployment by restricting network binding and implementing additional security measures.
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
Finally, you'll need a model provider and API key.
+24
View File
@@ -0,0 +1,24 @@
# Runtime Configuration
A Runtime is an environment where the OpenHands agent can edit files and run
commands.
By default, OpenHands uses a Docker-based runtime, running on your local computer.
This means you only have to pay for the LLM you're using, and your code is only ever sent to the LLM.
We also support "remote" runtimes, which are typically managed by third-parties.
They can make setup a bit simpler and more scalable, especially
if you're running many OpenHands conversations in parallel (e.g. to do evaluation).
Additionally, we provide a "local" runtime that runs directly on your machine without Docker,
which can be useful in controlled environments like CI pipelines.
## Available Runtimes
OpenHands supports several different runtime environments:
- [Docker Runtime](./runtimes/docker.md) - The default runtime that uses Docker containers for isolation (recommended for most users)
- [OpenHands Remote Runtime](./runtimes/remote.md) - Cloud-based runtime for parallel execution (beta)
- [Modal Runtime](./runtimes/modal.md) - Runtime provided by our partners at Modal
- [Daytona Runtime](./runtimes/daytona.md) - Runtime provided by Daytona
- [Local Runtime](./runtimes/local.md) - Direct execution on your local machine without Docker
+6 -174
View File
@@ -1,176 +1,8 @@
# Runtime Configuration
---
title: Runtime Configuration
slug: /usage/runtimes
---
A Runtime is an environment where the OpenHands agent can edit files and run
commands.
import { Redirect } from '@docusaurus/router';
By default, OpenHands uses a Docker-based runtime, running on your local computer.
This means you only have to pay for the LLM you're using, and your code is only ever sent to the LLM.
We also support "remote" runtimes, which are typically managed by third-parties.
They can make setup a bit simpler and more scalable, especially
if you're running many OpenHands conversations in parallel (e.g. to do evaluation).
Additionally, we provide a "local" runtime that runs directly on your machine without Docker,
which can be useful in controlled environments like CI pipelines.
## Docker Runtime
This is the default Runtime that's used when you start OpenHands. You might notice
some flags being passed to `docker run` that make this possible:
```
docker run # ...
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.29-nikolaik \
-v /var/run/docker.sock:/var/run/docker.sock \
# ...
```
The `SANDBOX_RUNTIME_CONTAINER_IMAGE` from nikolaik is a pre-built runtime image
that contains our Runtime server, as well as some basic utilities for Python and NodeJS.
You can also [build your own runtime image](how-to/custom-sandbox-guide).
### Connecting to Your filesystem
One useful feature here is the ability to connect to your local filesystem. To mount your filesystem into the runtime:
1. Set `WORKSPACE_BASE`:
```bash
export WORKSPACE_BASE=/path/to/your/code
# Linux and Mac Example
# export WORKSPACE_BASE=$HOME/OpenHands
# Will set $WORKSPACE_BASE to /home/<username>/OpenHands
#
# WSL on Windows Example
# export WORKSPACE_BASE=/mnt/c/dev/OpenHands
# Will set $WORKSPACE_BASE to C:\dev\OpenHands
```
2. Add the following options to the `docker run` command:
```bash
docker run # ...
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-v $WORKSPACE_BASE:/opt/workspace_base \
# ...
```
Be careful! There's nothing stopping the OpenHands agent from deleting or modifying
any files that are mounted into its workspace.
This setup can cause some issues with file permissions (hence the `SANDBOX_USER_ID` variable)
but seems to work well on most systems.
## OpenHands Remote Runtime
OpenHands Remote Runtime is currently in beta (read [here](https://runtime.all-hands.dev/) for more details), it allows you to launch runtimes in parallel in the cloud.
Fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSckVz_JFwg2_mOxNZjCtr7aoBFI2Mwdan3f75J_TrdMS1JV2g/viewform) to apply if you want to try this out!
NOTE: This runtime is specifically designed for agent evaluation purposes only through [OpenHands evaluation harness](https://github.com/All-Hands-AI/OpenHands/tree/main/evaluation). It should not be used to launch production OpenHands applications.
## Modal Runtime
Our partners at [Modal](https://modal.com/) have also provided a runtime for OpenHands.
To use the Modal Runtime, create an account, and then [create an API key.](https://modal.com/settings)
You'll then need to set the following environment variables when starting OpenHands:
```bash
docker run # ...
-e RUNTIME=modal \
-e MODAL_API_TOKEN_ID="your-id" \
-e MODAL_API_TOKEN_SECRET="your-secret" \
```
## Daytona Runtime
Another option is using [Daytona](https://www.daytona.io/) as a runtime provider:
### Step 1: Retrieve Your Daytona API Key
1. Visit the [Daytona Dashboard](https://app.daytona.io/dashboard/keys).
2. Click **"Create Key"**.
3. Enter a name for your key and confirm the creation.
4. Once the key is generated, copy it.
### Step 2: Set Your API Key as an Environment Variable
Run the following command in your terminal, replacing `<your-api-key>` with the actual key you copied:
```bash
export DAYTONA_API_KEY="<your-api-key>"
```
This step ensures that OpenHands can authenticate with the Daytona platform when it runs.
### Step 3: Run OpenHands Locally Using Docker
To start the latest version of OpenHands on your machine, execute the following command in your terminal:
```bash
bash -i <(curl -sL https://get.daytona.io/openhands)
```
#### What This Command Does:
- Downloads the latest OpenHands release script.
- Runs the script in an interactive Bash session.
- Automatically pulls and runs the OpenHands container using Docker.
Once executed, OpenHands should be running locally and ready for use.
For more details and manual initialization, view the entire [README.md](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/impl/daytona/README.md)
## Local Runtime
The Local Runtime allows the OpenHands agent to execute actions directly on your local machine without using Docker. This runtime is primarily intended for controlled environments like CI pipelines or testing scenarios where Docker is not available.
:::caution
**Security Warning**: The Local Runtime runs without any sandbox isolation. The agent can directly access and modify files on your machine. Only use this runtime in controlled environments or when you fully understand the security implications.
:::
### Prerequisites
Before using the Local Runtime, ensure you have the following dependencies installed:
1. You have followed the [Development setup instructions](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md).
2. tmux is available on your system.
### Configuration
To use the Local Runtime, besides required configurations like the model, API key, you'll need to set the following options via environment variables or the [config.toml file](https://github.com/All-Hands-AI/OpenHands/blob/main/config.template.toml) when starting OpenHands:
- Via environment variables:
```bash
# Required
export RUNTIME=local
# Optional but recommended
export WORKSPACE_BASE=/path/to/your/workspace
```
- Via `config.toml`:
```toml
[core]
runtime = "local"
workspace_base = "/path/to/your/workspace"
```
If `WORKSPACE_BASE` is not set, the runtime will create a temporary directory for the agent to work in.
### Example Usage
Here's an example of how to start OpenHands with the Local Runtime in Headless Mode:
```bash
# Set the runtime type to local
export RUNTIME=local
# Optionally set a workspace directory
export WORKSPACE_BASE=/path/to/your/project
# Start OpenHands
poetry run python -m openhands.core.main -t "write a bash script that prints hi"
```
### Use Cases
The Local Runtime is particularly useful for:
- CI/CD pipelines where Docker is not available.
- Testing and development of OpenHands itself.
- Environments where container usage is restricted.
- Scenarios where direct file system access is required.
<Redirect to="/modules/usage/runtimes-index" />
+32
View File
@@ -0,0 +1,32 @@
# Daytona Runtime
You can use [Daytona](https://www.daytona.io/) as a runtime provider:
## Step 1: Retrieve Your Daytona API Key
1. Visit the [Daytona Dashboard](https://app.daytona.io/dashboard/keys).
2. Click **"Create Key"**.
3. Enter a name for your key and confirm the creation.
4. Once the key is generated, copy it.
## Step 2: Set Your API Key as an Environment Variable
Run the following command in your terminal, replacing `<your-api-key>` with the actual key you copied:
```bash
export DAYTONA_API_KEY="<your-api-key>"
```
This step ensures that OpenHands can authenticate with the Daytona platform when it runs.
## Step 3: Run OpenHands Locally Using Docker
To start the latest version of OpenHands on your machine, execute the following command in your terminal:
```bash
bash -i <(curl -sL https://get.daytona.io/openhands)
```
### What This Command Does:
- Downloads the latest OpenHands release script.
- Runs the script in an interactive Bash session.
- Automatically pulls and runs the OpenHands container using Docker.
Once executed, OpenHands should be running locally and ready for use.
For more details and manual initialization, view the entire [README.md](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/impl/daytona/README.md)
+88
View File
@@ -0,0 +1,88 @@
# Docker Runtime
This is the default Runtime that's used when you start OpenHands.
## Image
The `SANDBOX_RUNTIME_CONTAINER_IMAGE` from nikolaik is a pre-built runtime image
that contains our Runtime server, as well as some basic utilities for Python and NodeJS.
You can also [build your own runtime image](../how-to/custom-sandbox-guide).
## Connecting to Your filesystem
One useful feature here is the ability to connect to your local filesystem. To mount your filesystem into the runtime:
1. Set `WORKSPACE_BASE`:
```bash
export WORKSPACE_BASE=/path/to/your/code
# Linux and Mac Example
# export WORKSPACE_BASE=$HOME/OpenHands
# Will set $WORKSPACE_BASE to /home/<username>/OpenHands
#
# WSL on Windows Example
# export WORKSPACE_BASE=/mnt/c/dev/OpenHands
# Will set $WORKSPACE_BASE to C:\dev\OpenHands
```
2. Add the following options to the `docker run` command:
```bash
docker run # ...
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-v $WORKSPACE_BASE:/opt/workspace_base \
# ...
```
Be careful! There's nothing stopping the OpenHands agent from deleting or modifying
any files that are mounted into its workspace.
This setup can cause some issues with file permissions (hence the `SANDBOX_USER_ID` variable)
but seems to work well on most systems.
## Hardened Docker Installation
When deploying OpenHands in environments where security is a priority, you should consider implementing a hardened Docker configuration. This section provides recommendations for securing your OpenHands Docker deployment beyond the default configuration.
### Security Considerations
The default Docker configuration in the README is designed for ease of use on a local development machine. If you're running on a public network (e.g. airport WiFi),
you should implement additional security measures.
### Network Binding Security
By default, OpenHands binds to all network interfaces (`0.0.0.0`), which can expose your instance to all networks the host is connected to. For a more secure setup:
1. **Restrict Network Binding**:
Use the `runtime_binding_address` configuration to restrict which network interfaces OpenHands listens on:
```bash
docker run # ...
-e SANDBOX_RUNTIME_BINDING_ADDRESS=127.0.0.1 \
# ...
```
This configuration ensures OpenHands only listens on the loopback interface (`127.0.0.1`), making it accessible only from the local machine.
2. **Secure Port Binding**:
Modify the `-p` flag to bind only to localhost instead of all interfaces:
```bash
docker run # ... \
-p 127.0.0.1:3000:3000 \
```
This ensures that the OpenHands web interface is only accessible from the local machine, not from other machines on the network.
### Network Isolation
Use Docker's network features to isolate OpenHands:
```bash
# Create an isolated network
docker network create openhands-network
# Run OpenHands in the isolated network
docker run # ... \
--network openhands-network \
```
+62
View File
@@ -0,0 +1,62 @@
# Local Runtime
The Local Runtime allows the OpenHands agent to execute actions directly on your local machine without using Docker. This runtime is primarily intended for controlled environments like CI pipelines or testing scenarios where Docker is not available.
:::caution
**Security Warning**: The Local Runtime runs without any sandbox isolation. The agent can directly access and modify files on your machine. Only use this runtime in controlled environments or when you fully understand the security implications.
:::
## Prerequisites
Before using the Local Runtime, ensure that:
1. You have followed the [Development setup instructions](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md).
2. tmux is available on your system.
## Configuration
To use the Local Runtime, besides required configurations like the model, API key, you'll need to set the following options via environment variables or the [config.toml file](https://github.com/All-Hands-AI/OpenHands/blob/main/config.template.toml) when starting OpenHands:
- Via environment variables:
```bash
# Required
export RUNTIME=local
# Optional but recommended
export WORKSPACE_BASE=/path/to/your/workspace
```
- Via `config.toml`:
```toml
[core]
runtime = "local"
workspace_base = "/path/to/your/workspace"
```
If `WORKSPACE_BASE` is not set, the runtime will create a temporary directory for the agent to work in.
## Example Usage
Here's an example of how to start OpenHands with the Local Runtime in Headless Mode:
```bash
# Set the runtime type to local
export RUNTIME=local
# Optionally set a workspace directory
export WORKSPACE_BASE=/path/to/your/project
# Start OpenHands
poetry run python -m openhands.core.main -t "write a bash script that prints hi"
```
## Use Cases
The Local Runtime is particularly useful for:
- CI/CD pipelines where Docker is not available.
- Testing and development of OpenHands itself.
- Environments where container usage is restricted.
- Scenarios where direct file system access is required.
+13
View File
@@ -0,0 +1,13 @@
# Modal Runtime
Our partners at [Modal](https://modal.com/) have provided a runtime for OpenHands.
To use the Modal Runtime, create an account, and then [create an API key.](https://modal.com/settings)
You'll then need to set the following environment variables when starting OpenHands:
```bash
docker run # ...
-e RUNTIME=modal \
-e MODAL_API_TOKEN_ID="your-id" \
-e MODAL_API_TOKEN_SECRET="your-secret" \
```
+6
View File
@@ -0,0 +1,6 @@
# OpenHands Remote Runtime
OpenHands Remote Runtime is currently in beta (read [here](https://runtime.all-hands.dev/) for more details), it allows you to launch runtimes in parallel in the cloud.
Fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSckVz_JFwg2_mOxNZjCtr7aoBFI2Mwdan3f75J_TrdMS1JV2g/viewform) to apply if you want to try this out!
NOTE: This runtime is specifically designed for agent evaluation purposes only through [OpenHands evaluation harness](https://github.com/All-Hands-AI/OpenHands/tree/main/evaluation). It should not be used to launch production OpenHands applications.
+33 -2
View File
@@ -156,9 +156,40 @@ const sidebars: SidebarsConfig = {
],
},
{
type: 'doc',
type: 'category',
label: 'Runtime Configuration',
id: 'usage/runtimes',
items: [
{
type: 'doc',
label: 'Overview',
id: 'usage/runtimes-index',
},
{
type: 'doc',
label: 'Docker Runtime',
id: 'usage/runtimes/docker',
},
{
type: 'doc',
label: 'Remote Runtime',
id: 'usage/runtimes/remote',
},
{
type: 'doc',
label: 'Modal Runtime',
id: 'usage/runtimes/modal',
},
{
type: 'doc',
label: 'Daytona Runtime',
id: 'usage/runtimes/daytona',
},
{
type: 'doc',
label: 'Local Runtime',
id: 'usage/runtimes/local',
},
],
},
{
type: 'doc',
@@ -24,12 +24,8 @@ import { useGetTrajectory } from "#/hooks/mutation/use-get-trajectory";
import { downloadTrajectory } from "#/utils/download-trajectory";
import { displayErrorToast } from "#/utils/custom-toast-handlers";
function getEntryPoint(
hasRepository: boolean | null,
hasImportedProjectZip: boolean | null,
): string {
function getEntryPoint(hasRepository: boolean | null): string {
if (hasRepository) return "github";
if (hasImportedProjectZip) return "zip";
return "direct";
}
@@ -48,7 +44,7 @@ export function ChatInterface() {
>("positive");
const [feedbackModalIsOpen, setFeedbackModalIsOpen] = React.useState(false);
const [messageToSend, setMessageToSend] = React.useState<string | null>(null);
const { selectedRepository, importedProjectZip } = useSelector(
const { selectedRepository } = useSelector(
(state: RootState) => state.initialQuery,
);
const params = useParams();
@@ -57,12 +53,8 @@ export function ChatInterface() {
const handleSendMessage = async (content: string, files: File[]) => {
if (messages.length === 0) {
posthog.capture("initial_query_submitted", {
entry_point: getEntryPoint(
selectedRepository !== null,
importedProjectZip !== null,
),
entry_point: getEntryPoint(selectedRepository !== null),
query_character_length: content.length,
uploaded_zip_size: importedProjectZip?.length,
});
} else {
posthog.capture("user_message_sent", {
@@ -0,0 +1,30 @@
import React from "react";
import { useDispatch } from "react-redux";
import { useCreateConversation } from "#/hooks/mutation/use-create-conversation";
import { setInitialPrompt } from "#/state/initial-query-slice";
const INITIAL_PROMPT = "";
export function CodeNotInGitHubLink() {
const dispatch = useDispatch();
const { mutate: createConversation } = useCreateConversation();
const handleStartFromScratch = () => {
// Set the initial prompt and create a new conversation
dispatch(setInitialPrompt(INITIAL_PROMPT));
createConversation({ q: INITIAL_PROMPT });
};
return (
<div className="text-xs text-neutral-400">
Code not in GitHub?{" "}
<span
onClick={handleStartFromScratch}
className="underline cursor-pointer"
>
Start from scratch
</span>{" "}
and use the VS Code link to upload and download your code.
</div>
);
}
@@ -63,8 +63,8 @@ export function PaymentForm() {
name="top-up-input"
onChange={handleTopUpInputChange}
type="text"
label="Top-up amount"
placeholder="Specify an amount to top up your credits"
label="Add funds"
placeholder="Specify an amount (USD) to add to your account"
className="w-[680px]"
/>
@@ -81,6 +81,7 @@ export function Sidebar() {
if (config?.APP_MODE === "saas") await logout();
else saveUserSettings({ unset_github_token: true });
posthog.reset();
window.location.href = "/logout";
};
return (
@@ -105,10 +106,10 @@ export function Sidebar() {
)}
/>
</TooltipButton>
<DocsButton />
</div>
<div className="flex flex-row md:flex-col md:items-center gap-[26px] md:mb-4">
<DocsButton />
<NavLink
to="/settings"
className={({ isActive }) =>
@@ -1,33 +0,0 @@
import { useTranslation } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import { SuggestionBox } from "./suggestion-box";
interface ImportProjectSuggestionBoxProps {
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
}
export function ImportProjectSuggestionBox({
onChange,
}: ImportProjectSuggestionBoxProps) {
const { t } = useTranslation();
return (
<SuggestionBox
title={t(I18nKey.LANDING$IMPORT_PROJECT)}
content={
<label htmlFor="import-project" className="w-full flex justify-center">
<span className="border-2 border-dashed border-neutral-600 rounded px-2 py-1 cursor-pointer">
{t(I18nKey.LANDING$UPLOAD_ZIP)}
</span>
<input
hidden
type="file"
accept="application/zip"
id="import-project"
multiple={false}
onChange={onChange}
/>
</label>
}
/>
);
}
+1 -1
View File
@@ -23,7 +23,7 @@ export function NavTab({ to, label, icon, isBeta }: NavTabProps) {
>
{({ isActive }) => (
<>
<div className={cn(isActive && "text-primary")}>{icon}</div>
<div className={cn(isActive && "text-logo")}>{icon}</div>
{label}
{isBeta && <BetaBadge />}
</>
+1 -1
View File
@@ -56,7 +56,7 @@ export function TaskForm({ ref }: TaskFormProps) {
};
return (
<div className="flex flex-col gap-2 w-full">
<div className="flex flex-col gap-1 w-full">
<form
ref={ref}
onSubmit={handleSubmit}
+9 -1
View File
@@ -3,6 +3,7 @@ import React from "react";
interface AuthContextType {
githubTokenIsSet: boolean;
setGitHubTokenIsSet: (value: boolean) => void;
logout: () => void;
}
interface AuthContextProps extends React.PropsWithChildren {
@@ -16,12 +17,19 @@ function AuthProvider({ children, initialGithubTokenIsSet }: AuthContextProps) {
!!initialGithubTokenIsSet,
);
const logout = React.useCallback(() => {
setGitHubTokenIsSet(false);
// Clear any auth-related data from localStorage
localStorage.removeItem("gh_token");
}, [setGitHubTokenIsSet]);
const value = React.useMemo(
() => ({
githubTokenIsSet,
setGitHubTokenIsSet,
logout,
}),
[githubTokenIsSet, setGitHubTokenIsSet],
[githubTokenIsSet, setGitHubTokenIsSet, logout],
);
return <AuthContext value={value}>{children}</AuthContext>;
@@ -11,21 +11,12 @@ export const useCreateConversation = () => {
const dispatch = useDispatch();
const queryClient = useQueryClient();
const { selectedRepository, files, importedProjectZip } = useSelector(
const { selectedRepository, files } = useSelector(
(state: RootState) => state.initialQuery,
);
return useMutation({
mutationFn: async (variables: { q?: string }) => {
if (
!variables.q?.trim() &&
!selectedRepository &&
files.length === 0 &&
!importedProjectZip
) {
throw new Error("No query provided");
}
if (variables.q) dispatch(setInitialPrompt(variables.q));
return OpenHands.createConversation(
+30
View File
@@ -1,4 +1,34 @@
{
"AUTH$LOGGED_OUT": {
"en": "You have been logged out",
"ja": "ログアウトしました",
"zh-CN": "您已退出登录",
"zh-TW": "您已登出",
"ko-KR": "로그아웃되었습니다",
"no": "Du har blitt logget ut",
"it": "Sei stato disconnesso",
"pt": "Você foi desconectado",
"es": "Has cerrado sesión",
"ar": "لقد تم تسجيل خروجك",
"fr": "Vous avez été déconnecté",
"tr": "Oturumunuz kapatıldı",
"de": "Sie wurden abgemeldet"
},
"AUTH$LOG_IN_WITH_GITHUB": {
"en": "Log in with GitHub",
"ja": "GitHubでログイン",
"zh-CN": "使用GitHub登录",
"zh-TW": "使用GitHub登入",
"ko-KR": "GitHub로 로그인",
"no": "Logg inn med GitHub",
"it": "Accedi con GitHub",
"pt": "Entrar com GitHub",
"es": "Iniciar sesión con GitHub",
"ar": "تسجيل الدخول باستخدام GitHub",
"fr": "Se connecter avec GitHub",
"tr": "GitHub ile giriş yap",
"de": "Mit GitHub anmelden"
},
"APP$TITLE": {
"en": "App",
"ja": "アプリ",
+3
View File
@@ -6,6 +6,9 @@ import {
} from "@react-router/dev/routes";
export default [
layout("routes/_no-sidebar/route.tsx", [
route("logout", "routes/logout.tsx"),
]),
layout("routes/_oh/route.tsx", [
index("routes/_oh._index/route.tsx"),
route("settings", "routes/settings.tsx", [
+10
View File
@@ -0,0 +1,10 @@
import React from "react";
import { Outlet } from "react-router";
export default function NoSidebarLayout() {
return (
<div className="h-screen w-screen">
<Outlet />
</div>
);
}
+6 -20
View File
@@ -1,18 +1,13 @@
import React from "react";
import { useDispatch } from "react-redux";
import posthog from "posthog-js";
import { setImportedProjectZip } from "#/state/initial-query-slice";
import { convertZipToBase64 } from "#/utils/convert-zip-to-base64";
import { useGitHubUser } from "#/hooks/query/use-github-user";
import { useGitHubAuthUrl } from "#/hooks/use-github-auth-url";
import { useConfig } from "#/hooks/query/use-config";
import { ImportProjectSuggestionBox } from "../../components/features/suggestions/import-project-suggestion-box";
import { GitHubRepositoriesSuggestionBox } from "#/components/features/github/github-repositories-suggestion-box";
import { CodeNotInGitHubLink } from "#/components/features/github/code-not-in-github-link";
import { HeroHeading } from "#/components/shared/hero-heading";
import { TaskForm } from "#/components/shared/task-form";
function Home() {
const dispatch = useDispatch();
const formRef = React.useRef<HTMLFormElement>(null);
const { data: config } = useConfig();
@@ -29,29 +24,20 @@ function Home() {
className="bg-base-secondary h-full rounded-xl flex flex-col items-center justify-center relative overflow-y-auto px-2"
>
<HeroHeading />
<div className="flex flex-col gap-8 w-full md:w-[600px] items-center">
<div className="flex flex-col gap-1 w-full mt-8 md:w-[600px] items-center">
<div className="flex flex-col gap-2 w-full">
<TaskForm ref={formRef} />
</div>
<div className="flex gap-4 w-full flex-col md:flex-row">
<div className="flex gap-4 w-full flex-col md:flex-row mt-8">
<GitHubRepositoriesSuggestionBox
handleSubmit={() => formRef.current?.requestSubmit()}
gitHubAuthUrl={gitHubAuthUrl}
user={user || null}
/>
<ImportProjectSuggestionBox
onChange={async (event) => {
if (event.target.files) {
const zip = event.target.files[0];
dispatch(setImportedProjectZip(await convertZipToBase64(zip)));
posthog.capture("zip_file_uploaded");
formRef.current?.requestSubmit();
} else {
// TODO: handle error
}
}}
/>
</div>
<div className="w-full flex justify-start mt-2 ml-2">
<CodeNotInGitHubLink />
</div>
</div>
</div>
@@ -1,44 +1,12 @@
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { setImportedProjectZip } from "#/state/initial-query-slice";
import { useSelector } from "react-redux";
import { RootState } from "#/store";
import { base64ToBlob } from "#/utils/base64-to-blob";
import { useUploadFiles } from "../../../hooks/mutation/use-upload-files";
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";
import { displayErrorToast } from "#/utils/custom-toast-handlers";
export const useHandleRuntimeActive = () => {
const dispatch = useDispatch();
const { mutate: uploadFiles } = useUploadFiles();
const { curAgentState } = useSelector((state: RootState) => state.agent);
const runtimeActive = !RUNTIME_INACTIVE_STATES.includes(curAgentState);
const { importedProjectZip } = useSelector(
(state: RootState) => state.initialQuery,
);
const handleUploadFiles = (zip: string) => {
const blob = base64ToBlob(zip);
const file = new File([blob], "imported-project.zip", {
type: blob.type,
});
uploadFiles(
{ files: [file] },
{
onError: () => {
displayErrorToast("Failed to upload project files.");
},
},
);
dispatch(setImportedProjectZip(null));
};
React.useEffect(() => {
if (runtimeActive && importedProjectZip) {
handleUploadFiles(importedProjectZip);
}
}, [runtimeActive, importedProjectZip]);
return { runtimeActive };
};
+40
View File
@@ -0,0 +1,40 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useGitHubAuthUrl } from "#/hooks/use-github-auth-url";
import { useConfig } from "#/hooks/query/use-config";
import { AllHandsLogoButton } from "#/components/shared/buttons/all-hands-logo-button";
export default function LogoutPage() {
const { t } = useTranslation();
const config = useConfig();
const gitHubAuthUrl = useGitHubAuthUrl({
appMode: config.data?.APP_MODE || null,
gitHubClientId: config.data?.GITHUB_CLIENT_ID || null,
});
return (
<div className="h-screen w-screen flex items-center justify-center bg-base">
<div className="flex flex-col items-center gap-8 p-8 rounded-lg bg-neutral-800">
<AllHandsLogoButton
onClick={() => {
window.location.href = "/";
}}
/>
<h1 className="text-2xl font-bold text-neutral-200">
{t("AUTH$LOGGED_OUT")}
</h1>
<div className="flex flex-col gap-4">
{gitHubAuthUrl && (
<a
href={gitHubAuthUrl}
className="px-4 py-2 bg-primary text-white rounded hover:bg-primary/90 text-center"
>
{t("AUTH$LOG_IN_WITH_GITHUB")}
</a>
)}
</div>
</div>
</div>
);
}
@@ -4,14 +4,12 @@ type SliceState = {
files: string[]; // base64 encoded images
initialPrompt: string | null;
selectedRepository: string | null;
importedProjectZip: string | null; // base64 encoded zip
};
const initialState: SliceState = {
files: [],
initialPrompt: null,
selectedRepository: null,
importedProjectZip: null,
};
export const selectedFilesSlice = createSlice({
@@ -39,9 +37,6 @@ export const selectedFilesSlice = createSlice({
clearSelectedRepository(state) {
state.selectedRepository = null;
},
setImportedProjectZip(state, action: PayloadAction<string | null>) {
state.importedProjectZip = action.payload;
},
},
});
@@ -53,6 +48,5 @@ export const {
clearInitialPrompt,
setSelectedRepository,
clearSelectedRepository,
setImportedProjectZip,
} = selectedFilesSlice.actions;
export default selectedFilesSlice.reducer;
@@ -1,10 +0,0 @@
export const convertZipToBase64 = async (file: File) => {
const reader = new FileReader();
return new Promise<string>((resolve) => {
reader.onload = () => {
resolve(reader.result as string);
};
reader.readAsDataURL(file);
});
};
+2
View File
@@ -10,6 +10,7 @@ export default {
extend: {
colors: {
primary: "#C9B974", // nice yellow
logo: "#CFB755", // color for logos and icons
base: "#0D0F11", // dark background also used for tooltips
"base-secondary": "#24272E", // lighter background
danger: "#E76A5E",
@@ -35,6 +36,7 @@ export default {
dark: {
colors: {
primary: "#4465DB",
logo: "#CFB755",
},
},
},