forked from All-Hands-AI/OpenHands
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add port mappings support (All-Hands-AI#5577)
Co-authored-by: openhands <[email protected]> Co-authored-by: tofarr <[email protected]> Co-authored-by: Robert Brennan <[email protected]> Co-authored-by: Robert Brennan <[email protected]>
- Loading branch information
1 parent
da0006d
commit 7aeb6aa
Showing
15 changed files
with
333 additions
and
33 deletions.
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
frontend/src/components/features/served-host/path-form.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
interface PathFormProps { | ||
ref: React.RefObject<HTMLFormElement | null>; | ||
onBlur: () => void; | ||
defaultValue: string; | ||
} | ||
|
||
export function PathForm({ ref, onBlur, defaultValue }: PathFormProps) { | ||
return ( | ||
<form ref={ref} onSubmit={(e) => e.preventDefault()} className="flex-1"> | ||
<input | ||
name="url" | ||
type="text" | ||
defaultValue={defaultValue} | ||
className="w-full bg-transparent" | ||
onBlur={onBlur} | ||
/> | ||
</form> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { useActiveHost } from "#/hooks/query/use-active-host"; | ||
|
||
export function ServedAppLabel() { | ||
const { activeHost } = useActiveHost(); | ||
|
||
return ( | ||
<div className="flex items-center justify-between w-full"> | ||
<div className="flex items-center gap-2">App</div> | ||
{activeHost && <div className="w-2 h-2 bg-green-500 rounded-full" />} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { useQueries, useQuery } from "@tanstack/react-query"; | ||
import axios from "axios"; | ||
import React from "react"; | ||
import { useSelector } from "react-redux"; | ||
import { openHands } from "#/api/open-hands-axios"; | ||
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state"; | ||
import { RootState } from "#/store"; | ||
import { useConversation } from "#/context/conversation-context"; | ||
|
||
export const useActiveHost = () => { | ||
const { curAgentState } = useSelector((state: RootState) => state.agent); | ||
const [activeHost, setActiveHost] = React.useState<string | null>(null); | ||
|
||
const { conversationId } = useConversation(); | ||
|
||
const { data } = useQuery({ | ||
queryKey: [conversationId, "hosts"], | ||
queryFn: async () => { | ||
const response = await openHands.get<{ hosts: string[] }>( | ||
`/api/conversations/${conversationId}/web-hosts`, | ||
); | ||
return { hosts: Object.keys(response.data.hosts) }; | ||
}, | ||
enabled: !RUNTIME_INACTIVE_STATES.includes(curAgentState), | ||
initialData: { hosts: [] }, | ||
}); | ||
|
||
const apps = useQueries({ | ||
queries: data.hosts.map((host) => ({ | ||
queryKey: [conversationId, "hosts", host], | ||
queryFn: async () => { | ||
try { | ||
await axios.get(host); | ||
return host; | ||
} catch (e) { | ||
return ""; | ||
} | ||
}, | ||
refetchInterval: 3000, | ||
})), | ||
}); | ||
|
||
const appsData = apps.map((app) => app.data); | ||
|
||
React.useEffect(() => { | ||
const successfulApp = appsData.find((app) => app); | ||
setActiveHost(successfulApp || ""); | ||
}, [appsData]); | ||
|
||
return { activeHost }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import React from "react"; | ||
import { FaArrowRotateRight } from "react-icons/fa6"; | ||
import { FaExternalLinkAlt, FaHome } from "react-icons/fa"; | ||
import { useActiveHost } from "#/hooks/query/use-active-host"; | ||
import { PathForm } from "#/components/features/served-host/path-form"; | ||
|
||
function ServedApp() { | ||
const { activeHost } = useActiveHost(); | ||
const [refreshKey, setRefreshKey] = React.useState(0); | ||
const [currentActiveHost, setCurrentActiveHost] = React.useState< | ||
string | null | ||
>(null); | ||
const [path, setPath] = React.useState<string>("hello"); | ||
|
||
const formRef = React.useRef<HTMLFormElement>(null); | ||
|
||
const handleOnBlur = () => { | ||
if (formRef.current) { | ||
const formData = new FormData(formRef.current); | ||
const urlInputValue = formData.get("url")?.toString(); | ||
|
||
if (urlInputValue) { | ||
const url = new URL(urlInputValue); | ||
|
||
setCurrentActiveHost(url.origin); | ||
setPath(url.pathname); | ||
} | ||
} | ||
}; | ||
|
||
const resetUrl = () => { | ||
setCurrentActiveHost(activeHost); | ||
setPath(""); | ||
|
||
if (formRef.current) { | ||
formRef.current.reset(); | ||
} | ||
}; | ||
|
||
React.useEffect(() => { | ||
resetUrl(); | ||
}, [activeHost]); | ||
|
||
const fullUrl = `${currentActiveHost}/${path}`; | ||
|
||
if (!currentActiveHost) { | ||
return ( | ||
<div className="flex items-center justify-center w-full h-full p-10"> | ||
<span className="text-neutral-400 font-bold"> | ||
If you tell OpenHands to start a web server, the app will appear here. | ||
</span> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<div className="h-full w-full"> | ||
<div className="w-full p-2 flex items-center gap-4 border-b border-neutral-600"> | ||
<button | ||
type="button" | ||
onClick={() => window.open(fullUrl, "_blank")} | ||
className="text-sm" | ||
> | ||
<FaExternalLinkAlt className="w-4 h-4" /> | ||
</button> | ||
<button | ||
type="button" | ||
onClick={() => setRefreshKey((prev) => prev + 1)} | ||
className="text-sm" | ||
> | ||
<FaArrowRotateRight className="w-4 h-4" /> | ||
</button> | ||
|
||
<button type="button" onClick={() => resetUrl()} className="text-sm"> | ||
<FaHome className="w-4 h-4" /> | ||
</button> | ||
<div className="w-full flex"> | ||
<PathForm | ||
ref={formRef} | ||
onBlur={handleOnBlur} | ||
defaultValue={fullUrl} | ||
/> | ||
</div> | ||
</div> | ||
<iframe | ||
key={refreshKey} | ||
title="Served App" | ||
src={fullUrl} | ||
className="w-full h-full" | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
export default ServedApp; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,21 @@ You are OpenHands agent, a helpful AI assistant that can interact with a compute | |
* If user provides a path, you should NOT assume it's relative to the current working directory. Instead, you should explore the file system to find the file before working on it. | ||
* When configuring git credentials, use "openhands" as the user.name and "[email protected]" as the user.email by default, unless explicitly instructed otherwise. | ||
* The assistant MUST NOT include comments in the code unless they are necessary to describe non-obvious behavior. | ||
{{ runtime_info }} | ||
</IMPORTANT> | ||
{% if repo_instructions %} | ||
{% if repo_instructions -%} | ||
<REPOSITORY_INSTRUCTIONS> | ||
{{ repo_instructions }} | ||
</REPOSITORY_INSTRUCTIONS> | ||
{% endif %} | ||
{% if runtime_info and runtime_info.available_hosts -%} | ||
<RUNTIME_INFORMATION> | ||
The user has access to the following hosts for accessing a web application, | ||
each of which has a corresponding port: | ||
{% for host, port in runtime_info.available_hosts.items() -%} | ||
* {{ host }} (port {{ port }}) | ||
{% endfor %} | ||
When starting a web server, use the corresponding ports. You should also | ||
set any options to allow iframes and CORS requests. | ||
</RUNTIME_INFORMATION> | ||
{% endif %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.