- Notifications
You must be signed in to change notification settings - Fork1.1k
feat: support devcontainer agents in ui and unify backend#18332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from1 commit
765c2cf4019358452dbc96a23998ee3ed36fc1a90a88ce78fb1431a32c2bf2818e1593df28ff99f69f69ecfe48371c61b62e1c31f7e41c1579e18448ce0aecd3bda05d4f208b7e5ede07a3674ac0607b1e1ea4bff150bad2b22ee24b9d218e021c8d0c44de8File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
- Loading branch information
Uh oh!
There was an error while loading.Please reload this page.
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import type { | ||
| Workspace, | ||
| WorkspaceAgent, | ||
| WorkspaceAgentDevcontainer, | ||
| } from "api/typesGenerated"; | ||
| import { Button } from "components/Button/Button"; | ||
| import { displayError } from "components/GlobalSnackbar/utils"; | ||
| @@ -24,33 +24,34 @@ import type { FC } from "react"; | ||
| import { useEffect, useState } from "react"; | ||
| import { portForwardURL } from "utils/portForward"; | ||
| import { AgentButton } from "./AgentButton"; | ||
| import { | ||
| AgentDevcontainerSSHButton, | ||
| AgentSSHButton, | ||
| } from "./SSHButton/SSHButton"; | ||
| import { TerminalLink } from "./TerminalLink/TerminalLink"; | ||
| import { VSCodeDevContainerButton } from "./VSCodeDevContainerButton/VSCodeDevContainerButton"; | ||
| type AgentDevcontainerCardProps = { | ||
| agent: WorkspaceAgent; | ||
| devcontainer: WorkspaceAgentDevcontainer; | ||
| workspace: Workspace; | ||
| wildcardHostname: string; | ||
| }; | ||
| export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({ | ||
| agent, | ||
| devcontainer, | ||
| workspace, | ||
| wildcardHostname, | ||
| }) => { | ||
| const [isRecreating, setIsRecreating] = useState(false); | ||
| const handleRecreateDevcontainer = async () => { | ||
| setIsRecreating(true); | ||
| let recreateSucceeded = false; | ||
| try { | ||
| const response = await fetch( | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. In the FE, we handle requests in two ways: queries or mutations. In this case, you should wrap this request into a mutation.Here is the docs. Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. When using a mutation, you can invalidate other queries or update their data to reflect the most recent status, instead of doing it manually, so you wouldn't need the isRebuilding or subAgentRemoved statuses.Here is how you can do it. If you need more examples, you can search in the codebase for "invalidateQueries". MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Thanks for the tip! This was superb, actually solved a couple of issues I wanted to address but didn't know how! | ||
| `/api/v2/workspaceagents/${agent.id}/containers/devcontainers/container/${devcontainer.container?.id}/recreate`, | ||
| { | ||
| method: "POST", | ||
| }, | ||
| @@ -79,27 +80,35 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({ | ||
| } | ||
| }; | ||
| // If thedevcontainer is starting, reflect this in the recreate button. | ||
| useEffect(() => { | ||
| if (devcontainer.status === "starting") { | ||
| setIsRecreating(true); | ||
| } else { | ||
| setIsRecreating(false); | ||
| } | ||
| }, [devcontainer.status]); | ||
| return ( | ||
| <section | ||
| className="border border-border border-dashed rounded p-6 " | ||
| key={devcontainer.id} | ||
| > | ||
| <header className="flex justify-between items-center mb-4"> | ||
| <div className="flex items-center gap-2"> | ||
| <h3 className="m-0 text-xs font-medium text-content-secondary"> | ||
| dev container:{" "} | ||
| <span className="font-semibold"> | ||
| {devcontainer.name} | ||
| {devcontainer.container && ( | ||
| <span className="text-content-tertiary"> | ||
| {" "} | ||
| ({devcontainer.container.name}) | ||
| </span> | ||
| )} | ||
| </span> | ||
| </h3> | ||
| {devcontainer.dirty && ( | ||
| <HelpTooltip> | ||
| <HelpTooltipTrigger className="flex items-center text-xs text-content-warning ml-2"> | ||
| <span>Outdated</span> | ||
| @@ -126,10 +135,17 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({ | ||
| Recreate | ||
| </Button> | ||
| {/*<AgentDevcontainerSSHButton | ||
| workspace={workspace.name} | ||
| container={devcontainer.container?.name || devcontainer.name} | ||
| /> */} | ||
| {agent.display_apps.includes("ssh_helper") && ( // TODO agent | ||
| <AgentSSHButton | ||
| workspaceName={workspace.name} | ||
| agentName={devcontainer.name} | ||
| workspaceOwnerUsername={workspace.owner_name} | ||
| /> | ||
| )} | ||
| </div> | ||
| </header> | ||
| @@ -139,20 +155,19 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({ | ||
| <VSCodeDevContainerButton | ||
| userName={workspace.owner_name} | ||
| workspaceName={workspace.name} | ||
| devContainerName={devcontainer.name} | ||
| devContainerFolder={devcontainer.workspace_folder} | ||
| displayApps={agent.display_apps} // TODO agent | ||
| agentName={devcontainer.name} | ||
| /> | ||
| <TerminalLink | ||
| workspaceName={workspace.name} | ||
| agentName={devcontainer.name} | ||
| userName={workspace.owner_name} | ||
| /> | ||
| {wildcardHostname !== "" && | ||
| devcontainer.container?.ports.map((port) => { | ||
| const portLabel = `${port.port}/${port.network.toUpperCase()}`; | ||
| const hasHostBind = | ||
| port.host_port !== undefined && port.host_ip !== undefined; | ||